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 221
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) {
2767 #ifdef isnan
2768         return (bool)isnan(val);
2769 #else
2770         return !(val==val);
2771 #endif
2772       }
2773       static double min() { return -DBL_MAX; }
2774       static double max() { return DBL_MAX; }
2775       static double inf() {
2776 #ifdef INFINITY
2777         return (double)INFINITY;
2778 #else
2779         return max()*max();
2780 #endif
2781       }
2782       static double nan() {
2783 #ifdef NAN
2784         return (double)NAN;
2785 #else
2786         const double val_nan = -std::sqrt(-1.0); return val_nan;
2787 #endif
2788       }
2789       static double cut(const double val) { return val; }
2790       static const char* format() { return "%.17g"; }
2791       static const char* format_s() { return "%g"; }
2792       static double format(const double val) { return val; }
2793     };
2794 
2795     template<> struct type<float> {
2796       static const char* string() { static const char *const s = "float"; return s; }
2797       static bool is_float() { return true; }
2798       static bool is_inf(const float val) {
2799 #ifdef isinf
2800         return (bool)isinf(val);
2801 #else
2802         return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
2803 #endif
2804       }
2805       static bool is_nan(const float val) {
2806 #ifdef isnan
2807         return (bool)isnan(val);
2808 #else
2809         return !(val==val);
2810 #endif
2811       }
2812       static float min() { return -FLT_MAX; }
2813       static float max() { return FLT_MAX; }
2814       static float inf() { return (float)cimg::type<double>::inf(); }
2815       static float nan() { return (float)cimg::type<double>::nan(); }
2816       static float cut(const double val) { return (float)val; }
2817       static float cut(const float val) { return (float)val; }
2818       static const char* format() { return "%.9g"; }
2819       static const char* format_s() { return "%g"; }
2820       static double format(const float val) { return (double)val; }
2821     };
2822 
2823     template<> struct type<long double> {
2824       static const char* string() { static const char *const s = "long double"; return s; }
2825       static bool is_float() { return true; }
2826       static bool is_inf(const long double val) {
2827 #ifdef isinf
2828         return (bool)isinf(val);
2829 #else
2830         return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
2831 #endif
2832       }
2833       static bool is_nan(const long double val) {
2834 #ifdef isnan
2835         return (bool)isnan(val);
2836 #else
2837         return !(val==val);
2838 #endif
2839       }
2840       static long double min() { return -LDBL_MAX; }
2841       static long double max() { return LDBL_MAX; }
2842       static long double inf() { return max()*max(); }
2843       static long double nan() { const long double val_nan = -std::sqrt(-1.0L); return val_nan; }
2844       static long double cut(const long double val) { return val; }
2845       static const char* format() { return "%.17g"; }
2846       static const char* format_s() { return "%g"; }
2847       static double format(const long double val) { return (double)val; }
2848     };
2849 
2850 #ifdef cimg_use_half
2851     template<> struct type<half> {
2852       static const char* string() { static const char *const s = "half"; return s; }
2853       static bool is_float() { return true; }
2854       static bool is_inf(const long double val) {
2855 #ifdef isinf
2856         return (bool)isinf(val);
2857 #else
2858         return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
2859 #endif
2860       }
2861       static bool is_nan(const long double val) {
2862 #ifdef isnan
2863         return (bool)isnan(val);
2864 #else
2865         return !(val==val);
2866 #endif
2867       }
2868       static half min() { return (half)-65504; }
2869       static half max() { return (half)65504; }
2870       static half inf() { return max()*max(); }
2871       static half nan() { const half val_nan = (half)-std::sqrt(-1.0); return val_nan; }
2872       static half cut(const double val) { return (half)val; }
2873       static const char* format() { return "%.9g"; }
2874       static const char* format_s() { return "%g"; }
2875       static double format(const half val) { return (double)val; }
2876     };
2877 #endif
2878 
2879     template<typename T, typename t> struct superset { typedef T type; };
2880     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2881     template<> struct superset<bool,char> { typedef char type; };
2882     template<> struct superset<bool,signed char> { typedef signed char type; };
2883     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2884     template<> struct superset<bool,short> { typedef short type; };
2885     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2886     template<> struct superset<bool,int> { typedef int type; };
2887     template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
2888     template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
2889     template<> struct superset<bool,float> { typedef float type; };
2890     template<> struct superset<bool,double> { typedef double type; };
2891     template<> struct superset<unsigned char,char> { typedef short type; };
2892     template<> struct superset<unsigned char,signed char> { typedef short type; };
2893     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2894     template<> struct superset<unsigned char,short> { typedef short type; };
2895     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2896     template<> struct superset<unsigned char,int> { typedef int type; };
2897     template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
2898     template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
2899     template<> struct superset<unsigned char,float> { typedef float type; };
2900     template<> struct superset<unsigned char,double> { typedef double type; };
2901     template<> struct superset<signed char,unsigned char> { typedef short type; };
2902     template<> struct superset<signed char,char> { typedef short type; };
2903     template<> struct superset<signed char,unsigned short> { typedef int type; };
2904     template<> struct superset<signed char,short> { typedef short type; };
2905     template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
2906     template<> struct superset<signed char,int> { typedef int type; };
2907     template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
2908     template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
2909     template<> struct superset<signed char,float> { typedef float type; };
2910     template<> struct superset<signed char,double> { typedef double type; };
2911     template<> struct superset<char,unsigned char> { typedef short type; };
2912     template<> struct superset<char,signed char> { typedef short type; };
2913     template<> struct superset<char,unsigned short> { typedef int type; };
2914     template<> struct superset<char,short> { typedef short type; };
2915     template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
2916     template<> struct superset<char,int> { typedef int type; };
2917     template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
2918     template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
2919     template<> struct superset<char,float> { typedef float type; };
2920     template<> struct superset<char,double> { typedef double type; };
2921     template<> struct superset<unsigned short,char> { typedef int type; };
2922     template<> struct superset<unsigned short,signed char> { typedef int type; };
2923     template<> struct superset<unsigned short,short> { typedef int type; };
2924     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2925     template<> struct superset<unsigned short,int> { typedef int type; };
2926     template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
2927     template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
2928     template<> struct superset<unsigned short,float> { typedef float type; };
2929     template<> struct superset<unsigned short,double> { typedef double type; };
2930     template<> struct superset<short,unsigned short> { typedef int type; };
2931     template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
2932     template<> struct superset<short,int> { typedef int type; };
2933     template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
2934     template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
2935     template<> struct superset<short,float> { typedef float type; };
2936     template<> struct superset<short,double> { typedef double type; };
2937     template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
2938     template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
2939     template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
2940     template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
2941     template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
2942     template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
2943     template<> struct superset<unsigned int,float> { typedef float type; };
2944     template<> struct superset<unsigned int,double> { typedef double type; };
2945     template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
2946     template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
2947     template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
2948     template<> struct superset<int,float> { typedef float type; };
2949     template<> struct superset<int,double> { typedef double type; };
2950     template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
2951     template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
2952     template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
2953     template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
2954     template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
2955     template<> struct superset<cimg_uint64,float> { typedef double type; };
2956     template<> struct superset<cimg_uint64,double> { typedef double type; };
2957     template<> struct superset<cimg_int64,float> { typedef double type; };
2958     template<> struct superset<cimg_int64,double> { typedef double type; };
2959     template<> struct superset<float,double> { typedef double type; };
2960 #ifdef cimg_use_half
2961     template<> struct superset<half,unsigned short> { typedef float type; };
2962     template<> struct superset<half,short> { typedef float type; };
2963     template<> struct superset<half,unsigned int> { typedef float type; };
2964     template<> struct superset<half,int> { typedef float type; };
2965     template<> struct superset<half,cimg_uint64> { typedef float type; };
2966     template<> struct superset<half,cimg_int64> { typedef float type; };
2967     template<> struct superset<half,float> { typedef float type; };
2968     template<> struct superset<half,double> { typedef double type; };
2969 #endif
2970 
2971     template<typename t1, typename t2, typename t3> struct superset2 {
2972       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2973     };
2974 
2975     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2976       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2977     };
2978 
2979     template<typename t1, typename t2> struct last { typedef t2 type; };
2980 
2981 #define _cimg_Tt typename cimg::superset<T,t>::type
2982 #define _cimg_Tfloat typename cimg::superset<T,float>::type
2983 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
2984 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
2985 
2986     // Define variables used internally by CImg.
2987 #if cimg_display==1
2988     struct X11_info {
2989       unsigned int nb_wins;
2990       pthread_t *events_thread;
2991       pthread_cond_t wait_event;
2992       pthread_mutex_t wait_event_mutex;
2993       CImgDisplay **wins;
2994       Display *display;
2995       unsigned int nb_bits;
2996       bool is_blue_first;
2997       bool is_shm_enabled;
2998       bool byte_order;
2999 #ifdef cimg_use_xrandr
3000       XRRScreenSize *resolutions;
3001       Rotation curr_rotation;
3002       unsigned int curr_resolution;
3003       unsigned int nb_resolutions;
3004 #endif
3005       X11_info():nb_wins(0),events_thread(0),display(0),
3006                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
3007 #ifdef __FreeBSD__
3008         XInitThreads();
3009 #endif
3010         wins = new CImgDisplay*[1024];
3011         pthread_mutex_init(&wait_event_mutex,0);
3012         pthread_cond_init(&wait_event,0);
3013 #ifdef cimg_use_xrandr
3014         resolutions = 0;
3015         curr_rotation = 0;
3016         curr_resolution = nb_resolutions = 0;
3017 #endif
3018       }
3019 
3020       ~X11_info() {
3021         delete[] wins;
3022         /*
3023           if (events_thread) {
3024           pthread_cancel(*events_thread);
3025           delete events_thread;
3026           }
3027           if (display) { } // XCloseDisplay(display); }
3028           pthread_cond_destroy(&wait_event);
3029           pthread_mutex_unlock(&wait_event_mutex);
3030           pthread_mutex_destroy(&wait_event_mutex);
3031         */
3032       }
3033     };
3034 #if defined(cimg_module)
3035     X11_info& X11_attr();
3036 #elif defined(cimg_main)
3037     X11_info& X11_attr() { static X11_info val; return val; }
3038 #else
3039     inline X11_info& X11_attr() { static X11_info val; return val; }
3040 #endif
3041 #define cimg_lock_display() cimg::mutex(15)
3042 #define cimg_unlock_display() cimg::mutex(15,0)
3043 
3044 #elif cimg_display==2
3045     struct Win32_info {
3046       HANDLE wait_event;
3047       Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
3048     };
3049 #if defined(cimg_module)
3050     Win32_info& Win32_attr();
3051 #elif defined(cimg_main)
3052     Win32_info& Win32_attr() { static Win32_info val; return val; }
3053 #else
3054     inline Win32_info& Win32_attr() { static Win32_info val; return val; }
3055 #endif
3056 #endif
3057 
3058     struct Mutex_info {
3059 #if cimg_OS==2
3060       HANDLE mutex[32];
3061       Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); }
3062       void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
3063       void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
3064       int trylock(const unsigned int) { return 0; }
3065 #elif defined(_PTHREAD_H)
3066       pthread_mutex_t mutex[32];
3067       Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
3068       void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
3069       void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
3070       int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
3071 #else
3072       Mutex_info() {}
3073       void lock(const unsigned int) {}
3074       void unlock(const unsigned int) {}
3075       int trylock(const unsigned int) { return 0; }
3076 #endif
3077     };
3078 #if defined(cimg_module)
3079     Mutex_info& Mutex_attr();
3080 #elif defined(cimg_main)
3081     Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
3082 #else
3083     inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
3084 #endif
3085 
3086 #if defined(cimg_use_magick)
3087     static struct Magick_info {
3088       Magick_info() {
3089         Magick::InitializeMagick("");
3090       }
3091     } _Magick_info;
3092 #endif
3093 
3094 #if cimg_display==1
3095     // Define keycodes for X11-based graphical systems.
3096     const unsigned int keyESC        = XK_Escape;
3097     const unsigned int keyF1         = XK_F1;
3098     const unsigned int keyF2         = XK_F2;
3099     const unsigned int keyF3         = XK_F3;
3100     const unsigned int keyF4         = XK_F4;
3101     const unsigned int keyF5         = XK_F5;
3102     const unsigned int keyF6         = XK_F6;
3103     const unsigned int keyF7         = XK_F7;
3104     const unsigned int keyF8         = XK_F8;
3105     const unsigned int keyF9         = XK_F9;
3106     const unsigned int keyF10        = XK_F10;
3107     const unsigned int keyF11        = XK_F11;
3108     const unsigned int keyF12        = XK_F12;
3109     const unsigned int keyPAUSE      = XK_Pause;
3110     const unsigned int key1          = XK_1;
3111     const unsigned int key2          = XK_2;
3112     const unsigned int key3          = XK_3;
3113     const unsigned int key4          = XK_4;
3114     const unsigned int key5          = XK_5;
3115     const unsigned int key6          = XK_6;
3116     const unsigned int key7          = XK_7;
3117     const unsigned int key8          = XK_8;
3118     const unsigned int key9          = XK_9;
3119     const unsigned int key0          = XK_0;
3120     const unsigned int keyBACKSPACE  = XK_BackSpace;
3121     const unsigned int keyINSERT     = XK_Insert;
3122     const unsigned int keyHOME       = XK_Home;
3123     const unsigned int keyPAGEUP     = XK_Page_Up;
3124     const unsigned int keyTAB        = XK_Tab;
3125     const unsigned int keyQ          = XK_q;
3126     const unsigned int keyW          = XK_w;
3127     const unsigned int keyE          = XK_e;
3128     const unsigned int keyR          = XK_r;
3129     const unsigned int keyT          = XK_t;
3130     const unsigned int keyY          = XK_y;
3131     const unsigned int keyU          = XK_u;
3132     const unsigned int keyI          = XK_i;
3133     const unsigned int keyO          = XK_o;
3134     const unsigned int keyP          = XK_p;
3135     const unsigned int keyDELETE     = XK_Delete;
3136     const unsigned int keyEND        = XK_End;
3137     const unsigned int keyPAGEDOWN   = XK_Page_Down;
3138     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
3139     const unsigned int keyA          = XK_a;
3140     const unsigned int keyS          = XK_s;
3141     const unsigned int keyD          = XK_d;
3142     const unsigned int keyF          = XK_f;
3143     const unsigned int keyG          = XK_g;
3144     const unsigned int keyH          = XK_h;
3145     const unsigned int keyJ          = XK_j;
3146     const unsigned int keyK          = XK_k;
3147     const unsigned int keyL          = XK_l;
3148     const unsigned int keyENTER      = XK_Return;
3149     const unsigned int keySHIFTLEFT  = XK_Shift_L;
3150     const unsigned int keyZ          = XK_z;
3151     const unsigned int keyX          = XK_x;
3152     const unsigned int keyC          = XK_c;
3153     const unsigned int keyV          = XK_v;
3154     const unsigned int keyB          = XK_b;
3155     const unsigned int keyN          = XK_n;
3156     const unsigned int keyM          = XK_m;
3157     const unsigned int keySHIFTRIGHT = XK_Shift_R;
3158     const unsigned int keyARROWUP    = XK_Up;
3159     const unsigned int keyCTRLLEFT   = XK_Control_L;
3160     const unsigned int keyAPPLEFT    = XK_Super_L;
3161     const unsigned int keyALT        = XK_Alt_L;
3162     const unsigned int keySPACE      = XK_space;
3163     const unsigned int keyALTGR      = XK_Alt_R;
3164     const unsigned int keyAPPRIGHT   = XK_Super_R;
3165     const unsigned int keyMENU       = XK_Menu;
3166     const unsigned int keyCTRLRIGHT  = XK_Control_R;
3167     const unsigned int keyARROWLEFT  = XK_Left;
3168     const unsigned int keyARROWDOWN  = XK_Down;
3169     const unsigned int keyARROWRIGHT = XK_Right;
3170     const unsigned int keyPAD0       = XK_KP_0;
3171     const unsigned int keyPAD1       = XK_KP_1;
3172     const unsigned int keyPAD2       = XK_KP_2;
3173     const unsigned int keyPAD3       = XK_KP_3;
3174     const unsigned int keyPAD4       = XK_KP_4;
3175     const unsigned int keyPAD5       = XK_KP_5;
3176     const unsigned int keyPAD6       = XK_KP_6;
3177     const unsigned int keyPAD7       = XK_KP_7;
3178     const unsigned int keyPAD8       = XK_KP_8;
3179     const unsigned int keyPAD9       = XK_KP_9;
3180     const unsigned int keyPADADD     = XK_KP_Add;
3181     const unsigned int keyPADSUB     = XK_KP_Subtract;
3182     const unsigned int keyPADMUL     = XK_KP_Multiply;
3183     const unsigned int keyPADDIV     = XK_KP_Divide;
3184 
3185 #elif cimg_display==2
3186     // Define keycodes for Windows.
3187     const unsigned int keyESC        = VK_ESCAPE;
3188     const unsigned int keyF1         = VK_F1;
3189     const unsigned int keyF2         = VK_F2;
3190     const unsigned int keyF3         = VK_F3;
3191     const unsigned int keyF4         = VK_F4;
3192     const unsigned int keyF5         = VK_F5;
3193     const unsigned int keyF6         = VK_F6;
3194     const unsigned int keyF7         = VK_F7;
3195     const unsigned int keyF8         = VK_F8;
3196     const unsigned int keyF9         = VK_F9;
3197     const unsigned int keyF10        = VK_F10;
3198     const unsigned int keyF11        = VK_F11;
3199     const unsigned int keyF12        = VK_F12;
3200     const unsigned int keyPAUSE      = VK_PAUSE;
3201     const unsigned int key1          = '1';
3202     const unsigned int key2          = '2';
3203     const unsigned int key3          = '3';
3204     const unsigned int key4          = '4';
3205     const unsigned int key5          = '5';
3206     const unsigned int key6          = '6';
3207     const unsigned int key7          = '7';
3208     const unsigned int key8          = '8';
3209     const unsigned int key9          = '9';
3210     const unsigned int key0          = '0';
3211     const unsigned int keyBACKSPACE  = VK_BACK;
3212     const unsigned int keyINSERT     = VK_INSERT;
3213     const unsigned int keyHOME       = VK_HOME;
3214     const unsigned int keyPAGEUP     = VK_PRIOR;
3215     const unsigned int keyTAB        = VK_TAB;
3216     const unsigned int keyQ          = 'Q';
3217     const unsigned int keyW          = 'W';
3218     const unsigned int keyE          = 'E';
3219     const unsigned int keyR          = 'R';
3220     const unsigned int keyT          = 'T';
3221     const unsigned int keyY          = 'Y';
3222     const unsigned int keyU          = 'U';
3223     const unsigned int keyI          = 'I';
3224     const unsigned int keyO          = 'O';
3225     const unsigned int keyP          = 'P';
3226     const unsigned int keyDELETE     = VK_DELETE;
3227     const unsigned int keyEND        = VK_END;
3228     const unsigned int keyPAGEDOWN   = VK_NEXT;
3229     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
3230     const unsigned int keyA          = 'A';
3231     const unsigned int keyS          = 'S';
3232     const unsigned int keyD          = 'D';
3233     const unsigned int keyF          = 'F';
3234     const unsigned int keyG          = 'G';
3235     const unsigned int keyH          = 'H';
3236     const unsigned int keyJ          = 'J';
3237     const unsigned int keyK          = 'K';
3238     const unsigned int keyL          = 'L';
3239     const unsigned int keyENTER      = VK_RETURN;
3240     const unsigned int keySHIFTLEFT  = VK_SHIFT;
3241     const unsigned int keyZ          = 'Z';
3242     const unsigned int keyX          = 'X';
3243     const unsigned int keyC          = 'C';
3244     const unsigned int keyV          = 'V';
3245     const unsigned int keyB          = 'B';
3246     const unsigned int keyN          = 'N';
3247     const unsigned int keyM          = 'M';
3248     const unsigned int keySHIFTRIGHT = VK_SHIFT;
3249     const unsigned int keyARROWUP    = VK_UP;
3250     const unsigned int keyCTRLLEFT   = VK_CONTROL;
3251     const unsigned int keyAPPLEFT    = VK_LWIN;
3252     const unsigned int keyALT        = VK_LMENU;
3253     const unsigned int keySPACE      = VK_SPACE;
3254     const unsigned int keyALTGR      = VK_CONTROL;
3255     const unsigned int keyAPPRIGHT   = VK_RWIN;
3256     const unsigned int keyMENU       = VK_APPS;
3257     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
3258     const unsigned int keyARROWLEFT  = VK_LEFT;
3259     const unsigned int keyARROWDOWN  = VK_DOWN;
3260     const unsigned int keyARROWRIGHT = VK_RIGHT;
3261     const unsigned int keyPAD0       = 0x60;
3262     const unsigned int keyPAD1       = 0x61;
3263     const unsigned int keyPAD2       = 0x62;
3264     const unsigned int keyPAD3       = 0x63;
3265     const unsigned int keyPAD4       = 0x64;
3266     const unsigned int keyPAD5       = 0x65;
3267     const unsigned int keyPAD6       = 0x66;
3268     const unsigned int keyPAD7       = 0x67;
3269     const unsigned int keyPAD8       = 0x68;
3270     const unsigned int keyPAD9       = 0x69;
3271     const unsigned int keyPADADD     = VK_ADD;
3272     const unsigned int keyPADSUB     = VK_SUBTRACT;
3273     const unsigned int keyPADMUL     = VK_MULTIPLY;
3274     const unsigned int keyPADDIV     = VK_DIVIDE;
3275 
3276 #else
3277     // Define random keycodes when no display is available.
3278     // (should rarely be used then!).
3279     const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent).
3280     const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent).
3281     const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent).
3282     const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent).
3283     const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent).
3284     const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent).
3285     const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent).
3286     const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent).
3287     const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent).
3288     const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent).
3289     const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent).
3290     const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent).
3291     const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent).
3292     const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent).
3293     const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent).
3294     const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent).
3295     const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent).
3296     const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent).
3297     const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent).
3298     const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent).
3299     const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent).
3300     const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent).
3301     const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent).
3302     const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent).
3303     const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent).
3304     const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent).
3305     const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent).
3306     const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent).
3307     const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent).
3308     const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent).
3309     const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent).
3310     const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent).
3311     const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent).
3312     const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent).
3313     const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent).
3314     const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent).
3315     const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent).
3316     const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent).
3317     const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent).
3318     const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent).
3319     const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent).
3320     const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent).
3321     const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent).
3322     const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent).
3323     const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent).
3324     const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent).
3325     const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent).
3326     const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent).
3327     const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent).
3328     const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent).
3329     const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent).
3330     const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent).
3331     const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent).
3332     const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent).
3333     const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent).
3334     const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent).
3335     const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent).
3336     const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent).
3337     const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent).
3338     const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent).
3339     const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent).
3340     const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent).
3341     const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent).
3342     const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent).
3343     const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent).
3344     const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent).
3345     const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent).
3346     const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent).
3347     const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent).
3348     const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent).
3349     const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent).
3350     const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent).
3351     const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent).
3352     const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent).
3353     const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent).
3354     const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent).
3355     const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent).
3356     const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent).
3357     const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent).
3358     const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent).
3359     const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent).
3360     const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent).
3361     const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent).
3362     const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent).
3363     const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent).
3364     const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent).
3365     const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent).
3366     const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent).
3367 #endif
3368 
3369     const double PI = 3.14159265358979323846;   //!< Value of the mathematical constant PI
3370 
3371     // Define a 12x13 font (small size).
3372     static const char *const data_font12x13 =
3373       "                          .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w      Fw                      "
3374       "   mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxlwlwkwuwnwuynwuwTwlwlwtwnwtwnw my     Qw   +wlw   b"
3375       "{ \\w  Wx`xTw_w[wbxawSwkw  nynwky<x1w `y    ,w  Xwuw   CxlwiwlwmyuwbwuwUwiwlwbwiwrwqw^wuwmxuwnwiwlwmy"
3376       "uwJwiwlw^wnwEymymymymy1w^wkxnxtxnw<| gybwkwuwjwtwowmxswnxnwkxlxkw:wlymxlymykwn{myo{nymy2ykwqwqwm{myo"
3377       "zn{o{mzpwrwpwkwkwswowkwqwqxswnyozlyozmzp}pwrwqwqwqwswswsxsxqwqwp}qwlwiwjybw`w[wcw_wkwkwkwkw mw\"wlwiw"
3378       "=wtw`xIw awuwlwm{o{mylwn|pwtwtwoy`w_w_wbwiwkxcwqwpwkznwuwjzpyGzqymyaxlylw_zWxkxaxrwqxrwqyswowkwkwkwk"
3379       "wkwkwk}qyo{o{o{o{owkwkwkwkznxswnymymymymyayuwqwrwpwrwpwrwpwrwqwqwpwkwtwlwkwlwuwnwuynwuwmyTwkwlwuwmwu"
3380       "wnwkwlwuwmwuwkxlwuxmwkwlwuwnwuynwuwTwkwlwuwmwuwlwmwkwtwUwuwuwowswowswowswowsw;wqwtw_ymzp~py>w bwswcw"
3381       "kwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw"
3382       "rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wt"
3383       "wmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlwtwpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawsw"
3384       "owswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw"
3385       "rwpwrwpwswoznwtw  Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuw"
3386       "nwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwewewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqw"
3387       "uwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux"
3388       "myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqx"
3389       "uwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nwuwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnw"
3390       "twmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw"
3391       "uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpw"
3392       "rwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwowrwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnyn"
3393       "wtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws"
3394       "wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwt"
3395       "wqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmwswowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwt"
3396       "wqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo"
3397       "yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwr"
3398       "wpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswcwtxows"
3399       "wowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux"
3400       "qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmw"
3401       "kwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkwjwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswsw"
3402       "owswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw"
3403       "twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtw"
3404       "hwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswowswowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqw"
3405       "qwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow"
3406       "swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_"
3407       "}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmwswkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwk"
3408       "wtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws"
3409       "wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowt"
3410       "wtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwuxqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owk"
3411       "xuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq"
3412       "wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkws"
3413       "wowswowswowswowswowswowswcwuwuwowswowswowswowswowtwnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxm"
3414       "w bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw"
3415       "swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswsw"
3416       "swswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTxuxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtw"
3417       "nwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw"
3418       "tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtx"
3419       "pxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxowkwswowswowswowswowkwkwkwkwswowswowswowswowswowswow"
3420       "swlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz"
3421       "mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzoz"
3422       "mymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznxlwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pw"
3423       "txn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw"
3424       "qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynw"
3425       "swnymymymymybzmznznznznwlzmw     hwHwlwSwTw <w8z ]x tx Zxjwmx RwWw/wgw pw_ynwky=wCwmwaw\\w_wnw  1wIwl"
3426       "z 'wiwuwaw  mw    Pw   swlwjw     hw        f| pyWx/wgw rxSw/wCwmwaw\\w_wnw  1w  AwRx  nw    Pw   txk"
3427       "wlxm";
3428 
3429     // Define a 20x23 font (normal size).
3430     static const char *const data_font20x23 =
3431       "                                                9q\\q^r_rnp`qnq`plp7q\\q^q_qmqbq\\q^q_qmqHqmp_q\\q^r_rnp"
3432       "`qnq7q\\q^q_qmq_q \"r                                                       Mq^q^qnq`pnr`qnq`plp6q^q^p"
3433       "mp`qmqaq^q^pmp`qmqIpmq]q^q^qnq`pnr`qnq6q^q^pmp`qmq`q \"plp         'q     5qmq               Vq      "
3434       "               Xq    [plp      3qYq_p^rnpLplp8qYq_qNqYq_q4rmpaqYq_q_rmp%qYq^pGq  Irc|!pKp]raqjq`p   "
3435       "HtNq_qmq\\plqbp_shpdscq[q^q[p [q]s_r`uau]rbv`tcxbuat LsZucrav_udwcxdw`udqiqeq]q]qjreq]sksgrjqbtcv_tcv"
3436       "aud{eqiqgqfqgqjsjqlrjrhrirfzfs`q[sZqMqJqCqNsLq]q]q]q]q   .scq]s \\sKt%r  [s^raxdxat_qazgqlqlqctJqIqIq"
3437       "LqHsOqiqOtaqmq\\uft nufu`sLs`t\\qKv<r\\rLrepirepitgpeq]r^r^r^r^r^r^{gudxdxdxdxdq]q]q]q]wcrjqbt`t`t`t`tL"
3438       "tlpgqiqeqiqeqiqeqiqgrireq[s_q[q_pnp_pnr`qnq`plp7q[q_s`qmqcq[q_s`qmq]pkpbpmr`q[q_s`pmraqmq8q[q^pnp_qn"
3439       "q^qaq\\qnq !pnqd{!pJp^tdunucr _y  dvOq_qmq\\plpap_pmpipdudq[p\\p_plplp _q^ubtawcw^rbvavdxcwcw Ou]yerawb"
3440       "xeyexdwbxeqiqeq]q]qkrdq]sksgrjqdxewbxewcwe{eqiqfqhqfqjsjqkqjqfqiqezfs`q[s[sMpJqCqOtLq]q]q]q]q  q 1tc"
3441       "q]t ^vaq_w&r  \\u_raxdxcxcuczgqlqlqexMsJqJsMq[p^uPqiqdq]uaqmq]qkqcq!qkqguaqmqNpkp\\p]pKtmp:p]plpKpfpfp"
3442       "fpcpipdq]r^r^r^r^r^r^{ixexdxdxdxdq]q]q]q]yerjqdxdxdxdxdxPwnpfqiqeqiqeqiqeqiqfqiqdq\\u_p[p^pnpKqnq_r5p"
3443       "[p^pmp`qmqbp[p^pmp`qmq]tKp[p^pmpLqmq7p[p]pnp_qnq^p`q\\qnq5uauauauaucq`qhq4p]pKr_ueunucr `q  \\rkpOq_qm"
3444       "q\\plpctbqmqkqerlpdq\\q\\q_qnpnq\\q%q^qkqcqnqapjrdpjr`sbq]rkp^qcrkrerkq Oplr`sirgtbqkrdripeqjsfq]q]ripeq"
3445       "iqeq]q]qlrcq]sksgskqerjrfqkrdrjrfqkrerjp`q`qiqfqhqeqkskqiqlqdqkq\\qeq]qZq\\qmqNqKqCqOqIq5q]q  q 1q`qZq"
3446       " _rlqbtaqjp$q  ^qkqatbr^q]rjrewdqhqgqlqlqfrjrOuKqKu8p_rlpOqkqcq]qFpgpcp\"pgpTpkp\\q^p\\p^qLump:p^pjpLpg"
3447       "pepgpbpjpPt`t`t`t`t`qnq_qnqcripeq]q]q]q]q]q]q]q]qjsfskqerjrfrjrfrjrfrjrfrjrRrjrfqiqeqiqeqiqeqiqeqkqc"
3448       "vbrlq`q]q_plp Iq]q_qmqNq]q_qmqKtIq]q_qmq ^q]q^plpKq`q mqkqcqkqcqkqcqkqcqkqdq`qhq5q^qLt`ueunudtasbqip"
3449       "`q`pipcq  [qIq_qmq`{gvcqmqkpdq_q\\q\\q]rZq%q_rkraqZq]qaqnqbq]qXqcqiqeqiq1pSpXq`qfrhqnqbqjqdq]qhqfq]q]q"
3450       "]qiqeq]q]qmrbq]qnqmqnqgskqeqhqfqjqdqhqfqjqeqYq`qiqfrjreqkskqirnrdrmr]qdq]qZq]qkq)qCqOqIq5q]q  q 1q`q"
3451       "Zq _qkq_qaq mq  ^qkqaqnqar_q]qhqfrnqnreqhqgqlqlqfqhqPwLqLw9p_q_phqdqkqcq]qGplslpiu#pmtlpUpkp\\q_q_r8u"
3452       "mp:p^pjpLpgpepgperipcq^qnq`qnq`qnq`qnq`qnq`qnq`qmqcq]q]q]q]q]q]q]q]q]qhqfskqeqhqfqhqfqhqfqhqfqhqdphp"
3453       "fqirfqiqeqiqeqiqeqiqermrcwcqkq    [q 3qZp Oq nqmqmqeqiqeqiqeqiqeqiqeq_piq4q^pLvatd|evdvcqipasaqkqdq "
3454       " [qHq_qmq`{hrnpmpcqmqlpcq_q\\pZp]rZq%q_qiqaqZq]qapmqbq^qWqcqiqeqiqdq]qUsSs[qaqdqhqnqbqjqeq\\qgqgq]q^q\\"
3455       "qiqeq]q]qnraq]qnqmqnqgqnqlqfqfqgqjqeqfqgqjqeqYq`qiqeqjqdqlqmqlqhqnqbqmq]rdq]qZq^pgp=taqns`s`snqatdv_"
3456       "snqcqnsbq]q]qkqcq]qnsmshqns`saqnsasnqcqnr`tbvaqjqeqiqdqkqkqjrkreqiqdw`q`qZq#tnreqkq^qatauaqnsdqiq`ra"
3457       "qjqdqiqdpmrcxdqmqmqatbxfyeqiqbqnq`r`q^qfqhrmqmrfqhqgqlqlqgqfqep[pnqnp[p`q`pipbpnqnpNq]taq^qnqnqbqmqb"
3458       "q\\qIqmpkpmqkqkp$qmpkpmqVqmq\\q`q[pLqjqeump:p^pjpLphpdphpapkpbq^qnq`qnq`qnq`qnq`qnq`qnq`qmqdq\\q]q]q]q]"
3459       "q]q]q]q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqfrjrhqiqnqgqiqeqiqeqiqeqiqdqmqbqkrdqmsbt`t`t`t`t`t`tlsfs_t`t`t`"
3460       "tbq]q]q]q[tbqns`s_s_s_s_s\\q`smpdqjqdqjqdqjqdqjqeqiqdqnscqiq;qlqlqgqgqgqnqmqnqgqjqnqgqgqfq_qjq<{fpjpL"
3461       "vatd|fxeqkqdqipasaqkqdp  \\yNqGplqeqmp`qmqmqcrLqZq`qnpnq\\q%q_qiqaqZq^rbqmqbubqms^qaqkqdqiqdq]qXuf{fu_"
3462       "q`qlrnqlqjqlqcqkreq\\qgqgq]q^q\\qiqeq]q]t`q]qnqmqnqgqnqlqfqfqgqkreqfqgqkres[q`qiqeqjqdqlqmqlqhs`s]rcq]"
3463       "qZq#vbwcvbwcwev`wcwcq]q]qlqbq]vnthwcwcwcwcubwcvaqjqdqkqcqkqkqiqkqdqiqdw`q`qZq7smsfxdqlr^qavdvawdqkq_"
3464       "raqjqdpgpeqntdxdqmqmqcwdyfyeqiqcqlq`raq^qfqhqlqlqfqhqgqlqlqgqfqfrZqZraqarkraqLq^vbq^wbqmqbq]tKpmpfpk"
3465       "pjp_plp9plpkplpUs[qaqZpLqjqeump:p^pjpaplp_piqdpiqaplqbq_qlqbqlqbqlqbqlqbqlqbqlqbrmqdq\\q]q]q]q]q]q]q]"
3466       "q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqerlrgqjqmqgqiqeqiqeqiqeqiqcsaqjqdqnq`vbvbvbvbvbvbvnuivbwcwcwcwcq]q]q]"
3467       "q]wcwcwcwcwcwcwOwcqjqdqjqdqjqdqjqeqiqdwdqiq;pkqkpgpepgpmumpgpjrmpgpepfq_qkq;{hrkpLxdxf|fxepipdqipas`"
3468       "pkpcp  ZqHqGplpdt_pmplpmshsMqZqaplplp]q&q^qiqaq[qat`plqbvcx_q`ucrkr:uc{cucq`qlvlqjqlqcwdq\\qgqgxdvcqj"
3469       "tfyeq]q]s_q]qmsmqgqmqmqfqfqgwdqfqgwcv_q`qiqdqlqbqmqmqmqfr`s]qbq\\q[q#pjqcrlrdqkpcrlrcqkrdq^rlrcrlrdq]"
3470       "q]qmqaq]rlrlqirlrdqkqcrlrerlrcr_qjpbq]qjqdqkqcqlslqhqmqbqkq^q_q`qZq_tjpSqmsmpgrlsdqnsaqmqbqkqdq\\rlrd"
3471       "qlq_raqjqeqgqgrnqnrdqlqcqmqmqcqkqerkq`qaycqlq_rbq^qfqhqlqlqfqhqgqlqlqgqnvnqgrYqYrbqbrirbqLq_rnpmpdwa"
3472       "qmqcydq^qlqLpmpfpkpkq`plpa{RpltkpB{gpXpLqjqdtmpcqHp]plp_plp`pipjpipipmsfplpjphr_qlqbqlqbqlqbqlqbqlqb"
3473       "qlqbqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqhqfqhqfqhqfqhqfqdrnrfqkqlqgqiqeqiqeqiqeqiqcsaqjqdqnq`pjqcpjqc"
3474       "pjqcpjqcpjqcpjqcpjrlrjqkpbqkrdqkrdqkrdqkrdq]q]q]q]qkrdrlrdqkqcqkqcqkqcqkqcqkqOqkqcqjqdqjqdqjqdqjqdqk"
3475       "qcrlrdqkq:pnwnpgpnwnpgplslpgpkrlpgpkqkpfq^qlq6qaqlpMzfzfzfzgqipdqipbqmp`qmqc|  fqHqHqlpcuasmplpmpiul"
3476       "qSqZq]p^{+q^qiqaq\\q`ubqlqbpkrdrkrarawcx<tEteq`qlqlqlqjqlqcwdq\\qgqgxdvcqjtfyeq]q]t`q]qmsmqgqmqmqfqfqg"
3477       "vcqfqgv_t`q`qiqdqlqbqmqmqmqgs_q]qaq\\q[q\"vcqjqeq]qjqdqiqdq^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcq^"
3478       "s^q]qjqdqkqbqmsmqgqmqbqkq_qas_qYsc{Spkqkphqkrcqntcvcqiqeq\\qjqdqmr`tbqjqeqgqgqmqmqdqlqcqmqmqdqiqfqiqa"
3479       "qaycqlq_qaq^qfqhqlqlqfqhqfqmqmqfqnvnqh}cqc}cqc}cqLq_qmpawbqkqasaq^qkqMpmpfpjsnpaplp`{RplpmqkpB{huatK"
3480       "qjqbrmpcqJt^r]plpctlpjqktlpmpkpltlpjqhq^qlqbqlqbqlqbqlqbqlqcrlrcqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqh"
3481       "qfqhqfqhqfqhqfqcteqlqkqgqiqeqiqeqiqeqiqbq`qkrdqmravbvbvbvbvbvbvjqkq]qiqeqiqeqiqeqiqdq]q]q]q^qiqdqjqe"
3482       "qiqeqiqeqiqeqiqeqiqd{hqkpnqdqjqdqjqdqjqdqjqdqkqcqjqdqkq:pnwnpgpnwnpgplslpgplrkpgpkqkpfq^qlq6qaqmqMzg"
3483       "|fxdxfqipdqipbqmqaqmqcp  \\wLqK{dt]qmqmqkrmrnrSqZqK{TtKq^qiqaq]r\\rdqkq\\qdqiqaqarkrcsmq<tEtfq_qlqlqlqk"
3484       "qjqdqjqeq\\qgqgq]q^qgqfqiqeq]q]qnraq]qmsmqgqlqnqfqfqgq^qfqgqkq]raq`qiqdqlqbqnqkqnqgt`q^raq\\q[q#wcqjqe"
3485       "q]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcq]uaq]qjqcqmqaqmpmpmqfs`qmq_ras_qYscpjtRpkqkphqkrcqk"
3486       "reqlrcqiqcr_qjqdqmq_qnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqaq`q^qfqhqlqlqfqhqfrnqnrfqfqh}cqc"
3487       "}cqc}cqLq_qmp_q^qkq`qMrlqMpmpfpWplpUqRplplqlp=q&qjq`pmp _plp]qkpnpdqhpeqkpnpiq^qjqdqjqdqjqdqjqdqjqdq"
3488       "jqdqkqdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqbrdqmqjqgqiqeqiqeqiqeqiqbq`wcqlrcwcwcwcwcwcwc~"
3489       "kq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqd{hqlpmqdqjqdqjqdqjqdqjqcqmqbqjqcqmq9pkqkpgpepgpmumpgp"
3490       "mrjpgpepfq]pmq:{epmpLzg|evbveqipdqipbqmqaqmpbq  [qHqK{cpmq^plqmqkqktRqZqFqOtKq^qiqaq^rZqdy^qdqiqaqaq"
3491       "iq]q:uc{cudq_qlqlqmqjxdqiqfq\\qgqgq]q^qgqfqiqeq]q]qmrbq]qlqlqgqlqnqfqfqgq^qfqgqkr]qaq`qiqcqnqaqnqkqnq"
3492       "hrnq`q_r`q\\q[q$qjqcqjqeq]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcqZsbq]qjqcqmqaqnqmqnqfs`qmq`r"
3493       "^r`qZr9pkqkphqkrcqjqeqkqcqiqet_qjqcqnq`rnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqbr`q]qhqgrmqmr"
3494       "fqhqeweqfqgrYqYrdpnqnpdrirdpnqnpNq_qmp_q]qmqcyPrmqMqmpkpmqkvaplpVqRqmpkpmq=q&qjq`pmp(v_plp\\pkpmpdphq"
3495       "epkpmpjq]xdxdxdxdxdxdwdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqcteqnqiqgqiqeqiqeqiqeqiqbq`vbq"
3496       "jqeqjqdqjqdqjqdqjqdqjqdqjqdqjxkq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqmplqdqjqdqjqdqjqdqjqcq"
3497       "mqbqjqcqmq9qlqlqgqgqgqnqmqnqgqnqjqgqgqfq]qnq:{eqnpLzg|dt`tdqipcpipbpkp`sbq  Zq plq`pmq_pkqmqkqjrQqZq"
3498       "Fq'q]rkraq_rYqdy^qdqiqbq`qiq^q6uf{fuaq_qlyjzeqiqeq]qhqfq]q]qhqfqiqeq]q]qlrcq]qlqlqgqkseqhqfq]qhqfqjq"
3499       "]qaq`qiqcqnq`skshrmraq_q_q[q\\q$qjqcqjqeq]qjqdq\\q^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcqXqbq]qjqcq"
3500       "mqaqnqmqnqgqmq`s_q\\q`qZq7pmpnqmpgqkrcqjqeqkpbqiqeq\\qjqcs_qlqcqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaq`qkq"
3501       "drjrdr_q]riqfrnqnreqhqducqhqerZqZrdwdrkrdwOq_qmp_q^w`q`q[sKplslpTplpWqQpmpkqnp<q&qjq`pmp aplp\\pkplpe"
3502       "phqepkplpjq^zfzfzfzfzfzfxcq]q]q]q]q]q]q]q]q]qhqfqkseqhqfqhqfqhqfqhqfqhqcrnreriqfqiqeqiqeqiqeqiqbq`q]"
3503       "qjqeqjqdqjqdqjqdqjqdqjqdqjqdqjqdq]q]q]q]q\\q]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqnpkqdqjqdqjqdqjqdqjqb"
3504       "saqjqbs7qmqmqeqiqeqiqeqiqeqiqeq]qnp7q]rJrnpnresnpnsct_rcqipcqkqcqkqasaq  [rkp&plpcplpnr`qkqmqkrltRqZ"
3505       "qFq'q\\qkq`q`r_pjr^qcpjrcqkrbq`rkrdpkr3sSsLrlrnrhqhqeqjreripeqjsfq]q]riqfqiqeq]q]qkrdq]qgqgqkserjrfq]"
3506       "rjrfqjrfpiraq_qkqbt`skshqkqaq`q^q[q\\q$qkrcrlrdqkpcrlrcqipdq^rlrcqjqdq]q]qmqaq]qkqkqiqjqdqkqcrlrerlrc"
3507       "q^pjqbq]rlrbs_rkrfqmq`s`r\\q`qZq6qlrfrmscrlrepkqbrkqdqkpaqjqcs`rlqcrlrernsnrgrnqnrdqlqcrnqnrdrkqdqkra"
3508       "q`qkqdqhqer^q\\rkqdwdqhqbqarjrdpYqYpbubpipbuNq_rnpmpbq^qnqnq`q`qZqIpgpRplp7pgp;q&rlr`pmp bplp[pkufpiq"
3509       "dpkukrlpcqhqfqhqfqhqfqhqfqhqfqhqfqjqcripeq]q]q]q]q]q]q]q]qjsfqkserjrfrjrfrjrfrjrfrjrdrlrfrjreqkqcqkq"
3510       "cqkqcqkqaq`q]qnplqeqkrdqkrdqkrdqkrdqkrdqkrdqksjpjqkpbqipdqipdqipdqipdq]q]q]q]qkqcqjqdqkqcqkqcqkqcqkq"
3511       "cqkq^qbqkqcrlrdrlrdrlrdrlrbsarlrbs6qkqcqkqcqkqcqkqcqkqdq\\r7q\\qFp\\p]r^rcqipcvbqkqas`r  \\vOqIqlpcw_pip"
3512       "mpivnrRpZpEqbqIq^q[ubwdxdw]qcwbwaq_wcvbq]qRpSp[q^q^qhqexcxeyexdq\\xeqiqeq]q]qjrexdqgqgqjrdxeq\\xeqiqfx"
3513       "`q_war_ririqiqbqazfq[q\\q$xcwcvbwcxdq]wcqjqdq]q]qlqbq]qkqkqiqjqdwcwcwcq^wbu`wbs_rkrgqkq`q`w`q`qZq$yew"
3514       "dqmq`wdvaqjqbr`qkqcyeyewcqlsdwcxdw`sauczexdq^umteucqhqbq`xLqJsKsMq^vdxdpgpaq`qYqIqkq bqkq?{+yapmp Jp"
3515       "fpfpipcpfpiucqhqfqhqfqhqfqhqfqhqfqhqfqjxixexdxdxdxdq]q]q]q]yeqjrdxdxdxdxdxdrjrgpnwdwcwcwcwaq`q]qnuex"
3516       "dxdxdxdxdxdvnwjvbxdxdxdxdq]q]q]q]wcqjqdwcwcwcwcw^qbwbwcwcwcwaq`w`q4uauauauaucq\\r7p[qFp\\p\\p\\pbqipasap"
3517       "ip`q^y  ctNqIqmqbu_phsgslrSq\\qEqbqIq^qZsawdxcu\\qbt^taq]uataq]q q]qgpiqfqfw`udwcxdqZudqiqeq]q]qirfxdq"
3518       "gqgqjrbtcqZtcqirfv_q]s_r_rirjrircqazfq[q\\q#tnqcqns`s`snqaucq\\snqcqjqdq]q]qkqcq]qkqkqiqjqbsaqnsasnqcq"
3519       "]t_t_snqaq^rkrhrkraq`w`q`qZq#smrevbs^t`s`qjqbq`qiqdqnrmqdrmrcubqkrcubqntat^r`sc|fxdq^umtcqaqhqbq^tJq"
3520       "IqIqLq]tcxLq`qYqHu `u>{+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr"
3521       "bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms"
3522       "_snqbsnqbsnqbsnqaq`qns_q !p Zp      jp#q\\q6q7q   lq [sjq  Qq -q  OqZq]q  Cq;q HqWq $rIq`qZq _q iqbqK"
3523       "qFqIq`q     hp$q]u   JqYpmpLp   .p        jp    ]p Xr`q[r !p       Tp\"p\\p6q6q   mq Yx  Qr -r  Ps\\q_s"
3524       "  Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q     hp$q]t   IqYpmpLq   /q        kq     Fq_q[q #s       Tp\"q"
3525       "^q6p   1p Vu  Rs    YsJsMy &v<s HqWq &sHtcq]t _q iqbqKqFqIq`q     hp$q   2q2q   /q        kq     Hs_"
3526       "q]s \"q                (r     Xy %t;r GqWq &rFscq]s ^q iqbqKqFqIq`q         ,q4r   0r        lr     G"
3527       "r^q                               *q                                                                "
3528       "                   kr               i";
3529 
3530     // Define a 47x53 font (extra-large size).
3531     static const char *const data_font47x53 =
3532       "                                                                                          "
3533       "                                                                    9])]2_2]T\\8^U^3]  E])]"
3534       "2`4^U^>])]2_4^U^ 6^T\\5])]1_2]T\\8^U^  K])]2`4^V^3]                                         "
3535       "                                                                                          "
3536       "                                                        U]*\\2a4`V\\8^U^5a  F]*\\1\\X\\4^U^=]*\\"
3537       "2a5^U^ 7aV\\4]*\\1a4`V\\8^U^  J]*\\1\\X\\4^V^3\\                                                 "
3538       "                                                                                          "
3539       "                                                S],\\1\\W\\5g8^U^6c  F],\\1\\V\\5^U^<],\\2]W]6^U^"
3540       " 8h3],\\0\\W\\5g8^U^  I],\\1\\V\\5^V^4\\      ;]                                                 "
3541       "                                                                                          "
3542       "                                         :\\-]2\\U\\6\\V`7^U^7]U]  F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\"
3543       "Va1\\-]1\\U\\6\\V`7^U^  H\\-]2\\T\\6^V^5]      =a                                J]              "
3544       "                                                                                          "
3545       "                                            N\\/]2\\S\\7\\T]6^U^7\\S\\  E\\/]2\\R\\7^U^:\\/]3]S]8^U^"
3546       " 8\\T^/\\/]1\\S\\7\\T]6^U^  G\\/]2\\R\\7^V^6]      =c                                L^           "
3547       "                                                         *^                            U` "
3548       "                                        O^             )\\S\\                     !^$^3\\  E]"
3549       "U\\  K^$^4^ G^$^4]   J^$^3\\   #^$^3\\ 4^            B[                                      "
3550       "                              &^                            Xe                            "
3551       "             S^             (\\S\\               )Z      Q^&^3^2]S\\ A\\S\\  K^&^3^ F^&^4_  >]S"
3552       "\\9^&^3^2]S\\   W^&^3^ 6^        Q]    M[               ?`   ![1^H]?` =]4](\\    %` >b4c  Bb "
3553       "?`2a    .a   Ib   Pb      Aa <a @b      Fb =b  F^ :] '] Da A].].].].]            <_:]._   "
3554       " Xh ?c   W^       @`   La   Pa        Sa   Va5^U^ @`   \"f4_ >`0`*^   $^.` <^F]F^F]G`G]    "
3555       " F\\S\\ ;b        %a2a2a2a2a <bR\\     D`4^(^3`4`U\\8^V^6\\S\\  J^(^3`4^U^@^(^3_4^U^/^/`U\\8^(^3`"
3556       "4`U\\8^V^  K^(^3`4^V^1^9]+^V^      ?`    O\\  D\\6]M]            We D]1]T] 9[3bJ\\@e<])]2])\\  "
3557       "  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"
3558       "_K]S^J^F^G^CrBb7]*b'_ D] :] '] Fc A].].].].]            >a:].a   !^T_ Bg   `       Dd2_8n?"
3559       "m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d   Fb@f5a Ad4e-] :f  Ra0d AaF\\HaF\\HeJ\\?]._0_"
3560       "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"
3561       "^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   "
3562       "P[ 9[/a:aQa7[    Wl      \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\    U^1f8c8k;j1`;k7h?n;h9g    5i*b:_"
3563       "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"
3564       " A].].].].]      ;]     (b:].b   #^Q] Dj  !a       Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f "
3565       "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"
3566       "?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"
3567       "^>]*]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:]   "
3568       "\"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_."
3569       "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]&])"
3570       "c D] <] '] G] :].].].].]      ;]     (^6]*^   #]P^ E^P\\   V^       H^T^4_8n?m:`S`6]:rD]P]P"
3571       "]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]>"
3572       "]._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^@],^"
3573       "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]+^"
3574       "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   $"
3575       "]2]P]<_W]8]N]<ZL^4a;]+]MZ/]<^P^=^Q^    7\\O]1nAa9]N_<_M]C]NaA].]+_L^E]H]C].].]N_?].aKaHaL]@"
3576       "^M^C]P_:^M^C]P_=^M\\6]6]H]F^G^D]MaM]P^N^B^K^-^B]1]&]*e D] =] '] H] 9].].].].]      ;]     )"
3577       "^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"
3578       "^1]L];[   N]L] Q]S] :\\,\\1] <dU\\  M\\2\\P\\ >\\H\\A\\H\\<\\M\\=]/a2a2a2a2a1_/]V];_M]C].].].].].].].]"
3579       "-]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"
3580       "^<].]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<"
3581       "_   &r      $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T]   &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^  "
3582       "  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]"
3583       "O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9]  B].]      ;]     )]4](]   %]N]:c6]   G]       J^P^7a8"
3584       "_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\\ >"
3585       "\\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]"
3586       "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"
3587       ";\\6]G]   P\\ :`5g@gWh>a   (_       J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.]   T]2]N]5]8ZJ]-]"
3588       "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]"
3589       "M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_  &] '] H] 9]  B].]      ;]     )]4](]   %]N]:d7] "
3590       "  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\\."
3591       "]  IeU\\  M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]"
3592       "B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N]    .]        '`X_           I]   FbWa=bWa=bWa=bWa=bWa<"
3593       "\\6^I^  ?Z2[ :a5gAiXh?c   *^       H] 7]1]T]-]S]Aj>]R]Q]@]1],],\\1^X\\X^,]   T]3]L]6]'].]7]W]"
3594       ";]-]!]<]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]"
3595       "M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^-^@]0]']-_S_  '] '] H] 9]  B].]      ;]     )]4](]   %]N]:e8"
3596       "_   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\\"
3597       "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]"
3598       "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]   "
3599       "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"
3600       "],],]0d*]   T]3]L]6]'].]7\\V];].] ]<]L]@]K]  7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;]"
3601       ".]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])`"
3602       "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"
3603       "]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"
3604       "\\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"
3605       "]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]  "
3606       "  :]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["
3607       "RZ>]R]R\\>]1],],].`(]   U^3]L]6]'].]8]V];].]!^<]L]@]K]  :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]"
3608       "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"
3609       "8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](]   PdU"
3610       "]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_ @]"
3611       " @_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"
3612       "^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"
3613       "]H]C]H]C]H]A^S^<k<]Ra<h9h9h9h9h9h9hTeFf7e6e6e6e;].].].]\"^;]Vd8f7f7f7f7f/^6eX]@]L]?]L]?]L]?"
3614       "]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]"
3615       "-\\R\\B]T[6]R]S]>^2]-]*\\.`(]   U]2]L]6]'].]9]U];].]!];]L]@]K]  =` P`'^7]?\\I]U];]K]@].]F]E].]"
3616       ".].]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"
3617       ";]Xf9h9fX]<h?h3fX]?]Xg=].].]P_=].]XfVfL]Xg:h<]Xf9fX]?]Xb7i>h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](]"
3618       "   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]@"
3619       "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"
3620       "\\ ?\\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]"
3621       "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"
3622       "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"
3623       "`7]N]?]       F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*]   U]3]J]7]'].]9\\T];].\\Ua-^;]L]@]K^?].] Uc "
3624       "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]"
3625       "Q]I^X^8^U^.^<]/](]1^I^  ]R_<aT_;_R\\:^Tb=_S^@h4_Ub?bT^=].].]Q_<].aT_X]T^LbT^;_T_=aT_;^Tb?aT"
3626       "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]"
3627       "=_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"
3628       " :[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"
3629       "]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^>"
3630       "]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"
3631       "^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^"
3632       "=]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"
3633       "^?].] 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"
3634       "]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^"
3635       ">`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]"
3636       "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"
3637       "]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]"
3638       "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]"
3639       "H]?^W^:]M]>]U^6ZM^<ZM^<ZM^<ZM^<ZM^<ZM^<ZMbP]M^NZ;^P^=^P^=^P^=^P^>].].].]+i=`Q^=^P^=^P^=^P^"
3640       "=^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"
3641       "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]"
3642       "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"
3643       "^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])]   B]=_O]=].]O_>]N^>].]O_?_O]>].].]S_:]._P`P]M_O]=]N]>_O]"
3644       "=]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]"
3645       ">]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` >\\"
3646       "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]"
3647       "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]']"
3648       ".].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]"
3649       "@]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]?]       "
3650       "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\\"
3651       "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.^:]"
3652       ".])]   B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T"
3653       "^<^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"
3654       "]/]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"
3655       "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"
3656       "]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"
3657       "]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^  P]P]P\\G]C\\G]T^"
3658       "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 @"
3659       "](]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].]"
3660       ".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"
3661       "^?^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?"
3662       "]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"
3663       "]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^"
3664       "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]"
3665       "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]?].].].].^"
3666       "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"
3667       "]2]O]  QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\       D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&]   W]1]J"
3668       "]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"
3669       "]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]"
3670       "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"
3671       "]=]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"
3672       "_:] 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"
3673       "\\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"
3674       "]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L"
3675       "]@^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"
3676       "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"
3677       "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"
3678       "^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]?]/^.]"
3679       ".]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"
3680       "]>]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"
3681       "[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"
3682       "^;]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"
3683       "_(].].].].].].]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]"
3684       "?]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 "
3685       "     '] 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"
3686       "]>]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]-]*"
3687       "]   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]"
3688       "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"
3689       "]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] "
3690       "  ]L];aU\\<]   I]T],]O[X\\>]K]@]O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]"
3691       "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]"
3692       "@].].].]/]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_"
3693       "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](]"
3694       ".\\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"
3695       "^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]?]"
3696       ".].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]="
3697       "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="
3698       "]=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`"
3699       "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]"
3700       "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]@]"
3701       "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];"
3702       "]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 ("
3703       "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"
3704       "]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^"
3705       ":]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]@"
3706       "]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]"
3707       " 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]?]-].].]"
3708       ".].].].].]-]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]"
3709       ".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]"
3710       "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]"
3711       "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"
3712       "]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"
3713       "?]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"
3714       "^=]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"
3715       "^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]"
3716       "/\\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]"
3717       "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]@]"
3718       "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]"
3719       "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]"
3720       "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"
3721       "]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]"
3722       "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"
3723       "^;]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_;"
3724       "] 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"
3725       "@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`"
3726       "?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?"
3727       "]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];]"
3728       "       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@]"
3729       "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]+],]"
3730       "   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"
3731       "^,]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];"
3732       "^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"
3733       "]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]"
3734       "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]?].].].].]-]"
3735       ".].].]/]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"
3736       "]W^L]F]E]B\\.]U]  NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];]       E]   Nu=[S]3\\R]R]O]M_X\\ M]("
3737       "] 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"
3738       "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]?].]."
3739       "]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"
3740       "^.]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"
3741       "]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"
3742       "\\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"
3743       "]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]"
3744       "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"
3745       "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"
3746       "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]"
3747       "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"
3748       "]<]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]>"
3749       "]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 ="
3750       "[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]=]"
3751       "/].].].].].].].]-]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^"
3752       "?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8"
3753       "^M^<]W]  I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W]  MuC]X[ ;cWZWbDeWZXe>e6e>]L]?]L]=]P]8^X^:]    "
3754       "   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]@"
3755       "]/]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]+^-]   "
3756       "R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]"
3757       "(]  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]"
3758       "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^"
3759       "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"
3760       "]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_?].].].].].].]."
3761       "].].]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^@"
3762       "\\-]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]  !]"
3763       ",]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]"
3764       "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"
3765       "]M]N]L]?]L]?^M]?]M^?] ]<].]M^;]W]5aRaB^U^6c8_(]4](]  FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?"
3766       "\\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]"
3767       "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]<"
3768       "]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^?"
3769       "]M^?]M^?]M^?]M^?]M_?^/^/^/^/^/].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;`O]?]M^?]M^?]M^?]M^;c8"
3770       "^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"
3771       "]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]"
3772       ">^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_>"
3773       "_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-]O_;]X^5aRaC^S^6a8_']4](]  D]P"
3774       "^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"
3775       "^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]4]'^ <\\F\\ M\\S\\  J\\F\\     L^N^6\\U\\   ,\\S\\-]OhG]K]@]OhQ]LZ=]G]"
3776       "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^"
3777       "L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^"
3778       ";_O]=]O_>]O_>]O_>]O_:a7_O]9a  E^P_>^P_>^P_>^P_>^P_>\\,a  H^.] /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ "
3779       "      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"
3780       "`.]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"
3781       "]7^.]*^.]   Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`"
3782       "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"
3783       "^?]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"
3784       "\\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^"
3785       "@^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>_"
3786       "LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a  Dk<k<k<k<k>],a  "
3787       "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"
3788       "]<]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]*"
3789       "`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]"
3790       "=_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"
3791       "^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]"
3792       " 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].].].].]."
3793       "].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_T"
3794       "b>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6"
3795       "aS^7_  Bi:i:i:i:i=]+`  I],] /[,[-].]:]L]<h;]N]7`3q      \"h E] 7]S]=k5]LdIjW^ M],] /]:] 8]1"
3796       "](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"
3797       "]2j7_2`M`K^J]9]8tC])].]   PgX]>]Xf9h9fX]<k>],fX]?]L]?].].]O^=].]M]M]N]L]<h<]Xf9fX]?]/j9d4g"
3798       "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;]:"
3799       "]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"
3800       "]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]="
3801       "gX]=gTjLh9k<k<k<k?].].].]+h<]L]<h9h9h9h9h Fk:gX]=gX]=gX]=gX]9_6]Xf6_  @e6e6e6e6e;]+_  G\\+["
3802       " /].]-[,[9]L];e:^N^8`2p       e D] 7]S]<i4\\JbGgT^ M\\,\\ .]:] 8]1]'d8k?n>i-]<i4e6]0h;g8].]  "
3803       " 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"
3804       "6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](]   QdV`B]Xe;"
3805       "d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] "
3806       "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>"
3807       "]K`<g8g8g8g8g J]Vh:h9h9h9h6]6].]Ve;dV]<dV]<dV]<dV]<dV]<dV]<eRiJf7i:i:i:i?].].].]*f;]L];f7f"
3808       "7f7f7f F]Xe7dV]<dV]<dV]<dV]9_6]Wd5_  <\\-\\-\\-\\-\\6]+_  FZ*[ /].],Z+Z9]L]8`8]L]7^.m       W` "
3809       "A] 7\\R\\7b2]H^BaP_ O].] .]:\\ 7]2^%`6k?n:b*]9c/a5],b6b5].\\   H]/\\4]C]Di0b=h9n?]#c?]H]C].].]I"
3810       "_Dm>]C]H]J_9a<]$d?]I^?c0].b3_2_K_M^G^;]8tC](]/]   M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]"
3811       "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_:"
3812       "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"
3813       ";]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"
3814       "]:`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]      "
3815       "=]                       &[   O].]  E]  E]         ']    S]        R]      ^       (](]/] "
3816       "       C]  S]    '] V]      F^ 7]4](]   %])[  4]7] @])_Q_:] 9]6]                6[   S]0[R"
3817       "^           H]%\\U\\ A\\            @\\             /Z            <\\             ,[    M^5](^ "
3818       "     =]                       &[   N]0]  D\\  D]         '\\    Q^DZ       1]      _       )"
3819       "](]/]        D^  S]    '] V]      F] 6]4](]   %]   ;]7] @] /] 9]6]                6[   S]0"
3820       "g           H]%\\U\\ @\\            @\\                          J\\                  X]4](]   "
3821       "   <]                       &[   N]0]  D\\  E^         '\\    P^G]       2]      X^       )]"
3822       "(^0]        D]  R]    '] V]      G^ 6]4](]   %]   ;]7] @] /] 9]6]                6[   S]0e"
3823       "           F]%\\U\\ ?[            ?[                          I[                  ^4])^     "
3824       " @ZV]                       &[   M]2]  D]  E]         ']    O_K_       3]      V^       *b"
3825       ",]5b        E^  R]    '] V]      G^ 6^5])^   %]   ;]7] @] /] 9]6]                6[   S].a"
3826       "           D]%\\U\\ ?\\            @\\                          J\\                 !^4])^     "
3827       " B\\V]                       &[   M]2]  D\\            G\\    L`P`       2]      U^       +b "
3828       "=b        RZN^  R^    '] V]      H^ 4^6]*^   $]   ;]7] @] /] 9]6]                6[   S]  "
3829       "          J]  :\\            @\\                          J\\                 \"^3]*^      A\\V"
3830       "\\                       %[   L]4]                   Vm       2^      S^       ,b =b       "
3831       " R\\Q_  R]    &] V]      I^ 3b:].b   $]   ;]7] @] /] 9]6]                6[   S]           "
3832       " J]  @ZU]            FZU]                          PZU]                 #^2]+^      @b    "
3833       "                   %[                       Si       4b                       %i  Ua    &]"
3834       " V]      Mb 2a:].a   #]   ;]7] @] /] 9]6]                   .]            J]  @b          "
3835       "  Fb                          Pb                 'b2]       E`                            "
3836       "                   Qb       1a                       $g  S`    %] V]      Ma /_:]._   !]  "
3837       " ;]7] @] /] 9]6]                   .]            J]  @a            Ea                     "
3838       "     Oa                 &a1]       D^                                                     "
3839       "  X^                 Ip      Fc  Q^    #] V]      M_  A]    )]   ;]7] @] /] 9]6]          "
3840       "                      T]  @`            D`                          N`                 %_/"
3841       "]       BZ                                                                        Ap      "
3842       "                 6]                                                                       "
3843       "                                                                                          "
3844       "                          p                       6]                                      "
3845       "                                                                                          "
3846       "                                                                                          "
3847       "                                                F]']2]    +]']2^ D]']3_   E]']1]   \"]']2^ "
3848       "8]                             H";
3849 
3850     // Define a 90x103 font (huge size).
3851     static const char *const _data_font90x103[] = {
3852       // Defined as an array to avoid MS compiler limit about constant string (65Kb).
3853       // First string:
3854       "                                                                                          "
3855       "                                                                                          "
3856       "                                                                                          "
3857       "                                                                                          "
3858       "            HX     4V         >X       IX           *W             FW                     "
3859       "                                                                                          "
3860       "                                                                                          "
3861       "                                                                                          "
3862       "                                                                                          "
3863       "                                                         HX  W 4Z 3VCT   <Z     >X  W 4Z  "
3864       " HX  W 4Z     'VCT ;X  W 3Y 2UCT       KX  W 3Y   0W                                      "
3865       "                                                                                          "
3866       "                                                                                          "
3867       "                                                                                          "
3868       "                                                                                          "
3869       "                                    @W !W 4\\ 5YET ?XHX 8]     >W !W 4\\ 7XGX KW !W 4\\ 7XHX "
3870       "  +YET :W !W 3[ 5ZFT ?XGX     EW !W 3[ 7XGX 5W                                            "
3871       "                                                                                          "
3872       "                                                                                          "
3873       "                                                                                          "
3874       "                                                                                          "
3875       "                              >W \"V 3\\ 7]HU ?XHX 9`     ?W \"V 3\\ 7XGX JW \"V 3\\ 7XHX   -]HU"
3876       " 9W \"V 3] 7]HT ?XGX     DW \"V 3] 8XGX 5V                                                  "
3877       "                                                                                          "
3878       "                                                                                          "
3879       "                                                                                          "
3880       "                                                                                          "
3881       "                        <W $V 3VNV 8_KV ?XHX 9`     >W $V 3VNV 8XGX IW $V 3VNV 8XHX   -_KV"
3882       " 8W $V 2] 7_KU ?XGX     CW $V 2] 8XGX 6V                                                  "
3883       "                                                                                          "
3884       "                                                                                          "
3885       "                                                                                          "
3886       "                                                                                          "
3887       "                        :W &W 4VLV :j >XHX :VJV     >W &W 4VLV 9XGX HW &W 4VLV 9XHX   .j 6"
3888       "W &W 3VMV 9i >XGX     BW &W 3VMV 9XGX 7W               MW                                 "
3889       "                                                                                          "
3890       "                                                                                          "
3891       "                                                                                          "
3892       "                                                                                          "
3893       "                          CV 'W 4VJV ;j >XHX ;UGV     >V 'W 4VJV :XGX GV 'W 4VJV :XHX   .j"
3894       " 5V 'W 3VKV :i >XGX     AV 'W 3VKV :XGX 8W               N[                               "
3895       "                                                                                          "
3896       "                                                                                          "
3897       "                                                                                          "
3898       "                                                                                          "
3899       "                            DV )W 4VHU <VK_ =XHX ;TEU     =V )W 4VHU :XGX FV )W 4VHU :XHX "
3900       "  /VK_ 3V )W 3VIV <UK_ =XGX     @V )W 3VIV ;XGX 9W               N]                       "
3901       "                                                                                          "
3902       "                                                                                          "
3903       "                                                                                          "
3904       "                                                                                          "
3905       "                                    DV *V 3UFU =UH\\ <XHX <UDT     <V *V 3UFU ;XGX EV *V 3U"
3906       "FU ;XHX   /UH\\ 1V *V 2UGU <TH] =XGX     ?V *V 2UGU ;XGX 9V               a                "
3907       "                                                                                          "
3908       "                                                                                          "
3909       "                                                                                          "
3910       "                                                                                          "
3911       "                                           EV ,V 3UDU >TEY ;XHX <TBT     <V ,V 3UDU <XGX D"
3912       "V ,V 3UDU <XHX   /TEY /V ,V 2UEU =TFZ <XGX     >V ,V 2UEU <XGX :V               Na        "
3913       "                                                                                          "
3914       "                                                                                          "
3915       "                                                                                          "
3916       "                                                                                          "
3917       "                                                   DU -V 3VDV ?TCV :XHX <TBT     ;U -V 3VD"
3918       "V =XGX CU -V 3VDV =XHX   /TCV -U -V 2UCU >TCU :XGX     =U -V 2UCU =XGX ;V               NV"
3919       "IV                                                                          \"W            "
3920       "                                                                                          "
3921       "                                                                                          "
3922       "                                                                                          "
3923       "                                                              JU /V 3VBV     ETBT     :U /"
3924       "V 3VBV   FU /V 3VBV       (U /V 2UAU         DU /V 2UAU   @V               NVGV           "
3925       "                                                               $X                         "
3926       "                                                                                          "
3927       "                                            *X                                            "
3928       "                                                                                          "
3929       "                           JX                                GTBT                         "
3930       "                          MX  GX 7V     :UEU     DX  GX 7V   JX  GX 7W       4X  GX 6V    "
3931       "     GX  GX 5V   (X                            &X                                         "
3932       "                                                                                          "
3933       "                            )X                                                     8V     "
3934       "                                                                                          "
3935       "            ;X                                FTBT                                        "
3936       "           LX  IX 7X     <UCU     DX  IX 7X   JX  IX 6W       3X  IX 6X         GX  IX 5X "
3937       "  *X                            &Y                                                        "
3938       "                                                                                          "
3939       "             (X                                                     9V                    "
3940       "                                                                                       <X "
3941       "                               ETBT                                                   KX  "
3942       "KX 6X 1TBT   BTAT     CX  KX 6Y   JX  KX 6Y     (TBT BX  KX 5X 1TBT       LX  KX 4X   +X  "
3943       "                          %T                                                    #W 9W     "
3944       "                                                                                          "
3945       "3a   :a     <W   2W    >W   E\\   AW ,W ,W ,W ,W                             HY GV +Y      "
3946       "   4Z           NX                 @X                                                     "
3947       "             %W                                DUDU                                       "
3948       "          =Y 7W  KW 6Z 4XDT   BTAT     BW  KW 6Z   IW  KW 6[   ,Y )XDT AW  KW 5Z 4XDT     "
3949       "  KW  KW 4Z   ,W BW                 8V         (S                                         "
3950       "    <S       9V 7V                                                                        "
3951       "                       3a   :a     ;W   3W    >W   H_   AW ,W ,W ,W ,W                    "
3952       "         L] GV +]         ;a          #[                 F^                               "
3953       "            8XGX                      +W                                BTEU              "
3954       "                      *R            9a :W  MW 6\\ 6ZET ?XHX <TAT     AW  MW 6\\ 7XGX LW  MW "
3955       "5[ 7XGX .Y +ZET @W  MW 5\\ 6ZET ?XHX     DW  MW 4\\ 7XHX 0W AW &XHX               MZ        "
3956       " +T                                   $Y         BS 1W,V MY   8W 7W  T           9X   5Z /"
3957       "[     0Z   8Z /Y           GY       .\\       <\\               [   4[   :\\              -a "
3958       "  :a     :W   4W    >W   Ja   AW ,W ,W ,W ,W                             N_ GV +_         "
3959       "?e   8]       J]                 Jb       8[       <[                  $Y       FY 7XGX   "
3960       "=Z         Di 5W   8Z .Y !W         FW *Y   4W)V*W)V-Y(V            <UFU   3\\             "
3961       "       +[ 0[ 0[ 0[ 0[   4[=T            <e ;W  W 5\\ 7\\FT ?XHX <TAT     @W  W 6^ 8XGX KW  W"
3962       " 5] 8XGX .Z@R ?\\FT ?W  W 4\\ 7\\FT ?XHX     CW  W 3\\ 7XHX 1W @W &XHX               N\\       "
3963       "  ,T     :U :U5U                            `   EX 2VFV   .S 4]0W\"b DV  V 5V  T         7W"
3964       " .` 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["
3965       "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"
3966       " ,W ,W                            !a GV +a         Ch   =f       ^                 Mf 2Z @"
3967       "x Mx <c 3X C~Q)X?X?X Kc   2T   .V   .T   CX   $a  !W.W   N` ;XGX ![ Lb       &Z Mi 7[   >a"
3968       " 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"
3969       " @[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"
3970       "VNV 8XGX JW \"W 5VMV 9XGX -ZDV @]HU >W \"W 4VNV 8]HU ?XHX     BW \"W 3VNV 8XHX 2W ?W &XHX    "
3971       "           ^ K~\\       >S   3Q +[ @[;[ ;Q                          ;e   HX 2VFV #VBV FS 6`"
3972       "1V#g GV !V 3V !T         7W 0d :` ;j ?k -[ Dq :g Ky Df ;d          $f   1Z @o 5j Np Ex Mt "
3973       ":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   "
3974       "5W    >W   Lc   AW ,W ,W ,W ,W                            \"b GV +a         Dk   Aj      \"_"
3975       "                 h 3Z @x Mx ?i 6X C~Q)X?X?X Ni   6V   /V   /V   DX   &f  #W0W   e >XGX %c#"
3976       "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"
3977       " 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"
3978       "X <TAT     >V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT <V \"V 4VLV :_IT >XHX     AV \"V 3VLV 9"
3979       "XHX 2V >W &XHX              !_ K~[       >T   4R -_ D_?_ >S         =t                Fh  "
3980       " IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T         7V 0f @e >o Co 0\\ Dq <j Ly Fj ?h          (i  "
3981       "\\ ?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%"
3982       "a =V $a  ]   EV   6W    >W   Md   AW ,W ,W ,W ,W               HW             1b GV +b    "
3983       "     Fm   Dm      #`                \"j 4Z @x Mx Am 8X C~Q)X?X?X!m   9X   0V   0X   EX   'h"
3984       "  $W0W  \"h ?XGX 'g%g       0h%i :a   Cf :f *V   4m    %^ 0e   A^+V/^+V1f1V!X )Z /Z /Z /Z /"
3985       "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 ;"
3986       "V $V 4UJU :ULXLU >XHX <UCU     =V $V 5VJV :XGX HV $V 4VKV :XGX +ZL\\ AULXLU ;V $V 3UJU :ULX"
3987       "LU >XHX     @V $V 2UJU 9XHX 3V =W &XHX              !` K~Z       >T   4S /a FaAa @T       "
3988       "  @w                Hl   KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T         7V 2j Eh ?q Dp 1\\ Dq >"
3989       "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"
3990       "-X(X,X6[6XAY3Y+Y0Y%~S%W 3V  IW !_   FW   7W    >W   Md   AW ,W ,W ,W ,W               HW  "
3991       "           2[ ?V #[         Hn   En      #`                #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o  "
3992       " ;Z   1V   1Z   FX  KS 0i  #W2W LV ,i ?XGX *l'h       3l'i ;c   Dg ;g ,W   6o    %^ 1g   B"
3993       "^,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"
3994       "-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"
3995       " ?TI_ ;W &W 4VJV ;TI_ >XHX     @W &W 3VJV :XHX 4W =W &XHX     1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y     "
3996       "  =S   4U 1c IdCc AU         Dz                In   LX 2VFV $VBV ES 9g7V$k HV #W 1W #T    "
3997       "     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 -"
3998       "^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 "
3999       ",W ,W ,W               HW             2X <V  X         H[G[   Go       KZ                %"
4000       "[H[ 7\\ Ax Mx Ds ;X C~Q)X?X?X$s   >\\   2V   2\\   GX  KS 1j  #W2W LV -j ?XGX +ZEZ)VGY       "
4001       "5ZDZ)i <e   EUFY <UFX -W   7q    %VMU 2YIY   CVMU,V.VMU,V0UFX3V X *\\ 1\\ 1\\ 1\\ 1\\ 1\\ 0~W4v%"
4002       "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"
4003       " <UH] =XHX ;VGV     ;W (W 5VHV ;XGX GW (W 4UGU ;XGX )c =UH] 9W (W 3UHU <UH] =XHX     ?W (W"
4004       " 2UHU :XHX 5W <W &XHX     5c 8c 8c 8c 8c @WKU J~X       >T   5V 2e KfEe CW         G|     "
4005       "           Jp   MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T         8V 3n Gh ?s Fr 5^ Dq @n Lx Ir"
4006       " 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["
4007       "6X?X5X'X2X#~S%W 2V  JW #c   FW   9W    >W   NX   4W ,W ,W ,W ,W               HW          "
4008       "   2W ;V  NW         IZCY   Hp       JY                &ZDZ 9^ Bx Mx Eu <X C~Q)X?X?X%u   @"
4009       "^   3V   3^   HX  KS 2k  \"W4W KV -ZGW ?XGX -X=X+R@W       8X<X  .XIX   FQ@W <Q@W /W   7dGU"
4010       "    %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"
4011       "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 "
4012       "<XGX FW *W 4VGV <XGX (_ :TF\\ 8W *W 3UFU =TF\\ =XHX     >W *W 2UFU ;XHX 6W ;W &XHX     7h =h"
4013       " =h =h =h DWJV K~X       >T   5W 4g MgFg EY         J~                K]FZ   MX 2VFV $VBV "
4014       "ES :XGX9V%\\GX HV $W /W 3PATAP         GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[        "
4015       "  /[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"
4016       "]8X>Y7Y'Y4Y#~S%W 2V  JW $e   FV   9W    >W   NW   3W ,W ,W ,W ,W               HW         "
4017       "    2W ;V  NW         IY@X >X 4[AV       IX                &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X"
4018       "&^E^   B`   4V   4`   IX  KS 3\\GW  \"W4W KV .YBT ?XGX .V7V,P=W       :W8W  /VEV   3V +V /V "
4019       "  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 ,{\""
4020       "_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     "
4021       "7V *V 4UDV =XGX EV *V 4VEV =XGX )] 7TEZ 6V *V 3UDU >TEZ <XHX     =V *V 2UDU <XHX 6V :W &XH"
4022       "X     9k @k @k @k @k EWJV K~W       >T   5Y 5g MhHi G[         M~Q                L\\AW   M"
4023       "X 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR         HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B"
4024       "[ I[CZ          0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X"
4025       "?] J[=X =X <X/X+X,X)X8]8X=Y9Y%Y6Y )Y$W 2W  KW %ZMZ   FV   :W    >W   X   3W     4W ,W     "
4026       "          HW             3X ;V  NX         KY?X Ca 9Y:R       HX                (X>X :VNV "
4027       "BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\   Db   5V   5b   JX  KS 3ZBT  !W6W JV .X?R   4V4U HV       ;V"
4028       "4V  1VCV   4V *U 0V   7fGU     KU 4WAW   <U.V#U.V JU6V MX +^ 3^ 3^ 3^ 3^ 3^ 2XIX F]=Z&X -X"
4029       " -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 ,"
4030       "V 3UDU >TDX   ;a     6V ,V 4UBU   GV ,V 3UCU   0` 6TDX 4V ,V 2UDU >TDX       >V ,V 1UDU   "
4031       ":V 9W       (o Do Do Do Do GWIU J~V       >T   6Z 6i jIj I\\         N~R                M[="
4032       "U   MX 2VFV %VBV H] AWCW;V%Y=R HV %W -V 4UETEU         IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R"
4033       " -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"
4034       "=[ F[;[&X<[ LZ8U =X <X/X+X,X)X8]8X<X9X#X6X )Z$W 1V  KW &ZKZ   FV   ;W    >W   W   2W     4"
4035       "W ,W               HW             3W :V  MW         KX=W Cc ;X7P       HX                ("
4036       "W<W ;WNW BY /X ([;[ Gg JX0X)X?X?X([;[   Fd   6V   6d   KX  KS 4Y>R  !X8X JV /X<P   6V1U IV"
4037       "       <U0U  2UAU   3U *U 1V   6fGU     KU 4V?V   <U/V\"U/V IU7V LX ,` 5` 5` 5` 5` 5` 3XIX "
4038       "G[7W&X -X -X -X -X -X -X -X ,X9_%`8X![;[![;[![;[![;[![;[  %[;](X/X'X/X'X/X'X/X)X6X MX ,X;W"
4039       " :V .V 3UBU ?TBT   7]     3V .V 4VAU   GV .V 3UAU   4d 7TBT 1V .V 2UBU ?TBT       ;V .V 1U"
4040       "BU   <V 8W       )r Gr Gr Gr Gr IVHR GX+W       =S   5[ 7i!kJk I]        !^               "
4041       " )Y:T   MX 2VFV %VBV Le EVAV<V$X:P HV %W -W 6WFTFV         IV 4X?Y IRBX ?T7Y IP5Z :VNX DX "
4042       "+Z8P .Y JY<Y KY=X          1S;Y 6];\\$WNW CX9Z J[4U&X6]%X -X )[2V)X/X'X -X -X<[ LX -XNV6VNX"
4043       "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 "
4044       "  2W     4W ,W               HW             3W :V  MW         KW<X Dd <W       -W         "
4045       "       )W;X <WNW AY 0X )Z7Z Jl MX0X)X?X?X)Z7Z   Hf   7V   7f   LX  KS 4X;P   W8W IV /W   \""
4046       "V.U JV       >U.U  4VAV &V 5U *U 2V   6gGU     KU 5W?W   =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5"
4047       "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'"
4048       "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       "
4049       "  CV 0V 1U@U   >V 7W       *`L` I`L` I`L` I`L` I`L` JV =X,X       >T   6] 9k\"lKl K_       "
4050       " #\\                'Y8S   MX 2VFV %VBV Nk IVAV=V$X 1V %V +V 6YHTHY -V       EW 5Y>Y :X ?R5"
4051       "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=["
4052       " 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      -"
4053       "W    >W   W   2W     4W ,W               HW             3W :V  MW         KW;W De =W      "
4054       " -X                *W:W <VLV @Y 1X *Z5Z Mp X0X)X?X?X*Z5Z   Jh   8V   8h   MX  KS 5Y   :X:X"
4055       " IV /W   #U+T JV       ?U+T  5U?U &V 5U +V     AgGU     KU 5V=V   =U0V!U0V IV8V KX ,WNW 5W"
4056       "NW 5WNW 5WNW 5WNW 5WNW 4XHX IZ1T&X -X -X -X -X -X -X -X ,X4\\'a9X#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z  )Z5Z"
4057       "(X/X'X/X'X/X'X/X(Y:Y LX -X:W          !W                    2\\LZ                          "
4058       "EW       +[@[ K[@[ K[@[ K[@[ K[@[ KV <X-X     /P 0T   7^ 9k\"lLm La        %Z              "
4059       "  %Z6Q   MX 2VFV %VCV n KWAW>V$X 1V &W +W 5XITIX +V       EV 4X<X :X ?P2Y -X <WMX DX ,Y  C"
4060       "X JY:Y MX9W          2P7Y :Z0Z(WLW DX7X KY.R&X2Z&X -X *Y+R)X/X'X -X -X>[ JX -XNW8WNX0a9X#Y"
4061       "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  "
4062       "   4W ,W               HW             3W :V  MW         LX;W Df >W       ,W               "
4063       " +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y   Kj   9V   9j     AS 5X   8W:W HV /W   #T)T KV     "
4064       "  @T(T  6U?U &V 5T +V     AhGU     KU 5V=V   =U0V!U0V JV7V   WLW 7WLW 7WLW 7WLW 7WLW 7XNX "
4065       "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"
4066       " >W8V                               *XHZ                          FW       ,Z<Z MZ<Z MZ<Z "
4067       "MZ<Z MZ<Z LV <X.X     .R 2S   7` :k#nMm Mb        &Z                $Y4P   MX 2VFV &VBV!o "
4068       "KV?V?V#W 0V &V )V 3XKTKX )V       EV 5X:X ;X  X -Y =VLX DX -Y  CY JY:Y NY9X           HX ;"
4069       "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"
4070       "/X)X0X&X:VMV:X9Y?Y NY<Y *Y W 0V  LW *ZCZ      /W    >W   W   2W     4W ,W               HW"
4071       "             3W :V  MW         LW:W Dg ?W       ,X                ,W8W >WLW ?Y 3X +Y1Y\"v#X"
4072       "0X)X?X?X+Y1Y   MYNVNY   :V   :YNVNY     BS 5X   8X<X HV /W   $T?ZBT*c       AT&T  7U?U &V "
4073       "6U -W     @hGU     KU 6V;V   >U1V U1V KW7V   NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X "
4074       "-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      "
4075       "                         +UDZ               7P          1W       -Y8Y Y8Y Y8Y Y8Y Y8Y MV ;"
4076       "W.X     /T 4T   7a ;k#nMn Nc 6P :W4W ?Z ?X6X KY                #Y   0X 2VFV &VBV\"p KV?V?V#"
4077       "W 0V 'W )W 2XMTMX 'V       FW 5X:X ;X  Y -X >VKX DX -X  BX IX8X NX7W      KP  1P  =X <Y)X+"
4078       "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"
4079       "X&X:VMV:X8YAY LY>Y *Z W 0W  MW +ZAZ      0W    >W   W   2W     4W ,W               HW     "
4080       "        3W :V  MW         LW:W DSF[ @X       -X                -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)"
4081       "X?X?X,Y/Y   YMVMY   ;V   ;YMVMY     CS 5X 5P*Q JW<W GV /W   %TBbET/g       BTGb?T  8U?U &V"
4082       " 7U 5_     ?hGU     KU 6V;V   >U2V NU2V$_7V   NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X"
4083       " -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     "
4084       "                          +R@Y               7Q          2W       .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y N"
4085       "V ;X/X     0V 5T   8c <k#nNo e >^ AW4W ?Z >W6W KY                \"Y   0X 2VFV &VCW#[LSKZ K"
4086       "V?V@V\"W 0V 'W )W 1XNTNX &V       FW 6Y:Y <X  NX -X ?WKX DX .Y  CY IX8X NX7W      NS  1S  @"
4087       "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"
4088       "/X)Y1X%W;WMW;W6XAX JX>X *Z NW 0W  MW ,Z?Z      1W    >W   W   2W     4W ,W               H"
4089       "W             3W :V  MW         LW:W DPAY ?Y       .W                -W6W @WJW >Y 5X ,X-X&"
4090       "_MXM_&X0X)X?X?X,Y/Y  !YLVLY   <V   <YLVLY     DS 6Y 6R,R JX>W FV /X   'TCfFT2i       CUGfB"
4091       "T  9U?U &V 7U 5]     >iGU     KU 6V;V   >U2V NU2V$]5V   NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX"
4092       " 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 "
4093       "GW=\\                                GY               9S          3W       /XDVDX$X2X$X2X$X"
4094       "2X$X2X V ;X0X     0X 7T   8d <k#~`!g Bd DW4W ?[ ?X7W LY                !X   /X 2VFV &VCV#Z"
4095       "JSGV KV?VAV!W 0V 'V 'V /d $V       FV 5X8X <X  NX -X ?VJX DX .X  BX HX8X Y7X     #V  1V  C"
4096       "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/"
4097       "X(X2X$X<VKV<X6YCY JY@Y +Z MW /V  MW -Y;Y    \"Z ;WDX 0Z 2XDW >Z <W !X :WDY     IW ,W  HX8X "
4098       "MY 3Z *X 3X &X 7] <W             3W :V  MW       ;X :W:W 4Y @[ )\\ (Y   6X     8QEV     :[ "
4099       "    JW6W @VIW =Y 6X -Y-Y(]JXJ]'X0X)X?X?X-Y-Y  #YKVKY   =V   =YKVKY     IZ 9X 6T.T JW>W FV "
4100       ".X   (TDgFT3j       CTFhDT  9U?U &V 8U 4\\     =iGU     KU 6V;V   >U3V MU3V#\\5V   MWJW 9WJW"
4101       " 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"
4102       "_+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"
4103       " /Z /Z   3ZCV          5WDX       DXCVCW%X0W%X0W%X0W%X0W V :X1X     0X 7T   9f =k#~`\"h Cf "
4104       "EW4W @\\ ?X8X LX                !Y   /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V       GW 5X"
4105       "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"
4106       " -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"
4107       "    'b ?WG^ 7b 9^GW A` Gl 2_GW MWG_ DW ,W ,W8Y MW ,WG^>^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm <W6W#"
4108       "X2X#W;X;W5Y7Y#W1X\"u 6W :V  MW       >^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X <W6W   HTG[ K}!WCWCW Ca"
4109       " 7p&{ NW6W AWHW >Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X  $YJVJY   >V   >YJVJY     Ma =X 7V0V JW@W E"
4110       "V .Y   *TEiET5k       DTEiDT  :VAV &V 9U 3_   ;W6W NiGU     KU 6V;V   >U3V MU3V#_8V   NXJX"
4111       " ;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"
4112       "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"
4113       "G_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X     NWBVBW&W.W&WJP:PJW&W4PJW&W."
4114       "W!V :X2X     0X 6S   8g >k#~`#j Fj GW4W @\\ >W8W LX                 X   .X 2VFV 'VBV$XGSCR "
4115       "KV?VBV X 1V (W 'W ,\\  V       GW 5X8X <X  NX -X AWIX DX /X  BY HX8X X5W     ([  1[  HX ?W "
4116       "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#"
4117       "X<VKV<X4XFY FXBX *Y KW /W  NW /Y7Y    +g AWIb ;f =bIW De Il 3bIW MWIc FW ,W ,W9Y LW ,WIbBb"
4118       "6WIc >f CWIb =bIW MWI^ =j Im <W6W\"W2W\"W<Z<W4X7X!W2W!u 6W :V  MW       @bEW\"W:W 2X @c 8j CW"
4119       "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 "
4120       "  =V   =WIVIW     f ?X 8X2X KW@W EV .Z   +SE[GVDS6ZDV       DSDVDXDS  9UAU %V :U 2`   <W6W"
4121       " NiGU     KU 6V;V   >U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X "
4122       "-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 "
4123       "<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"
4124       "2W MWIb IW2W     NWAVAW(W,W(WJR<RJW(W4RJW(W,W\"V 9W2X     1X 6T   9i ?k#~`#k Hl HW4W @] ?X9"
4125       "W LW                 NX   .X 2VFV 'VCW$WFSAP KV?VBV NW 1V (V &W *X  MV       GV 5X6X =X  N"
4126       "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"
4127       "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"
4128       "WJe ?j AeJW Eh Kl 5eJW MWJe GW ,W ,W:Y KW ,WJdDd7WJe @h DWJe AeJW MWJ_ ?l Im <W6W\"W2W!W=Z="
4129       "W2X9X W2W!u 6W :V  MW       BeFV!W;X 1W ?f =k CWJe NY2X =X =W6W JW-W$WI` N}!WCWCW Gi Av&{ "
4130       "W4W BVGW <Y 8X .X)X+ZEXEZ)X0X)X?X?X.Y+Y  #UHVHU   <V   <UHVHU    !j AX 9Z4Z KWBW DV -Z   -"
4131       "TFY@RDT8XAV       ETDVBWET  :VCV %V ;V )X   =W6W NiGU     KU 6V;V   >U5V KU5V GX<V FX /WHW"
4132       " ;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"
4133       "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"
4134       " GWJe @h =h =h =h =h ,Z @hLV NW6W MW6W MW6W MW6W\"W2W MWJe KW2W     W@VAW)W+W)WJT>TKW)W4TKW"
4135       ")W+W\"V 9X3X     2X 5T   :k ?i\"~`$m Jn IW4W A^ ?X:X MW                 NY   .X 2VFV 7~X2XFS"
4136       " <V?VCV MX 2V )W %W +X  MV       GV 5X6X =X  NX -X BVGX DX /X  BX GX8X X5X LX -X  7a  1a  "
4137       "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/"
4138       "X'X4X\"X>VIV>X2YIY DYFY +Z JW .V  NW 1Y3Y    1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW "
4139       ",WKfGg8WKg Cl FWLh ChLW MWK` @m Im <W6W\"X4X!W=Z=W1X;X NW3X!u 6W :V  MW       CgGV!W;W 0X ?"
4140       "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 #"
4141       "SGVGS %P 7V 9P0P CSGVGS    !l BX 8ZGWFZ JWCX DV ,Z   .SEW<PCS8V?V .P>P     JSCVAVDS  :WEV "
4142       "$V <V &W   >W6W NiGU     KU 6V;V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX <XDX"
4143       " 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"
4144       "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"
4145       "W MW6W MW6W MW6W\"W3X MWLh LW3X     V?V@W*V)W*VJV@VKW*V4VKW*V)W#V 9X4X     2X 4S   :l ?i\"~`"
4146       "%o Lp JW4W A^ >W:X MW                 NX   -X 2VFV 7~X2WES <V?VDV LX 2V )W %W -\\  V       "
4147       "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 "
4148       ",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 "
4149       " W 2Y1Y    2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,W<Y IW ,WLhIi9WLi En GWMj EjMW MWLa An I"
4150       "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!}!"
4151       "WCWCW Im Fy&{ W4W CWFW ;Y :X /X'X-YCXCY*X0X)X?X?X/Y)X!R #QFVFQ $R 9V :R1R DQFVFQ    \"n BX "
4152       "7ZJ\\JZ HWDW CV +[   1TFW.T:W?V /Q?Q     KTCVAWET  :XIX $V =V #U   >W6W NiGU     KU 6V;V BQ"
4153       "?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"
4154       "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"
4155       "n Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6W MW6W MW6W!W4W LWMj LW4W     "
4156       "W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X     2X 5T   ;n ?g!~_%p LZDZ JW4W A^ >W:W MW            "
4157       "     MX   -X 2VFV 7~X2WES <WAWDV KX 3V )W %W /` \"V       HV 4X6X =X  Y .X BVFX DX 0X  BX E"
4158       "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"
4159       "'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"
4160       "kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im <W6W!W4W W>\\>W0X=X LW5X u 6W :V "
4161       " MW       EkJV W<X /W >j Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X."
4162       "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   "
4163       "  KSBV@VDS  9e #V ?W \"V   ?W6W NiGU     KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX"
4164       " ?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/"
4165       "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 "
4166       "Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X     V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W     2"
4167       "X 4T   ;o @g ~^%q NY@Y KW4W B` ?X<X MV                 LX   -X 2VFV 7~X2WES ;VAVDV JY 4V )"
4168       "V $W 1d $V       HV 4X6X =X  X .Y CWFX DXLY =XEX 'Y EY<X MX5X LX -X  ?e  )e +Y ?V:X6V4WDW "
4169       "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>"
4170       "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"
4171       " ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V  MW "
4172       "    9X=X\"[IZKW W=Y /W @m H]DV CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y "
4173       "<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"
4174       " 1SAS     LTBV@VDS  9d \"V @W  U   ?W6W NiGU     KU 5V=V ASAS 2U7V IU7V @U@V DX 1WDW ?WDW ?"
4175       "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"
4176       "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[ "
4177       "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\\"
4178       " 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"
4179       "W                $x   FX 2VFV 7~X2WES ;VAVEW IY 5V *W #W 4XNTNX &V       IW 5X5X =X  X .X "
4180       "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["
4181       " =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>"
4182       "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"
4183       "6W W6W MW?\\?W.YAY JW6W 2Y 6W :V  MW     ;\\A\\%YDYLV NW>Y .W AXJa IZ<Q C^BZ MX8X =\\ ?W6W LW)"
4184       "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"
4185       "X <V   CV -\\JSHT BX 4v DXHX BV -b   7SEV*S;V?W 2TBT     LSAV@VCS  9b !V AV  MU   ?W6W MhGU"
4186       "     KU 5V=V ATBT 3U8V HU8V ?UAV CX 1WDW ?WDW ?WDW ?WDW ?WDW ?WDW ?XBX NX +X -X -X -X -X -"
4187       "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 "
4188       "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"
4189       " GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W     V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X    "
4190       " 4X NU:T   <s Ae N~^'u\"X<X LW4W BWNW >W<W MW                $w   EX   2~X2WES ;WCWEV GY   "
4191       "9W #W 5XMTMX 'V       IV 4X4X >X !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X  Ee   Le 3Z ?U=bKUC"
4192       "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 "
4193       "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"
4194       "NV@X;]@Y JY>Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V  MW     =_C_(YBXLV NW"
4195       "?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y L[B[ ;W >W2W FWBW 9Y >X 0X%X0X"
4196       "@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  "
4197       "   LSAV@VCS  7_  V BV  LU   ?W6W MhGU     KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AX"
4198       "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'"
4199       "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"
4200       " ,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    "
4201       " 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        "
4202       "        $x   EX   2~X2WES :VDWEV FZ   :W #W 7XKTKX )V       IV 4X4X >X !X 0Y BWDX Dm FXKf "
4203       "/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"
4204       "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="
4205       "\\ 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"
4206       "X GW8W 3Y 4W :V  MW     >aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWC"
4207       "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"
4208       "X 2q @XJX AV /WK[   :SFV)S;V@X 4VDV     LSAV@VCS  6\\  MV CV  KU   ?W6W MhGU     KU 4V?V @V"
4209       "DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW"
4210       "@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"
4211       "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 "
4212       "KW6W MW6W MW6W MW6W NW8W J\\=Y NW8W     NV=V=V.V$V.VFZLYEV.V8YEV.V$V&W 6W8X    &~X2\\<T   =v"
4213       " Ab K~\\(x$W8W MW4W CXNX ?X>X NW                $w   DX   $VBV#XFS :WFXEV H]   ;W #W 9XITIX"
4214       " +V       JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X  Je M~X Me 9Y >U?gMUCV7WBW IX>\\"
4215       " 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+"
4216       "_ :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"
4217       "[;X MX;[ M[ 3W )W ,W6W NW8W KWAVNVAW*XEX FW9X 4Y 3W :V  MW     ?cGc+Y?WNV MWD] +W DV=Z LX "
4218       "+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"
4219       "/X%X)Y  HV  IY ?V @Y3Y ?V   CV /YES 6X 1\\H[ JcJc LV 0WI\\   =TFV)S;WAX 5WEW     MTAVAWCS  3"
4220       "W 4~W.W  KV   ?W6W LgGU     KU 4WAW @WEW 6U9V GU9V ?VBV BX 2WBW AWBW AWBW AWBW AWBW AWBW A"
4221       "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"
4222       "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"
4223       "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&"
4224       "V 5X9W    %~X3]<T   >x A` J~\\(y%W8W MW4W CXMW >W>W MV                $x   DX   $VCV\"XFS 9X"
4225       "IXEV H_   <W #W ;YHTHY -V       JV 3X4X >X #Y ?g AVBX Do HXMk 3Y >l HX7Z MX -X  Me J~X Je "
4226       "=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/"
4227       "X$X:X LXBVEVBX+_ 9` +Y CW +V \"W       %W IZ9X NX .X9Z MW7W JW /X9Z MZ;X LW ,W ,WDY AW ,Z;["
4228       ";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"
4229       "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@"
4230       "Y-X0X(X@X@X/XImIX*Y  GV  HY @V AY1Y @V   CV /XDS 6X 0YDY JdLd LV 1WF[   >SFV'S<WBY 6XFX   "
4231       "  MS@VAVAS    @~W/W  JU   >W6W LgGU     KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX"
4232       " 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"
4233       "'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"
4234       "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"
4235       "V/V=X>V&V 4W:X    %~X2TNV<S   =y KWM^LW$~Z({&W7V MW4W CWLX ?X?W MV                 KX   ,X"
4236       "   %VBV!XGS 9gFV Ha   >W \"W ;WFTFW -V       JV 3X4X >X #Y ?f AWBX Dp IXNm 4X <j GX7Z MX -X"
4237       " !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<"
4238       "Z Ih 0X <X/X#X<X KXBVEVBX*] 8` ,Z CW +V \"W       %W IZ9X X -X9Z NX7X KW /X9Z MY9W LW ,W ,W"
4239       "EY @W ,Y:Z:W<Y9W MX8X LZ9X X9Z MY 1W )W ,W6W MW:W JWAVNVAW)XGX DW:W 4Y 3X :V  MW     @VHXK"
4240       "WGV,W<^ MWIa *W FW9Y NW *Y9W KW<X >` @W6W NW%W/WEWEW NW=W JWCWCW X8X!X8X =W >| GW@W 7Y AX "
4241       "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"
4242       "[ 7XFX G~X  .S@VBWAS    @~W0W .P>W   >W6W KfGU     KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@"
4243       "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"
4244       "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 "
4245       ",W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X  \"X=] LW6W MW6W MW6W MW6W MW:W IZ9X NW:W     NVLu"
4246       "KU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X    %~X2RLW>T   >{!z'~Z)}(W6W NW4W DXLX ?X@X MV          "
4247       "       KX   ,X   %VBV!YHS 8eEV Ic   ?W !W ;UETEU ,V       KW 3X4X >X $Y >c ?WAX DWD^ JbG] "
4248       "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"
4249       "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"
4250       "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 "
4251       ":V  MX     BUDVKVDU.X<] LWI_ :WEW FV7X NW *Y9W JV<X >a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X "
4252       "=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"
4253       " 1VB[   ASFV'S;YI] 9YGY F~X  .S@VDX@S    @~W1V ,TEZ   >W6W JeGU IX   +U 2YIY <YGY :U;V:W3U"
4254       ";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"
4255       "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"
4256       "W NW5W NW5W MW ,W ,W ,W -X7W LY9W MW6W MW6W MW6W MW6W MW6W  \"W=^ LW6W MW6W MW6W MW6W MW;X "
4257       "IY7W NW;X     NVLuKU/VLuKU/VA_@U/V;Y@U/V=X=U&V 4X<X    $~X,W>T   ?|\"}(~X)~(W6W NW4W DXKW >"
4258       "W@X MV                 KX   ,X   %VBV!ZIS 7cEV IYNZ8W  0W !W :RCTCR +V       KW 3X4X >X %Y"
4259       " =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"
4260       " 6X -XGVDVGX0XEXCX)X%X.X=[ NX%X.u Fl 6X <X/X\"W<W IWCWEVBW([ 5\\ ,Z AW +W #W       $V IY7X\"X"
4261       " -X7Y NW5W KW 0X7Y MX8X MW ,W ,WHZ >W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MX<X IWCVLVCW&"
4262       "XKX AW<W 5Y 1W 9V  LW  4P  /TBVMVBT.X;\\ LWI` =\\HW GW7X NW *X8X KV=X >XMW AW6W NW%W0XEWDW W"
4263       "=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"
4264       "BS 6X 1V<V KeNe LV 2V?Y   ASFV'S:dNV :XFY E~X  .S@i?S    @~W2i >h   =W6W JeGU IX   4g :g :"
4265       "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"
4266       "-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@"
4267       "X ,W5W NW5W NW5W NW5W MW ,W ,W ,W -X7X MX8X X6X X6X X6X X6X X6X  $X=_ MW6W MW6W MW6W MW6W "
4268       "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"
4269       " DWJX ?XAW L~^               $X   ,X   %VCV N\\LS 6aDVAW0XLZ9W  0W !W :PATAP +V       KV 2X"
4270       "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"
4271       "'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X <X/X\"X>X IXDVCVDX)[ 4\\ -Z @W *V #W       $"
4272       "W JX5W\"X -W5X W4W KW 0W5X MX7W MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HW"
4273       "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"
4274       "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   "
4275       "DX 2XBS 6X 2W<W =^ =V 2V>Y   BSFV'S9bMV ;XFY D~X  .S@h>S    @~W2i >g   <W6W HcGU IX   4g 9"
4276       "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"
4277       "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"
4278       "@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W  $W=VMW MW6W MW6W MW6W MW6W "
4279       "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"
4280       "W EXJX ?XBX L~^               $X   ,X   &VBV Mb 4]CVC]4XJZ:W  0W !W +T  KV       KV 2X4X >"
4281       "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 "
4282       "8X -XFVFVFX0XDXDX)X%X.u MX%X.r ?l :X <X/X\"X>X IXDVCVDX)\\ 4Z ,Y ?W *V #W       $W JX5W\"W ,W"
4283       "5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HWCVKUCW%XMX "
4284       "?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"
4285       "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 "
4286       "CV:V =^ =V 2V=Y   CSFV'S8`LV <XFX B~X  .S@e;S    @~W2i >e   :W6W GbGU IX   4g 8c 5XFX FgFV"
4287       ":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"
4288       "%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"
4289       "W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W  $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W "
4290       "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"
4291       "BX L~^               $X   ,X   &VBV Ld 4WAVD`6XHZ;W  0W !W +T  KV       LW 2X4X >X 'Y ;i G"
4292       "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"
4293       "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"
4294       " 0W5X MW6W MW ,W ,WKY :W ,W7W7W=W6W W4W MX5W\"W5X MX /Y ,W ,W6W LX>X GWEVJVEW#a >W>W 7Y 1Y "
4295       "8V  KY 9e8T  0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6W W#W2XDWDX!W=W JWCWCW!W4W#W4W"
4296       " >W >| IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-XImIX0Y  AV  BY FV GY%Y FV   DX 2WAS ?r DW:W =\\ <V "
4297       "2V;W   CSFV'S7]JV =XFX A~X  .S@d:S    (V Ii <a   8W6W FaGU IX   4g 6_ 2XFX GgGV:Z<gGVFUFY?"
4298       "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%",
4299 
4300       // Second string:
4301       "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"
4302       " NW ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W  $W?VKW MW6W MW6W MW6W MW6W KW>W GX5W MW>W     "
4303       "LVLuKU/VLuKU/V?\\?U/V?Y<U/V=X=U&V 2W>X     8X DWBT   ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^    "
4304       "           $X   ,X   &VBV Kg \"VEc8WFZ=W  /W !W +T 4~W      5V 1X4X >X (Y -] IW>X )Y M[9X 9"
4305       "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"
4306       "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 "
4307       "MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>W FWEVJVEW#a >W?X 8Z 4\\ 8V  K["
4308       " =i<V  0S=Y=S0X:[ KW@^ IfMW HW4W MY .W6W JW@W =XKX CW6W W#W2WCWCW!W=W JWCWCW\"X4X%X4X ?W >W"
4309       "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   "
4310       "DSFV'S4W /XFX @~X  .S@VIX;S    (V Ii 8Z   5W6W D_GU IX   4g 3Y .XFX HgGV;TNU<gGVFQ@W;Z=V;T"
4311       "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"
4312       "%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"
4313       " ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X     L"
4314       "VLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&V 2X?W     8X CWBT   ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^      "
4315       "         $X   ,X   'WCV Ii &VEe:XEZ>W  /W !W +T 4~W      5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y "
4316       "?Z@Z I]Gb    '^    =^$X 9U@V:WAU<X<X MX9Z\"X *X*X,X -X 0X6f+X/X'X -X -XM[ ;X -XEVHVEX0XBWEX"
4317       ")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"
4318       "6W MW ,W ,WMY 8W ,W7W7W=W6W!W2W NW3W$W3W MW -^ 2W ,W6W KX@X FWEVJVEW\"_ <W@W 7Y :b 7V  Jb F"
4319       "mAX  0S<W<S0W8Y JW<[ KYHVMV GV3X MZ 0W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W"
4320       "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   "
4321       "DSFV'S  <XFX  =V  .S@VGW<S    (V      \"W6W A\\GU IX       2XFX *V;TMU LV2V V;TMU4Z 2X<X IX<"
4322       "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@"
4323       "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"
4324       "X NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2W@VHW NW6W MW6W MW6W MW6W JW@W FW3W MW@W     KVLuKU/VLuKU/V"
4325       "A`AU/VAY:U/V=X=U&V 1W@X     9X BWBS   >~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^               $X  "
4326       " ,X   'VBV Gi (VFg;WCZ?W  /W !W +T 4~W      6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX    "
4327       "'[    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"
4328       " 0a >X <X/X XBX FXFVAVFX+b 6X /Z <W )W %W       =p JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,WNY 7"
4329       "W ,W7W7W=W6W!W2W NW3W$W3W MW -b 6W ,W6W JW@W EWFVHVFW!] ;WAX 8Y 9` 5V  H` HrG[  0S<W<S0W8Y"
4330       " JW:Y KXF^ HW2W Kc ;W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'"
4331       "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  "
4332       ".S@VFW=S    (V      \"W6W <WGU IX       1XFX +V;SLU LV2V V;SLU5Z 1W:W IW:W IW:W IW:W IW:W I"
4333       "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/"
4334       "X GX <X8X MWB] Bp Ep Ep Ep Ep Ep E~eBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
4335       "M~Y2WAWHW NW6W MW6W MW6W MW6W JWAX FW3W MWAX     KV<V=V/V#V/VBbCV/VBY:V/V=X>V&V 1XAW     9"
4336       "X @WDT   ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^               *r   ?V   &VBV Eh *VEXIX<XBZ@W  /W"
4337       " !W +T 4~W  5f   8V 0X4X >X +Y $Z NW<X 'X NZ7X ;X ?X:X HkMX    '[    7[%X 8UAV8VAU=X:X NX6"
4338       "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,"
4339       "c 6X /Y ;W (V %W       ?r JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W7W7W=W6W!W2W NW3W$W3W M"
4340       "W ,e :W ,W6W JW@W DWGVHVGW N[ 9WBW 8Y 8^ 3V  F^ I~X  0S;U;T1W8Y JW8X MXC\\ HW2W Ia ;W6W IWB"
4341       "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 "
4342       "GV H~a HV I~b HV   DX 3W@S ?r DV8V ;X   DW;V   DSFV'S  >XFX  ;V  .S@VFW=S    (V      \"W6W "
4343       ":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"
4344       " -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"
4345       "r Gr Gr G~gBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBWGW NW6W MW6W MW6W "
4346       "MW6W IWBW EW3W LWBW     IU<V=V.U#V.UCdDV.UCY9V.U=X>V&V 1XBX     :X ?WDT   ?~S,~[({ x&W4W W"
4347       "4W FWFX ?XFX JV                \"q   >V   &VBV Af -VEXGX=W@ZBW  .W !W +T 4~W  5f   8V 0X4X "
4348       ">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 -"
4349       "X -XJ[ >X -XDVJVDX0XAXGX)X%X.i AX%X.X>Z ,\\ ?X <X/X NWBW DWFVAVFW+XMY 7X 0Z ;W (V %W       "
4350       "@s JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW +g =W ,W6W JXBX DWGVH"
4351       "VGW N[ 9WBW 9Y 7^ 3V  F^ I[Gr  /S;U;T1W8X IW7X NWA[ HW2W F^ ;W6W HVCX >XGW DW6W!W<W<W3WCWC"
4352       "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 "
4353       "3W@S 6X 3V8V ;X   DX<V   DTFV)T  >WEW  :V  .TAVEW?T    (V      \"W6W :UGU IX       /WEW .V;"
4354       "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"
4355       "%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 ,|\"|\"|\"|"
4356       " NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBVFW NW6W MW6W MW6W MW6W IWBW EW3W LWBW   "
4357       "  IU<V=V.U#V.UDYMZEV.UDY8V.U#V&V 0WBX     ;X >WDS   >~T-~\\(y Mw&W4W W4W GXFX ?XFX JV      "
4358       "          #r   >V   'WCV <c .VEWEW=W?ZCW  .W !W   :~W  5f   9W 0X4X >X -Y  Y!W;X 'Y Y5X =X"
4359       " @Y8Y HgKX    'a    Ca%X 8UAV8VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WG"
4360       "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"
4361       "W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW )g ?W ,W6W IWBW CWGVHVGW MY 8WCX :Y 6` 5V  H"
4362       "` IW@m  -S;V<T1W8X IW7X W@[ HW2W Ia ;W6W HVCW >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W "
4363       ">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  "
4364       " CSFV)S  =WEW  :V  -SAVDW@S    'V      \"W6W :UGU IX       /WEW .V<TJU NV/U\"V<TJU8Z /W8W KW"
4365       "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"
4366       "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"
4367       "2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W LWCX     IV=V=V.V$V.VFYKZFV.VFY"
4368       "7V.V$V&V 0XCW     ;Y =WFT   >~T-~\\'w Ku%W4W W4W GXEW >WFW IV                #q   =V   6~X "
4369       "JSN^ /VEWCW?W=ZDW  .W !W   :~W  5f   9V /X4X >X .Y  MX\"W:X &X Y5X >Y @X6X FcJX    &d    Id"
4370       "%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 "
4371       "<X/X NXDX DXHV?VHX-YKY 8X 2Z 9W 'V &W       B]?W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W"
4372       "7W7W=W6W!W2W NW3W$W3W MW 'g AW ,W6W IWBW CWHVFVHW NZ 7WDW :Z 6a 6V  Jb IU;i  ,S;V<S0W7W IW"
4373       "6W W?Z HW2W Kc ;W6W HWEX >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =V2V KX8X BY ;X /Y)Y/"
4374       "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"
4375       "AVDW@S    'V      \"W6W :UGU      *m 5XFX /V;SIU V.T\"V;SIU9Z /X8X MX8X MX8X MX8X MX8X MX8X "
4376       "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"
4377       "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"
4378       "W\"W2W\"W2W M~Y2WDVDW NW6W MW6W MW6W MW6W HWDW DW3W KWDW     HV=V>V-V%V-VGYIZHV-VGY7V-V%V%V "
4379       "/WDX     ;X <WFT   >~T-~\\'v Is$W4W W4W GWDX ?XGW HV                %r   =V   6~X JSJ[ 0VEV"
4380       "AV?W<ZFW  -W !W   \"V   Lf   9V /X5X =X /Z  MX\"V9X &X NX5X >X ?X6X D`IX    $d    Ne#X 8UAV8"
4381       "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 "
4382       "CXHV?VHX-XIY 9X 3Z 8W 'V &W       CZ;W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,b 7W ,W7W7W=W6W!W"
4383       "2W NW3W$W3W MW %f BW ,W6W IXDX BWIVFVIW N\\ 8WEX :Y .[ 7V  K\\ BT8e  *S<X=S0W7V HW6X\"W=X GW2"
4384       "W Me ;W6W GVEX >WDW EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W KW6W CY :X .X)X.YAXAY,X0X\""
4385       "ZHXHZ(X'X/Y  AV  BY FV GY%Y FV   DX 3W@S 6X 2V:V L|  %ZAV   BSEV*S:m @XFX  <V  -SAVCWAS   "
4386       " 'V      \"W6W :UGU      *m 6XFX .V<TIU V/U\"V<TIU9Y .x Mx Mx Mx Mx Mx Mu NX +X -X -X -X -X "
4387       "-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"
4388       "Z;W KZ;W KZ;W KZ;W KZ;{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WEVCW NW6W "
4389       "MW6W MW6W MW6W HWEX DW3W KWEX     GV>V>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW     N~X'VGT   =~T-~\\"
4390       "&u Ir#W4W NV4W HXDX ?XHX HV                 KX   ,V   6~X JSHZ 2VDVAV?W;ZGW  -W !W   \"V   "
4391       "Lf   :W .X6X =X 0Z  LY#~ /X NX5X >X @X5Y AYFX    !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X"
4392       ")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"
4393       " 'W       CX9W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WNZ 8W ,W7W7W=W6W!W2W NW3W$W3W MW !c CW ,"
4394       "W6W HWDW AWIVFVIW N] 8WFW :Y *Y 8V  KY ?R3`  (S<X=S0W7V HW5W\"W=X GW2W N[ 0W6W GWFW >XDX FW"
4395       "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"
4396       " FY'Y EV   DX 3W@S 6X 2V:V L|  $[CV   BTFW,T:m ?XFX  =V  -TBVBVBT    'V      \"W6W :UGU    "
4397       "  *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'"
4398       "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 "
4399       ",W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WFVBW NW6W MW6W MW6W MW6W GWFW CW3"
4400       "W JWFW     FV>V?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX     N~X'WHT   =~T-~\\%s Gp\"W4W NV4V GXCW >WH"
4401       "X HW                 LX   ,V   6~X JSGY 3VDWAW@W:ZIW  ,W !W   \"V   Lf   :W .X6X =X 1Z  JX#"
4402       "~ /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"
4403       "VNVBX0X>WIX(X'X-X /X'X-X9X *Y AX <X/X MXFX BXJW?WJX.YGY :X 4Z 7W 'W 'W       DX8W JW3W$W ,"
4404       "W3W!W 'W 1W3W MW6W MW ,W ,WLY 9W ,W7W7W=W6W!W2W NW3W$W3W MW  K_ DW ,W6W HXFX AWIVFVIW ^ 8W"
4405       "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"
4406       "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"
4407       "<W L|  #\\FW   ASFW,S9m >XFX  >V  ,SBVBWCS    &V      \"W6W :UGU      *m 8XFX .V<TGU\"V.U#V<T"
4408       "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"
4409       "/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"
4410       "W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WGWBW NW6W MW6W MW6W MW6W GWFW CW3W JWFW     FW?V?V+W(V+WKXCY"
4411       "KV+WKX5V+W(V%W .WFX     N~X'WHT   =~T-~\\$q Eo\"W4W NV4V GWBW >XIW GW                 LX    "
4412       "   ;~X JSFX 3VDV?V@W9ZJW  +V \"W   !V       V -X6X =X 2Z  IX#~ /X NX5X ?X ?X4X .X     Jd D~"
4413       "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 "
4414       "AX <X/X LXHX AXJV=VJX.XEY ;X 5Z 6W &V 'W       DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WKY "
4415       ":W ,W7W7W=W6W!W2W NW3W$W3W MW  H\\ DW ,W6W GWFW @WJVDVJW!` 9WGX <Y &X 9V  LX =P   (T?\\@T0W8"
4416       "X IW5W\"W<W GW2W X ,W6W FVGW >XBW FW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LW4W FY 8X -X"
4417       "+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"
4418       "  ?V  ,TCVAVDT    &V      \"W6W :UGU      *m 9XFX -V<SFU\"V/U\"V<SFU;X ,z z z z z z v NY ,X -"
4419       "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"
4420       "7W LW7W LW7W LW7W LW7W LW7W LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
4421       " &WHWAW NW6W MW6W MW6W MW6W GWGX CW3W JWGX     EV?V@W*V)W*VJVAWKW*VJV5W*V)W%W .XGW     M~X"
4422       "&WJT   <~S,kNn#o Cm!W4W NV4V HXBX ?XJX FW                 MY       <~X JSEX 5VCV?V@W8ZLW  "
4423       "*W #W   !V       V -X6X =X 3Z  HX#~ /X NX5X @Y ?X4X /X     Ge G~X Ge GX 8UAV9WCU>|\"X3X$X ,"
4424       "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"
4425       " 6Z 5W &V 'W       DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WJY ;W ,W7W7W=W6W!W2W NW3W$W3W M"
4426       "W  EZ EW ,W6W GWFW ?WKVDVKW!b 9WHW <Y $W 9V  LW     BTAVNUAT/W8X IW5W#W;V FW2W!X +W6W FWIX"
4427       " >XBX GW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W MX4X HY 7X -Y-Y+ZDXDZ*X0X Mt#X)X*Y  EV  "
4428       "FY NSGVGS Y-Y MQFVFQ   X 3W@S 6X 1W>W 9X   =\\KW   >SEW<PCS  6XFX  @V  +SCVAWES    %V      "
4429       "\"W6W :UGU        &XFX -V<TFU#V/U\"V<TFU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X,X+X="
4430       "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"
4431       " LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WIW@W NW6W MW6W MW6W MW6W"
4432       " FWHW BW3W IWHW     DW@VAW)W+W)WJT?UKW)WJT5W)W+W$W -WHX     M~X&WJT   ;eMQMe+jNQNj!m Bl W4"
4433       "W NW6W HXBX >WJX FW                 LX       <~X JSEX 6WCV?V@W7ZMW  *W #W   !V      !W -X6"
4434       "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"
4435       " -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX <Y1Y KWHW ?WJV=VJW/YCY <X 7Z 4W &W (W       EW6"
4436       "W JX5X$X -X5X!X (W 0W5X MW6W MW ,W ,WIY <W ,W7W7W=W6W!X4X NX5X$X5X MW  CX EW ,W6W GXHX ?WK"
4437       "VDVKW!XNY :WIX =Y #X :V  MX     BUCVMVBT/W9Y IW5W#W<W FW3X!W *W6W EVIX ?X@W GW6W!W=Y=W3XDW"
4438       "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"
4439       "  !X 3XAS 6X 0W@W 8X   ;\\NW   =TEX@RDT  5XFY  BV  +TDV@WGT    %V      \"W6W :UGU        (YF"
4440       "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-"
4441       "X*Y MZGZ\"XLW5Y,Y1Y'Y1Y'Y1Y'Y1Y GX <r GW4X$W6W MW6W MW6W MW6W MW6W MW6W MW6X NX -X -X -X -X"
4442       " *W ,W ,W ,W /W2W NW6W!X4X\"X4X\"X4X\"X4X\"X4X  &WIV@X NW6W MW6W MW6W MW6W FWIX BX5X IWIX     "
4443       "CWAVAW(W,W(WJR=SJW(WJR4W(W,W$W -XIX     M~X&WJS   :dLQLd+iMQNj!l @j NW4W NW6W HW@W >WJW DW"
4444       "                 MX       .VCV :SDW 6VBV?V@W6b  )W #W   !V      !V +X8X <X 5Z  FX#~ /X MW5"
4445       "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"
4446       "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"
4447       "W5X MW6W MW ,W ,WHY =W ,W7W7W=W6W W4W MX5W\"W5X MW  BX FW ,W6W FWHW >WKVDVKW\"XLX 9WJW =Z #X"
4448       " :V  MX     AUEVKVDU/X:Y IW5W#W<W EW4W!X *W6W EVJX >X@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4"
4449       "W >W <W6W LW2W IY 6X ,Y/Y(ZFXFZ(X0X Kp!Y+X'Y  GV  HY NWIVIW Y1Y MUHVHU  \"X 2WAS 6X 0YDY 8X"
4450       "   :c   <TE[FUDS  3XFY  CV  *SDV@WGS    $V      \"W6W :UGU        )YFX ,V=TDU$V0V\"V=TDU=X +"
4451       "|\"|\"|\"|\"|\"|#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"
4452       "%X1X%X1X FX <p EW4X$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W"
4453       " W4W W4W W4W W4W W4W  $WKV?W MW6W MW6W MW6W MW6W EWJW AX5W GWJW     BXBVBW'X.W'XJP;QJW'XJP"
4454       "4W'X.W#V ,XIW     L~X%WLT   :dLQLc*iMQMi k ?i NW4W NW6W IX@X ?XLX DW                 MY   "
4455       "    0VBV :SDW 7VAV?V@X6a  )W #W   !V      !V +X8X <X 6Z  EX#~ 0Y MW5X AY >X4X 0X     =d ~X"
4456       " 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 "
4457       ";X1X JXJX ?XLV;VLX0YAY =X 8Z 3W %V (W       EW7X JX5W\"W ,W5X W (W 0W5X MW6W MW ,W ,WGY >W "
4458       ",W7W7W=W6W W4W MX5W\"W5X MW  BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V  MW     @VHXJWHV-W:"
4459       "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"
4460       "1Y'[GXH\\(X0X Jn NX+X&Y  HV  IY NYJVJY Y3Y MWIVIW  #X 2WAS 6X 0[H[ 8X :V %`   :TEiET  2YGY "
4461       " DV  *TEV?WIT    $V      \"W6W :UGU        *YGY ,V<SCU%V0V\"V<SCU=X ,X2X$X2X$X2X$X2X$X2X$X2X"
4462       "$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"
4463       " <m BW3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W W4W W4W W4"
4464       "W W4W W4W 5Z IWLV>W MW7X MW7X MW7X MW7X EWJW AX5W GWJW     AXCVCW%X0W%X0W%X0W%X0W\"V +WJX  "
4465       "   ?X 2WLT   9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW                 MX       0VBV :SDW "
4466       "7VAV?V@X5_  (W #W   !V      \"W +X8X <X 7Z  DX 5X 'X LX7X @X =X4X 0X     ;e   Le   JUAW<XFV"
4467       "=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"
4468       "V;VLX1Y?Y >X 9Z 2W %W )W       EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4"
4469       "W MX5W\"W5X MW  AW FW ,W7X FXJX =WMVBVMW#YJY ;WKX >Y  W :V  MW     ?dId,W;Z IW5W#W=W DW4W!W"
4470       " )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L"
4471       "Y-Y%Y  IV  JY LYKVKY MY5Y MYJVJY  $X 2XBS 6X 2q 9X :V #\\   7TDgFT  /XFX  EV  )TFV>VJT    #"
4472       "V      \"W6W :UGU        +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W$X1W$X1W$X2X%X7X LY .X -X -X -"
4473       "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"
4474       "W7X MW7X MW7X MW7X MW7Z NX -X -X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W"
4475       " MW7X MW7X MW7X MW7X EWKX AX5W GWKX     @XDVDX$X2X$X2X$X2X$X2X\"V +XKW     ?X 1WMT   7`JQKa"
4476       "'fLQLf Kg <f LW4W MW8W HW>W >WLW BX                 NY       1VBV :SDW 8V@V?V?W4]  &V $W  "
4477       "  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"
4478       " ,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 $"
4479       "V )W       EW8Y JY7X\"X -X7Y X )W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW  AW FW"
4480       " ,X8X EWJW <WMVBVMW#XHX :WLW >Y  NW :V  MW     >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7"
4481       "X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y  JV  KY JYLVL"
4482       "Y KY7Y KYKVKY  #X 2XBS 6X 3t ;X :V ![   8TCfFT  .XFX  FV  )UGV>WKT            MW7X :UGU   "
4483       "     ,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)"
4484       "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 "
4485       "MW8[ NX -X -X -X -X +W ,W ,W ,W .X6X MW6W X6X X6X X6X X6X X6X 5Z I_=X MX8X MX8X MX8X MX8X "
4486       "DWLW @Y7X FWLW     >XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX     @X /WNT   7`JQJ_&eKQKe Je :d KW4W MW8"
4487       "W HW>X ?XNX AX                 Y       1VCV 9SDW 9V?V?V?X4\\  &W %W    V      \"V )X:X ;X 9Z"
4488       "  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"
4489       "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 "
4490       "W ,W7Y NX *W /W8Z MW6W MW ,W ,WDY AW ,W7W7W=W6W NW6W LY7W W7Y MW  AW FW ,X9Y EWJW <WMVBVMW"
4491       "$XFX ;WMX ?Y  MW :V  MW     =`Ea+X<[ JW6W\"W>W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W "
4492       "IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X  JV  KX HYMVMY IX7X IYLVLY  \"X 1XCS"
4493       " 6X 4v <X :V  [   8TBbET  ,WEW  FV  (T$T            LX8X :UGU        ,WEW )V=m,V3W V=mCX -"
4494       "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"
4495       "%]3Y(X3X#X3X#X3X#X3X EX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y MW8[ MW ,X -X -X -X ,W ,W ,W "
4496       ",W -W6W LW6W NW6W MW6W MW6W MW6W MW6W 4Z H^=W LX9Y MX9Y MX9Y MX9Y DWMX @Y7W EWMX     =Y8Y "
4497       "Y8Y Y8Y Y8Y Y8Y V *WLX     AX .WNT   6^IQI]$cKRJc Id 8c KW4W MX:X IX>X ?XNX AY            "
4498       "     Y4P       VBV 9SDW 9V?V?V?Y4Z  %W %W    V      #W )X:X ;X :Z  CY 4X (Y KX9Y AX ;X6X 1"
4499       "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"
4500       ")X5Y /X @X :X4Y GXNX <XNV9VNX2Y;Y @X ;Y /W $W *W       EW9Z JZ9X X -X9Z NX *W /X9Z MW6W MW"
4501       " ,W ,WCY BW ,W7W7W=W6W NX8X LZ9X X9Z MW  AW FW +W9Y EXLX <WNV@VNW%YEX ;WNW ?Y  LW :V  MW  "
4502       "   <^C_)W=\\ JX7W\"W>W BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW NW=W IWDWEX!Z8X!X8X =W :W:W LX"
4503       "0X Y 2X (Y7Y Nv#X0X ?X AY1Y V  IV  JV FYNVNY GV5V GYMVMY  !X 1XCS 6X 5x =X :V  MZ   8T?ZBT"
4504       "  *VDV  FV  'T&T            KX8X :UGU        ,VDV )V<m-V3V NV<mCX -X/W&X/W&X/W&X/W&X/W&X0X"
4505       "'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"
4506       " <X -W3W$W9Z MW9Z MW9Z MW9Z MW9Z MW9Z MW9] NX -X -X -X -X ,W ,W ,W ,W -X8X LW6W NX8X MX8X "
4507       "MX8X MX8X MX8X 4Z H]=X KW9Y LW9Y LW9Y LW9Y CWNW ?Z9X DWNW     ;Y;Z MY;Z MY;Z MY;Z MY;Z NV "
4508       "*XMW     AY -[   3ZHRH[\"aJRI` Fb 6a JW4W LW:W HX=W >WNX @Y                !Z6Q       VBV K"
4509       "P>SEW 9V>WAW>X3Z  &W %W    V      #V 'X<X :X ;Z  BY 4X )Y IW9X AY ;Y8Y 2Y     .d  1d   >U?"
4510       "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"
4511       " <XNV9VNX2X9Y AX <Z /W #V *W       EX:Z JZ9X NX .X9Z MX +W .X;[ MW6W MW ,W ,WBY CW ,W7W7W="
4512       "W6W NX9Y LZ9X X9Z MW  AW FW +W:Z DWLW :^@^$XDY <WNW @Z  LW :V  MW     ;\\@['X>\\ JX8X\"W?W AX"
4513       "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"
4514       " @Y3Y MT  HV  IT Dj ET3T EYNVNY   X 0XDS 6X 6ZM`LY >X :V  LY   7T)T  (UCU     ET(T        "
4515       "    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 -"
4516       "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"
4517       "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"
4518       "W:Z LW:Z LW:Z LW:Z CWNW ?Z9X DWNW     :[@[ K[@[ K[@[ K[@[ K[@[ MV )WNX     AX ,[   1WGRFW "
4519       "N_IRH^ Da 5_ IW4W LX<X HW<W >` >Y                !Y8S   MX   +VBV KQ?SFX 9V=VAV=Y6]  &V &W"
4520       "    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"
4521       "Z1T&X4\\&X -X *Z0X+X/X'X -X -X:[ NX -X&X0X9a#Z5Z(X *Z5Z(X4Y%R/Y @X 9Y7Y EWNW :WNV9VNW2Y9Y A"
4522       "X =Z .W #V *W       EX;[ J[;X MY .X;[ MY2P JW .Y=\\ MW6W MW ,W ,WAY DW ,W7W7W=W6W MX:X K[;X"
4523       " MX;[ MW /P4X FX ,X<[ DXNX :^@^%XBX <` @Y  KW :V  MW     8V;W%X?^ KY9X!V@X @X:X NX *W6W C_"
4524       " >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 "
4525       " HR Bh CR1R Cj   NX 0YES 6X 7ZJ\\IY ?X :V  KY   8U+U  'TBT     DU+T            IY;Z :UGU   "
4526       "     ,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\\'"
4527       "X9a#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z\"Z7Z&Z5Z%Y7Y!Y7Y!Y7Y!Y7Y DX <X -W4X$X;[ MX;[ MX;[ MX;[ MX;[ MX;[ MX"
4528       ";`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<"
4529       "[ LX<[ LX<[ C` ?[;X C`     9_J_ I_J_ I_J_ I_J_ I_J_ LV )`     AX +Z    S <[GRFZ A_ 4^ HW4W"
4530       " KX>X HX<X ?` =Z                \"Y:T   MX   +VCV JSASFX :V<VAV<Y8_  'W 'W    NV BX   1X 2W"
4531       " &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 )"
4532       "[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"
4533       "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"
4534       "=[ CWNW 9^@^&X@X <^ @Y  JW :V  MW       HXA` LZ;X V@W ?Y<Y MX +W6W B^ ?X9W JZ<Z NXB]BX.YHW"
4535       "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 "
4536       "/YFSDP BX 8ZFVEY @X :V  JX   7V.U  %SAS     CU.U            HZ<Z :UGU        ,SAS (V:m/V5W"
4537       " 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"
4538       "Z#Z7Z\"Z5Z&[8Z$Z9Z!Z9Z!Z9Z!Z9Z DX <X -W4W\"X=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=b6R<Y7P GY5R KY5R"
4539       " 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^ >"
4540       "\\=Y B^     7r Gr Gr Gr Gr KV (_     BX )Y    S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[          "
4541       "      $[=U   MX   ,VBV JUCSHY :V;WCW<[<b  (W 'W    NV BX   1X 2W &Y@Y 9X >Z 0R5Z 2X GT9[ G"
4542       "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"
4543       "8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W       DY?] J]?Y KZ:R GY?] LZ8T JW"
4544       " -ZA^ MW6W MW ,W ,W?Y FW ,W7W7W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY  IW"
4545       " :V  MW       HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY"
4546       ">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[   ?V   6d   >f   LX /[HSFR BX 9Z3Y AX :V  IX   7"
4547       "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"
4548       "*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ"
4549       ";Z CX <X -WJP;X\"Y?] LY?] LY?] LY?] LY?] LY?] LY?XNZ9T<Z:R GZ8T KZ8T KZ8T KZ8T LW ,W ,W ,W "
4550       "+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 "
4551       "Do IV (_     CX (Y    S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\                %[@W   MX   ,VBV JXFS"
4552       "IZ :V:WEW:\\@e  (V 'V    MV BX   1X 2V $ZDZ 8X ?Z /U;] 2X GV=\\ EZC[ @X 7[@[ JT?[ EX -X  /Y "
4553       " 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["
4554       "?[ 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 "
4555       ",W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY  HW :V  MW       GZFYNY N]AZ N"
4556       "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"
4557       "Z >_ FX0X ?X =\\?\\   >V   5b   <d   KX .\\JSHT BX 8X2X @X :V  IX   5V4U   Q?Q     AV4V      "
4558       "      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"
4559       " -X -X -X -X ,X=b$X7_ \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\ X1X(`?\\ [?[ L[?[ L[?[ L[?[ BX <X -WJS@Z\"ZB_ "
4560       "LZB_ LZB_ LZB_ LZB_ LZB_ LZBYM\\>W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ "
4561       "GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\     3l Al Al Al Al HV (^     BX (X  "
4562       "  NS (S ,Z .Y FW4W In GX:X ?^ 9_                (]FZ   MX   ,VBV J[ISL\\ :V9XGX9^Fi  )W )W "
4563       "   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"
4564       "_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 "
4565       "\"W ,W       C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX"
4566       "G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'X<X =\\ AX  GW :V  MW       G\\IYM^$`F\\ MWEX ;]H] J]BV E"
4567       "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 "
4568       "<^E^   =V   4`   :b   JX -^MSLX Lz V0V ?X :V  HW   4V7V   MP>P     @W8W    3~W      :_GaKP"
4569       " @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 -"
4570       "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"
4571       "\\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"
4572       "] 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 ']"
4573       "     AV &W    T )T +X -X EW4W Hl FX9W ?^ 8~R                Jp   MX   ,VCV It 9V8XIX7sLZ  "
4574       "*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 "
4575       "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"
4576       "WMk Fo EkMW Is JW *jMW MW6W MW ,W ,W<Y IW ,W7W7W=W6W Jp HWMk GkMW MW /q Ae 9kMW B^ 7\\=[(Y;"
4577       "X >\\ Av 6W :V  MW       FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#hKh)s JW<] Lu LWNm Hp 6` Bl K~"
4578       "W'x MX 1iEi HX CX0X ?X ;u   <V   3^   8`   IX ,o Lz NT.T >X :V  HW   3X=X        )X<X    2"
4579       "~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 "
4580       "-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"
4581       "Is9o Ds Hs Hs Hs LW ,W ,W ,W )p HW6W Jp Ep Ep Ep Ep   Ls EkMW JkMW JkMW JkMW A\\ =WMk >\\   "
4582       "  /c 8c 8c 8c 8c CV '\\     ?T %W    U *T *W ,V DW4W Gj EW8W >\\ 5~P                In   LX "
4583       "  -VBV Is 9V7g6qJZ  *V )V    LV BX   1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X  &P  1P  LX"
4584       " 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"
4585       " !V ,W       BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p"
4586       " ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V  MW       EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<"
4587       "] 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   "
4588       "1ZEZ        %ZDZ    0~W      :WNfM\\ @UGU          !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-"
4589       "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"
4590       "jLW JjLW JjLW JjLW JjLW JmHr8n Cr Gr Gr Gr LW ,W ,W ,W (n GW6W In Cn Cn Cn Cn   Ls CiLW Ii"
4591       "LW IiLW IiLW @Z <WMj <Z     +] 2] 2] 2] 2] @V &[     >R $V    NU *U *U *U DW4W Fh DW8X ?\\ "
4592       "4~                Hl   KX   -VBV Hp 8V5e4nGZ  +W +W    LV BX   1X 3V  j Ct Mx Mr -X Gq =j "
4593       ">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 "
4594       "4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W       AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W"
4595       "=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5Z<Z(X8X >Z @v 6W :V  MW       DgI\\$s He 5l Dn EW6W @Y "
4596       ">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q   :V   1Z   4\\   GX *m"
4597       " Lz LP*P <X :V  HW   0m        \"l    .~W      :WMeM\\ @UGU          !V%U,V6i/V%U8l JX(X.X(X"
4598       ".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 "
4599       ">X <X -WJj KhKW IhKW IhKW IhKW IhKW IhKW IjEq7m Bq Fq Fq Fq LW ,W ,W ,W &j EW6W Hl Al Al A"
4600       "l Al   Ls AgKW HgKW HgKW HgKW @Z <WLh ;Z               MV &[     =P \"U    V +V )S (S CW4W "
4601       "De DX8X ?\\ 2|                Fh   IX   -VBV Ek 6V4c1kEZ  +V +V    KV BW   0X 4W  Mf At Mx "
4602       "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"
4603       " !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 "
4604       ",W9Y LW ,W7W7W=W6W Fh DWJe AeJW MW .m ;b 6eJW A\\ 5Z<Z)X6X >X ?v 6W :V  MW       CeG[$r Fc "
4605       "2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m   8V   0"
4606       "X   2Z   FX (j Kz   AX :V  HW   -g         Lh    ,~W      :WMdL\\ @UGU          \"V$U-V5i0V$"
4607       "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 "
4608       "Bm Bm Bm =X <X -WJh HfJW HfJW HfJW HfJW HfJW HfJW HhBn4j ?n Cn Cn Cn KW ,W ,W ,W %h DW6W F"
4609       "h =h =h =h =h   KVMi >eJW GeJW GeJW GeJW ?X ;WJe 9X               MW &Z       =U    W ,W *"
4610       "R &Q BW4W B` AW6W >[ /y                Dd   GX   -VCV Af 5V2a.gBZ  ,W -W    KV CX   0X 4V "
4611       " 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"
4612       "&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"
4613       "W MW6W MW ,W ,W8Y MW ,W7W7W=W6W Ef CWIb =bIW MW +h 8a 5cIW @Z 4Y:Y*Y5X ?X ?v 6W :V  MW    "
4614       "   AbDY$WMf Ca 0f >k EW6W @Y ?W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0"
4615       "X ?X 5i   6V   /V   0X   EX &f Iz   AX :V /P;W   *c         Gb    )~W      :WK`I[ @UGU    "
4616       "      #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 "
4617       ">i >i >i  $VEi @i >i >i >i ;X <X -WIf EcIW FcIW FcIW FcIW FcIW FcIW Fd>i0g ;i >i >i >i HW "
4618       ",W ,W ,W #d BW6W Ef ;f ;f ;f ;f   JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X               MW %Y "
4619       "      =T    X -X )P %P AW4W ?Z >W6X ?Z ,w                B`   EX   .VBV <] 1V0]*b?[  -W -W"
4620       "    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 "
4621       "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"
4622       "^ 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 "
4623       "@X ?v 6W :V  MW       ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ "
4624       "F~[)x MX 1iEi HX CX0X ?X 2c   3V   .T   .V   DX $b Gz   AX :V /R>X   &[         ?Z    %~W "
4625       "     :WJ^GY ?UGU          #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X "
4626       "-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*"
4627       "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    "
4628       "           MW         7S                   @r                >Y         BS .V,W#Z   ;V -V "
4629       "    7W     ;W  EX     ;\\   6] +Z   5\\ 5Z   <W         7X     %\\       <]    \"X         ([ "
4630       "  4c   E]   /[          (W  W .W       :Y #X 0Z 2X *\\   $W    &W         .Z =WDX 3XDW   I["
4631       "   0Y       8W   -W :V  MW       <Z ;WH[ 9Y &Z 1]  LW ?W   >WGXBU FX=X E` \"W >] @WDY 3Z   "
4632       "2X               C[           >T     :[       KV /TAY                          EWGXBU =UGU"
4633       "   BT       6V +V +V ,Y               ?\\                    +[ 0[ 0[ 0[ 0[   KT=[ 2[ 0[ 0["
4634       " 0[     7Z ;Y .Y .Y .Y .Y .Y -Y2\\\"Z /\\ 1\\ 1\\ 1\\         CZ   3Z /Z /Z /Z /Z   FVCZ 1Y .Y ."
4635       "Y .Y ,W :WDX 2W               LW         7R                                             #S"
4636       "       >W /W     8W     :V                      \"W         5X                  )X         "
4637       "    &Z                  CW  NV .W                   :W    %W           @W  :W             "
4638       " -X   -W :V  MW         LW        FW ?W   >W    NW   0W =W                                "
4639       "      3S       GV /XGZ                          DW  HUGU   AT                            %"
4640       "T                               'R                             JT                         "
4641       "      #T         (X :W  NX               LW                                               "
4642       "        7S       =V /V     7W     :V                      \"W         4X'Q                 "
4643       "&Y             %Z                  DW  NV .W                   :W    %W           @W  :W  "
4644       "            -W   ,W :V  MW         LW        FW ?W   >W    NW   0W =W                     "
4645       "                 3S       GV /j                          CW  HUGU   @T                    "
4646       "        %T                               'P                             HT                "
4647       "               \"Q         'W 9W  NW               KW                                      "
4648       "                 7S       =W 1W     7V     :W                      \"V         2X)R        "
4649       "         &X             #Z                  EW  NW /W                   :W    %W          "
4650       " @W  :W              -W   ,X ;V  NX         LW        FW ?W   >W    NW   0W =W            "
4651       "                          3S       GV /j                          CW  HUGU   @U           "
4652       "                 &U                                                             U         "
4653       "                      \"P         'W 9W  NW               KV                               "
4654       "                        6S       <V 1V     6V     :V                      !V         1Y-U "
4655       "                'X             \"Z                  FW  MV /W                   ;X    %W   "
4656       "        @W  :W              .X   +W ;V  NW         KW        FW ?W   >W    NW   0W =W     "
4657       "                                 3S       GV /h                          AW  HUGU   ?T    "
4658       "                        %T                                                             NT "
4659       "                                        )X 9W  X               KV                         "
4660       "                              6S       <W 3V     6V     9V                      \"V        "
4661       " /Z1X                 (X             !Z                  Ga (V 9a                   ;W    "
4662       "$W           @W  :W              .W   *W ;V  NW         KW        FW ?W   >W    NW   0W =W"
4663       "                                      3S       GV .f                          @W  HUGU   ?"
4664       "U                            &U                                                           "
4665       "  U                                         *W 8W  W               JV                     "
4666       "                                  6S       ;V 3V     6V     :W                      \"V    "
4667       "     .[5[                 *Y              Z                  Ha (W :a                   <X"
4668       "    $W           @W  :W              /X   *X <V  X         KW        FW ?W   >W    NW   0W"
4669       " =W                                      3S       GV +a                          >W  HUGU "
4670       "  >T                            %T                                                        "
4671       "     NT                                         +X 8W !X              (VIV                "
4672       "                                       6S       :V 5V     5U     9W                      \""
4673       "U         +\\;]                 )X              MZ                  Ia (W :a               "
4674       "    =Y    %W           ?W  :W              /W   )[ ?V #[         KW        FW ?W   >W    N"
4675       "W   0W =W                                      3S       GV 'Z                          ;W "
4676       " HUGU   >U                            &U                                                  "
4677       "           U                                         ,W 7W !W              'VIV           "
4678       "                                            6S       :V 6W     6V                         "
4679       "   4V         *_C`                 )Y              LZ                  Ja   :a            "
4680       "      (P7Y    $W           ?W  :W              0X   (b GV +b         JW        FW ?W   >W "
4681       "   NW   0W =W                                      3S       GV                            "
4682       "7W  HUGU   >U                            &U                                               "
4683       "              U                                         -X 7W \"X              'VJW        "
4684       "                                               6S       9V 7V     5U                      "
4685       "      3U         'x                 (Z              KZ                  Ka   :a           "
4686       "       (R:Z    $W           ?W  :W              0X   (b GV +b         JW        FW ?W   >W"
4687       "    NW   0W =W                                      3S       GV                           "
4688       " 7W     #U                            &U                                                  "
4689       "           U                                         -X 7W \"X              &UJW           "
4690       "                                            6S       9W 9W                                "
4691       "            Bu                 ([              IZ                  La   :a                "
4692       "  (T>[    $X           ?W  :W              1X   &a GV +a         IW        FW ?W   >W    N"
4693       "W   0W =W                                      3S       GV                            7W  "
4694       "   $V                            'V                                                       "
4695       "     !V                                         .X 6W #X              %VLW                "
4696       "                                       5S                                                 "
4697       "    2p                 -a                                                       8XE]    %Y"
4698       "           >W  :W              3Z   $_ GV +_         GW        FW ?W   >W    NW   0W =W   "
4699       "                                   3S       GV                            7W     /QGW     "
4700       "                       2QGW                                                            ,QG"
4701       "W                                         0Z 6W %Z              %a                        "
4702       "                               5S                                                     0l  "
4703       "               +a                                                       8p    +_          "
4704       " >W  :W              ;a   !] GV +]         EW        FW ?W   >W    NW   0W =W             "
4705       "                         3S       GV                            7W     /`                 "
4706       "           1`                                                            +`               "
4707       "                          7a 5W -a              #`                                        "
4708       "                                                                     >e                 '`"
4709       "                                                       7o    *^           =W  :W          "
4710       "    ;`    KY GV +Y         AW        FW ?W   >W    NW   0W =W                             "
4711       "         3S       GV                            7W     /`                            1`   "
4712       "                                                         +`                               "
4713       "          7` 4W -`              \"_                                                        "
4714       "                                                     8\\                 #_                "
4715       "                       \"}              3n    )^           =W  :W              ;`     9V   "
4716       "        BW        FW ?W   >W    NW   0W =W                                             'V "
4717       "                           7W     /_                            0_                        "
4718       "                                    *_                                         6` 4W -`   "
4719       "           !]                                                                             "
4720       "                                                  -]                                      "
4721       "  }              3l    ']           <W  :W              ;_     8V           BW        FW ?"
4722       "W   >W    NW   0W =W                                             'V                       "
4723       "     7W     /^                            /^                                              "
4724       "              )^                                         5_ 3W -_               N[        "
4725       "                                                                                          "
4726       "                             ,[                                        M}              2j "
4727       "   &\\           ;W  :W              ;^     7V           BW        FW ?W   >W    NW   0W =W"
4728       "                                                                          7W     -Y       "
4729       "                     *Y                                                            $Y     "
4730       "                                    2^ 2W -^               LX                             "
4731       "                                                                                          "
4732       "        *X                                        J}              /d    #Z           9W  :"
4733       "W              ;\\     5V           BW        FW ?W   >W    NW   0W =W                     "
4734       "                                                     7W                                   "
4735       "                                                                                          "
4736       "            /\\ 0W                 HT                                                      "
4737       "                                                                                          "
4738       "                        I}              *[     NW           6W  :W              ;Z     3V "
4739       "          BW        FW ?W   >W    NW   0W =W                                              "
4740       "                            7W                                                            "
4741       "                                                                             /Z .W        "
4742       "                                                                                          "
4743       "                                                                                       =} "
4744       "                                                                                          "
4745       "                                                                                          "
4746       "                                                                                          "
4747       "                                    D" };
4748 
4749     // Define a 40x38 'danger' color logo (used by cimg::dialog()).
4750     static const unsigned char logo40x38[4576] = {
4751       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,
4752       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,
4753       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,
4754       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,
4755       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,
4756       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,
4757       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,
4758       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,
4759       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,
4760       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,
4761       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,
4762       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,
4763       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,
4764       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,
4765       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,
4766       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,
4767       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,
4768       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,
4769       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,
4770       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,
4771       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,
4772       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,
4773       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,
4774       123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
4775 
4776     //! Get/set default output stream for the \CImg library messages.
4777     /**
4778        \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
4779        \return Currently used output stream.
4780     **/
4781     inline std::FILE* output(std::FILE *file) {
4782       cimg::mutex(1);
4783       static std::FILE *res = cimg::_stderr();
4784       if (file) res = file;
4785       cimg::mutex(1,0);
4786       return res;
4787     }
4788 
4789     // Return number of available CPU cores.
4790     inline unsigned int nb_cpus() {
4791       unsigned int res = 1;
4792 #if cimg_OS==2
4793       SYSTEM_INFO sysinfo;
4794       GetSystemInfo(&sysinfo);
4795       res = (unsigned int)sysinfo.dwNumberOfProcessors;
4796 #elif cimg_OS == 1
4797       res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
4798 #endif
4799       return res?res:1U;
4800     }
4801 
4802     // Lock/unlock mutex for CImg multi-thread programming.
4803     inline int mutex(const unsigned int n, const int lock_mode) {
4804       switch (lock_mode) {
4805       case 0 : cimg::Mutex_attr().unlock(n); return 0;
4806       case 1 : cimg::Mutex_attr().lock(n); return 0;
4807       default : return cimg::Mutex_attr().trylock(n);
4808       }
4809     }
4810 
4811     //! Display a warning message on the default output stream.
4812     /**
4813        \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
4814        \note If configuration macro \c cimg_strict_warnings is set, this function throws a
4815        \c CImgWarningException instead.
4816        \warning As the first argument is a format string, it is highly recommended to write
4817        \code
4818        cimg::warn("%s",warning_message);
4819        \endcode
4820        instead of
4821        \code
4822        cimg::warn(warning_message);
4823        \endcode
4824        if \c warning_message can be arbitrary, to prevent nasty memory access.
4825     **/
4826     inline void warn(const char *const format, ...) {
4827       if (cimg::exception_mode()>=1) {
4828         char *const message = new char[16384];
4829         std::va_list ap;
4830         va_start(ap,format);
4831         cimg_vsnprintf(message,16384,format,ap);
4832         va_end(ap);
4833 #ifdef cimg_strict_warnings
4834         throw CImgWarningException(message);
4835 #else
4836         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
4837 #endif
4838         delete[] message;
4839       }
4840     }
4841 
4842     // Execute an external system command.
4843     /**
4844        \param command C-string containing the command line to execute.
4845        \param module_name Module name.
4846        \return Status value of the executed command, whose meaning is OS-dependent.
4847        \note This function is similar to <tt>std::system()</tt>
4848        but it does not open an extra console windows
4849        on Windows-based systems.
4850     **/
4851     inline int system(const char *const command, const char *const module_name=0) {
4852       cimg::unused(module_name);
4853 #ifdef cimg_no_system_calls
4854       return -1;
4855 #else
4856 #if cimg_OS==1
4857       const unsigned int l = (unsigned int)std::strlen(command);
4858       if (l) {
4859         char *const ncommand = new char[l + 24];
4860         std::strncpy(ncommand,command,l);
4861         std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent.
4862         const int out_val = std::system(ncommand);
4863         delete[] ncommand;
4864         return out_val;
4865       } else return -1;
4866 #elif cimg_OS==2
4867       PROCESS_INFORMATION pi;
4868       STARTUPINFO si;
4869       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
4870       std::memset(&si,0,sizeof(STARTUPINFO));
4871       GetStartupInfo(&si);
4872       si.cb = sizeof(si);
4873       si.wShowWindow = SW_HIDE;
4874       si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
4875       const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
4876       if (res) {
4877         WaitForSingleObject(pi.hProcess,INFINITE);
4878         CloseHandle(pi.hThread);
4879         CloseHandle(pi.hProcess);
4880         return 0;
4881       } else return std::system(command);
4882 #else
4883       return std::system(command);
4884 #endif
4885 #endif
4886     }
4887 
4888     //! Return a reference to a temporary variable of type T.
4889     template<typename T>
4890     inline T& temporary(const T&) {
4891       static T temp;
4892       return temp;
4893     }
4894 
4895     //! Exchange values of variables \c a and \c b.
4896     template<typename T>
4897     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
4898 
4899     //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
4900     template<typename T1, typename T2>
4901     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
4902       cimg::swap(a1,b1); cimg::swap(a2,b2);
4903     }
4904 
4905     //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
4906     template<typename T1, typename T2, typename T3>
4907     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
4908       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
4909     }
4910 
4911     //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
4912     template<typename T1, typename T2, typename T3, typename T4>
4913     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
4914       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
4915     }
4916 
4917     //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
4918     template<typename T1, typename T2, typename T3, typename T4, typename T5>
4919     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
4920       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
4921     }
4922 
4923     //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
4924     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
4925     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) {
4926       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
4927     }
4928 
4929     //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
4930     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
4931     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,
4932                      T7& a7, T7& b7) {
4933       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
4934     }
4935 
4936     //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
4937     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
4938     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,
4939                      T7& a7, T7& b7, T8& a8, T8& b8) {
4940       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
4941     }
4942 
4943     //! Return the endianness of the current architecture.
4944     /**
4945        \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
4946     **/
4947     inline bool endianness() {
4948       const int x = 1;
4949       return ((unsigned char*)&x)[0]?false:true;
4950     }
4951 
4952     //! Reverse endianness of all elements in a memory buffer.
4953     /**
4954        \param[in,out] buffer Memory buffer whose endianness must be reversed.
4955        \param size Number of buffer elements to reverse.
4956     **/
4957     template<typename T>
4958     inline void invert_endianness(T* const buffer, const cimg_ulong size) {
4959       if (size) switch (sizeof(T)) {
4960         case 1 : break;
4961         case 2 : {
4962           for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
4963             const unsigned short val = *(--ptr);
4964             *ptr = (unsigned short)((val>>8) | ((val<<8)));
4965           }
4966         } break;
4967         case 4 : {
4968           for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
4969             const unsigned int val = *(--ptr);
4970             *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
4971           }
4972         } break;
4973         case 8 : {
4974           const cimg_uint64
4975             m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
4976             m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
4977           for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
4978             const cimg_uint64 val = *(--ptr);
4979             *ptr =  (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
4980                      ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
4981           }
4982         } break;
4983         default : {
4984           for (T* ptr = buffer + size; ptr>buffer; ) {
4985             unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4986             for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4987           }
4988         }
4989         }
4990     }
4991 
4992     //! Reverse endianness of a single variable.
4993     /**
4994        \param[in,out] a Variable to reverse.
4995        \return Reference to reversed variable.
4996     **/
4997     template<typename T>
4998     inline T& invert_endianness(T& a) {
4999       invert_endianness(&a,1);
5000       return a;
5001     }
5002 
5003     // Conversion functions to get more precision when trying to store unsigned ints values as floats.
5004     inline unsigned int float2uint(const float f) {
5005       int tmp = 0;
5006       std::memcpy(&tmp,&f,sizeof(float));
5007       if (tmp>=0) return (unsigned int)f;
5008       unsigned int u;
5009       // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
5010       std::memcpy(&u,&f,sizeof(float));
5011       return ((u)<<1)>>1; // set sign bit to 0.
5012     }
5013 
5014     inline float uint2float(const unsigned int u) {
5015       if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
5016       float f;
5017       const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
5018       // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
5019       std::memcpy(&f,&v,sizeof(float));
5020       return f;
5021     }
5022 
5023     //! Return the value of a system timer, with a millisecond precision.
5024     /**
5025        \note The timer does not necessarily starts from \c 0.
5026     **/
5027     inline cimg_ulong time() {
5028 #if cimg_OS==1
5029       struct timeval st_time;
5030       gettimeofday(&st_time,0);
5031       return (cimg_ulong)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
5032 #elif cimg_OS==2
5033       SYSTEMTIME st_time;
5034       GetLocalTime(&st_time);
5035       return (cimg_ulong)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
5036 #else
5037       return 0;
5038 #endif
5039     }
5040 
5041     // Implement a tic/toc mechanism to display elapsed time of algorithms.
5042     inline cimg_ulong tictoc(const bool is_tic);
5043 
5044     //! Start tic/toc timer for time measurement between code instructions.
5045     /**
5046        \return Current value of the timer (same value as time()).
5047     **/
5048     inline cimg_ulong tic() {
5049       return cimg::tictoc(true);
5050     }
5051 
5052     //! End tic/toc timer and displays elapsed time from last call to tic().
5053     /**
5054        \return Time elapsed (in ms) since last call to tic().
5055     **/
5056     inline cimg_ulong toc() {
5057       return cimg::tictoc(false);
5058     }
5059 
5060     //! Sleep for a given numbers of milliseconds.
5061     /**
5062        \param milliseconds Number of milliseconds to wait for.
5063        \note This function frees the CPU ressources during the sleeping time.
5064        It can be used to temporize your program properly, without wasting CPU time.
5065     **/
5066     inline void sleep(const unsigned int milliseconds) {
5067 #if cimg_OS==1
5068       struct timespec tv;
5069       tv.tv_sec = milliseconds/1000;
5070       tv.tv_nsec = (milliseconds%1000)*1000000;
5071       nanosleep(&tv,0);
5072 #elif cimg_OS==2
5073       Sleep(milliseconds);
5074 #else
5075       cimg::unused(milliseconds);
5076 #endif
5077     }
5078 
5079     inline unsigned int _wait(const unsigned int milliseconds, cimg_ulong& timer) {
5080       if (!timer) timer = cimg::time();
5081       const cimg_ulong current_time = cimg::time();
5082       if (current_time>=timer + milliseconds) { timer = current_time; return 0; }
5083       const unsigned int time_diff = (unsigned int)(timer + milliseconds - current_time);
5084       timer = current_time + time_diff;
5085       cimg::sleep(time_diff);
5086       return time_diff;
5087     }
5088 
5089     //! Wait for a given number of milliseconds since the last call to wait().
5090     /**
5091        \param milliseconds Number of milliseconds to wait for.
5092        \return Number of milliseconds elapsed since the last call to wait().
5093        \note Same as sleep() with a waiting time computed with regard to the last call
5094        of wait(). It may be used to temporize your program properly, without wasting CPU time.
5095     **/
5096     inline cimg_long wait(const unsigned int milliseconds) {
5097       cimg::mutex(3);
5098       static cimg_ulong timer = 0;
5099       if (!timer) timer = cimg::time();
5100       cimg::mutex(3,0);
5101       return _wait(milliseconds,timer);
5102     }
5103 
5104     // Random number generators.
5105     // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
5106     // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
5107     // at the same time!
5108 #ifdef cimg_use_rng
5109 
5110 #include <stdint.h>
5111 
5112     // Use a custom RNG.
5113     inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) {
5114       static cimg_ulong next = 0xB16B00B5;
5115       cimg::mutex(4);
5116       if (set_seed) next = (cimg_ulong)seed;
5117       else next = next*1103515245 + 12345U;
5118       cimg::mutex(4,0);
5119       return (unsigned int)(next&0xFFFFFFU);
5120     }
5121 
5122     inline unsigned int srand() {
5123       unsigned int t = (unsigned int)cimg::time();
5124 #if cimg_OS==1
5125       t+=(unsigned int)getpid();
5126 #elif cimg_OS==2
5127       t+=(unsigned int)_getpid();
5128 #endif
5129       return cimg::_rand(t,true);
5130     }
5131 
5132     inline unsigned int srand(const unsigned int seed) {
5133       return _rand(seed,true);
5134     }
5135 
5136     inline double rand(const double val_min, const double val_max) {
5137       const double val = cimg::_rand()/16777215.;
5138       return val_min + (val_max - val_min)*val;
5139     }
5140 
5141 #else
5142 
5143     // Use the system RNG.
5144     inline unsigned int srand() {
5145       const unsigned int t = (unsigned int)cimg::time();
5146 #if cimg_OS==1 || defined(__BORLANDC__)
5147       std::srand(t + (unsigned int)getpid());
5148 #elif cimg_OS==2
5149       std::srand(t + (unsigned int)_getpid());
5150 #else
5151       std::srand(t);
5152 #endif
5153       return t;
5154     }
5155 
5156     inline unsigned int srand(const unsigned int seed) {
5157       std::srand(seed);
5158       return seed;
5159     }
5160 
5161     //! Return a random variable uniformely distributed between [val_min,val_max].
5162     /**
5163     **/
5164     inline double rand(const double val_min, const double val_max) {
5165       const double val = (double)std::rand()/RAND_MAX;
5166       return val_min + (val_max - val_min)*val;
5167     }
5168 #endif
5169 
5170     //! Return a random variable uniformely distributed between [0,val_max].
5171     /**
5172      **/
5173     inline double rand(const double val_max=1) {
5174       return cimg::rand(0,val_max);
5175     }
5176 
5177     //! Return a random variable following a gaussian distribution and a standard deviation of 1.
5178     /**
5179     **/
5180     inline double grand() {
5181       double x1, w;
5182       do {
5183         const double x2 = cimg::rand(-1,1);
5184         x1 = cimg::rand(-1,1);
5185         w = x1*x1 + x2*x2;
5186       } while (w<=0 || w>=1.0);
5187       return x1*std::sqrt((-2*std::log(w))/w);
5188     }
5189 
5190     //! Return a random variable following a Poisson distribution of parameter z.
5191     /**
5192     **/
5193     inline unsigned int prand(const double z) {
5194       if (z<=1.0e-10) return 0;
5195       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
5196       unsigned int k = 0;
5197       const double y = std::exp(-z);
5198       for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
5199       return k - 1;
5200     }
5201 
5202     //! Cut (i.e. clamp) value in specified interval.
5203     template<typename T, typename t>
5204     inline T cut(const T& val, const t& val_min, const t& val_max) {
5205       return val<val_min?(T)val_min:val>val_max?(T)val_max:val;
5206     }
5207 
5208     //! Bitwise-rotate value on the left.
5209     template<typename T>
5210     inline T rol(const T& a, const unsigned int n=1) {
5211       return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
5212     }
5213 
5214     inline float rol(const float a, const unsigned int n=1) {
5215       return (float)rol((int)a,n);
5216     }
5217 
5218     inline double rol(const double a, const unsigned int n=1) {
5219       return (double)rol((cimg_long)a,n);
5220     }
5221 
5222     inline double rol(const long double a, const unsigned int n=1) {
5223       return (double)rol((cimg_long)a,n);
5224     }
5225 
5226 #ifdef cimg_use_half
5227     inline half rol(const half a, const unsigned int n=1) {
5228       return (half)rol((int)a,n);
5229     }
5230 #endif
5231 
5232     //! Bitwise-rotate value on the right.
5233     template<typename T>
5234     inline T ror(const T& a, const unsigned int n=1) {
5235       return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
5236     }
5237 
5238     inline float ror(const float a, const unsigned int n=1) {
5239       return (float)ror((int)a,n);
5240     }
5241 
5242     inline double ror(const double a, const unsigned int n=1) {
5243       return (double)ror((cimg_long)a,n);
5244     }
5245 
5246     inline double ror(const long double a, const unsigned int n=1) {
5247       return (double)ror((cimg_long)a,n);
5248     }
5249 
5250 #ifdef cimg_use_half
5251     inline half ror(const half a, const unsigned int n=1) {
5252       return (half)ror((int)a,n);
5253     }
5254 #endif
5255 
5256     //! Return absolute value of a value.
5257     template<typename T>
5258     inline T abs(const T& a) {
5259       return a>=0?a:-a;
5260     }
5261     inline bool abs(const bool a) {
5262       return a;
5263     }
5264     inline int abs(const unsigned char a) {
5265       return (int)a;
5266     }
5267     inline int abs(const unsigned short a) {
5268       return (int)a;
5269     }
5270     inline int abs(const unsigned int a) {
5271       return (int)a;
5272     }
5273     inline int abs(const int a) {
5274       return std::abs(a);
5275     }
5276     inline cimg_int64 abs(const cimg_uint64 a) {
5277       return (cimg_int64)a;
5278     }
5279     inline double abs(const double a) {
5280       return std::fabs(a);
5281     }
5282     inline float abs(const float a) {
5283       return (float)std::fabs((double)a);
5284     }
5285 
5286     //! Return square of a value.
5287     template<typename T>
5288     inline T sqr(const T& val) {
5289       return val*val;
5290     }
5291 
5292     //! Return <tt>1 + log_10(x)</tt> of a value \c x.
5293     inline int xln(const int x) {
5294       return x>0?(int)(1 + std::log10((double)x)):1;
5295     }
5296 
5297     //! Return the minimum between three values.
5298     template<typename t>
5299     inline t min(const t& a, const t& b, const t& c) {
5300       return std::min(std::min(a,b),c);
5301     }
5302 
5303     //! Return the minimum between four values.
5304     template<typename t>
5305     inline t min(const t& a, const t& b, const t& c, const t& d) {
5306       return std::min(std::min(a,b),std::min(c,d));
5307     }
5308 
5309     //! Return the maximum between three values.
5310     template<typename t>
5311     inline t max(const t& a, const t& b, const t& c) {
5312       return std::max(std::max(a,b),c);
5313     }
5314 
5315     //! Return the maximum between four values.
5316     template<typename t>
5317     inline t max(const t& a, const t& b, const t& c, const t& d) {
5318       return std::max(std::max(a,b),std::max(c,d));
5319     }
5320 
5321     //! Return the sign of a value.
5322     template<typename T>
5323     inline T sign(const T& x) {
5324       return (T)(x<0?-1:x>0);
5325     }
5326 
5327     //! Return the nearest power of 2 higher than given value.
5328     template<typename T>
5329     inline cimg_ulong nearest_pow2(const T& x) {
5330       cimg_ulong i = 1;
5331       while (x>i) i<<=1;
5332       return i;
5333     }
5334 
5335     //! Return the sinc of a given value.
5336     inline double sinc(const double x) {
5337       return x?std::sin(x)/x:1;
5338     }
5339 
5340     //! Return the modulo of a value.
5341     /**
5342        \param x Input value.
5343        \param m Modulo value.
5344        \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
5345     **/
5346     template<typename T>
5347     inline T mod(const T& x, const T& m) {
5348       const double dx = (double)x, dm = (double)m;
5349       return (T)(dx - dm * std::floor(dx / dm));
5350     }
5351     inline int mod(const bool x, const bool m) {
5352       return m?(x?1:0):0;
5353     }
5354     inline int mod(const unsigned char x, const unsigned char m) {
5355       return x%m;
5356     }
5357     inline int mod(const char x, const char m) {
5358 #if defined(CHAR_MAX) && CHAR_MAX==255
5359       return x%m;
5360 #else
5361       return x>=0?x%m:(x%m?m + x%m:0);
5362 #endif
5363     }
5364     inline int mod(const unsigned short x, const unsigned short m) {
5365       return x%m;
5366     }
5367     inline int mod(const short x, const short m) {
5368       return x>=0?x%m:(x%m?m + x%m:0);
5369     }
5370     inline int mod(const unsigned int x, const unsigned int m) {
5371       return (int)(x%m);
5372     }
5373     inline int mod(const int x, const int m) {
5374       return x>=0?x%m:(x%m?m + x%m:0);
5375     }
5376     inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
5377       return x%m;
5378     }
5379     inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
5380       return x>=0?x%m:(x%m?m + x%m:0);
5381     }
5382 
5383     //! Return the min-mod of two values.
5384     /**
5385        \note <i>minmod(\p a,\p b)</i> is defined to be:
5386        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
5387        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
5388     **/
5389     template<typename T>
5390     inline T minmod(const T& a, const T& b) {
5391       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
5392     }
5393 
5394     //! Return base-2 logarithm of a value.
5395     inline double log2(const double x) {
5396       const double base = std::log(2.0);
5397       return std::log(x)/base;
5398     }
5399 
5400     template<typename T>
5401     inline T round(const T& x) {
5402       return (T)std::floor((_cimg_Tfloat)x + 0.5f);
5403     }
5404 
5405     //! Return rounded value.
5406     /**
5407        \param x Value to be rounded.
5408        \param y Rounding precision.
5409        \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
5410        \return Rounded value, having the same type as input value \c x.
5411     **/
5412     template<typename T>
5413     inline T round(const T& x, const double y, const int rounding_type=0) {
5414       if (y<=0) return x;
5415       if (y==1) switch (rounding_type) {
5416         case 0 : return round(x);
5417         case 1 : return (T)std::ceil((_cimg_Tfloat)x);
5418         default : return (T)std::floor((_cimg_Tfloat)x);
5419         }
5420       const double sx = (double)x/y, floor = std::floor(sx), delta =  sx - floor;
5421       return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
5422     }
5423 
5424     //! Return x^(1/3).
5425     template<typename T>
5426     inline double cbrt(const T& x) {
5427 #if cimg_use_cpp11==1
5428       return std::cbrt(x);
5429 #else
5430       return x>=0?std::pow((double)x,1.0/3):-std::pow(-(double)x,1.0/3);
5431 #endif
5432     }
5433 
5434     // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
5435     // (contribution by RawTherapee: http://rawtherapee.com/).
5436     template<typename T>
5437     inline T median(T val0, T val1) {
5438       return (val0 + val1)/2;
5439     }
5440 
5441     template<typename T>
5442     inline T median(T val0, T val1, T val2) {
5443       return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
5444     }
5445 
5446     template<typename T>
5447     inline T median(T val0, T val1, T val2, T val3, T val4) {
5448       T tmp = std::min(val0,val1);
5449       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
5450       val3 = std::max(val0,tmp);  val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
5451       val1 = tmp; tmp = std::min(val2,val3);
5452       return std::max(val1,tmp);
5453     }
5454 
5455     template<typename T>
5456     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
5457       T tmp = std::min(val0,val5);
5458       val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
5459       tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
5460       val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
5461       val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
5462       val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
5463       tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
5464       return std::min(val3,val4);
5465     }
5466 
5467     template<typename T>
5468     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
5469       T tmp = std::min(val1,val2);
5470       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
5471       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
5472       val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
5473       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
5474       val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
5475       val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
5476       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
5477       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
5478       val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
5479       val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
5480       val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
5481       tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
5482       return std::min(val4,val2);
5483     }
5484 
5485     template<typename T>
5486     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,
5487                     T val12) {
5488       T tmp = std::min(val1,val7);
5489       val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
5490       tmp = std::min(val3,val4);  val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
5491       val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
5492       val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
5493       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
5494       tmp = std::min(val4,val6);  val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
5495       val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
5496       tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
5497       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
5498       tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
5499       val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
5500       tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
5501       tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
5502       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
5503       tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
5504       val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
5505       tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
5506       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
5507       val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
5508       val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
5509       val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
5510       return std::max(val5,val6);
5511     }
5512 
5513     template<typename T>
5514     inline T median(T val0, T val1, T val2, T val3, T val4,
5515                     T val5, T val6, T val7, T val8, T val9,
5516                     T val10, T val11, T val12, T val13, T val14,
5517                     T val15, T val16, T val17, T val18, T val19,
5518                     T val20, T val21, T val22, T val23, T val24) {
5519       T tmp = std::min(val0,val1);
5520       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
5521       val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
5522       val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
5523       tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
5524       tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
5525       val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
5526       tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
5527       val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
5528       tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
5529       val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
5530       tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
5531       val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
5532       tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
5533       val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
5534       tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
5535       val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
5536       tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
5537       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
5538       val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
5539       val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
5540       val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
5541       val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
5542       val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
5543       val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
5544       val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
5545       val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
5546       val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
5547       val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
5548       val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
5549       val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
5550       val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
5551       val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
5552       tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
5553       val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
5554       tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
5555       tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
5556       val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
5557       tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
5558       val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
5559       val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
5560       val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
5561       val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
5562       val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
5563       tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
5564       val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
5565       val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
5566       tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
5567       val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
5568       val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
5569       tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
5570       tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
5571       val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
5572       val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
5573       val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
5574       tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
5575       val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
5576       tmp = std::min(val10,val20);
5577       return std::max(tmp,val12);
5578     }
5579 
5580     template<typename T>
5581     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
5582                     T val7, T val8, T val9, T val10, T val11, T val12, T val13,
5583                     T val14, T val15, T val16, T val17, T val18, T val19, T val20,
5584                     T val21, T val22, T val23, T val24, T val25, T val26, T val27,
5585                     T val28, T val29, T val30, T val31, T val32, T val33, T val34,
5586                     T val35, T val36, T val37, T val38, T val39, T val40, T val41,
5587                     T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
5588       T tmp = std::min(val0,val32);
5589       val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
5590       tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
5591       val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
5592       tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
5593       val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
5594       tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
5595       val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
5596       val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
5597       tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
5598       val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
5599       val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
5600       tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
5601       val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
5602       val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
5603       val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
5604       tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
5605       val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
5606       tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
5607       val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
5608       val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
5609       tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
5610       val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
5611       val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
5612       tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
5613       val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
5614       val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
5615       tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
5616       val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
5617       val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
5618       tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
5619       val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
5620       val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
5621       tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
5622       val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
5623       val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
5624       tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
5625       val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
5626       tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
5627       val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
5628       tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
5629       val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
5630       tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
5631       val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
5632       val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
5633       tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
5634       val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
5635       val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
5636       tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
5637       val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
5638       val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
5639       tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
5640       val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
5641       val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
5642       tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
5643       val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
5644       val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
5645       tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
5646       val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
5647       val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
5648       tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
5649       val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
5650       val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
5651       tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
5652       val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
5653       val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
5654       tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
5655       val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
5656       val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
5657       tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
5658       val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
5659       val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
5660       tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
5661       val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
5662       val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
5663       val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
5664       tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
5665       val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
5666       val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
5667       tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
5668       val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
5669       val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
5670       tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
5671       val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
5672       val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
5673       tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
5674       val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
5675       val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
5676       tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
5677       val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
5678       val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
5679       tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
5680       val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
5681       val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
5682       tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
5683       val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
5684       val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
5685       tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
5686       val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
5687       val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
5688       tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
5689       val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
5690       val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
5691       tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
5692       val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
5693       val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
5694       tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
5695       val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
5696       val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
5697       tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
5698       val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
5699       val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
5700       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
5701       tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
5702       val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
5703       val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
5704       tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
5705       val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
5706       val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
5707       tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
5708       val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
5709       val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
5710       tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
5711       val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
5712       val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
5713       tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
5714       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
5715       tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
5716       val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
5717       tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
5718       val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
5719       val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
5720       tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
5721       val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
5722       val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
5723       tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
5724       val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
5725       val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
5726       tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
5727       val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
5728       val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
5729       tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
5730       val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
5731       val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
5732       val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
5733       tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
5734       val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
5735       val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
5736       tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
5737       val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
5738       val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
5739       tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
5740       val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
5741       val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
5742       tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
5743       val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
5744       val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
5745       tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
5746       val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
5747       val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
5748       tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
5749       val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
5750       val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
5751       val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
5752       tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
5753       val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
5754       val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
5755       tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
5756       val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
5757       val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
5758       tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
5759       val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
5760       val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
5761       tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
5762       val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
5763       val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
5764       tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
5765       val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
5766       val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
5767       val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
5768       tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
5769       val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
5770       val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
5771       tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
5772       val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
5773       val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
5774       tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
5775       val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
5776       val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
5777       tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
5778       val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
5779       val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
5780       tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
5781       val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
5782       val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
5783       val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
5784       val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
5785       val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
5786       val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
5787       val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
5788       val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
5789       val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
5790       val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
5791       val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
5792       val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
5793       val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
5794       val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
5795       val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
5796       val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
5797       val24 = std::max(val21,val24); val23 = std::min(val23,val26);
5798       return std::max(val23,val24);
5799     }
5800 
5801     //! Return sqrt(x^2 + y^2).
5802     template<typename T>
5803     inline T hypot(const T x, const T y) {
5804       return std::sqrt(x*x + y*y);
5805     }
5806 
5807     template<typename T>
5808     inline T hypot(const T x, const T y, const T z) {
5809       return std::sqrt(x*x + y*y + z*z);
5810     }
5811 
5812     template<typename T>
5813     inline T _hypot(const T x, const T y) { // Slower but more precise version
5814       T nx = cimg::abs(x), ny = cimg::abs(y), t;
5815       if (nx<ny) { t = nx; nx = ny; } else t = ny;
5816       if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); }
5817       return 0;
5818     }
5819 
5820     //! Return the factorial of n
5821     inline double factorial(const int n) {
5822       if (n<0) return cimg::type<double>::nan();
5823       if (n<2) return 1;
5824       double res = 2;
5825       for (int i = 3; i<=n; ++i) res*=i;
5826       return res;
5827     }
5828 
5829     //! Return the number of permutations of k objects in a set of n objects.
5830     inline double permutations(const int k, const int n, const bool with_order) {
5831       if (n<0 || k<0) return cimg::type<double>::nan();
5832       if (k>n) return 0;
5833       double res = 1;
5834       for (int i = n; i>=n - k + 1; --i) res*=i;
5835       return with_order?res:res/cimg::factorial(k);
5836     }
5837 
5838     inline double _fibonacci(int exp) {
5839       double
5840         base = (1 + std::sqrt(5.0))/2,
5841         result = 1/std::sqrt(5.0);
5842       while (exp) {
5843         if (exp&1) result*=base;
5844         exp>>=1;
5845         base*=base;
5846       }
5847       return result;
5848     }
5849 
5850     //! Calculate fibonacci number.
5851     // (Precise up to n = 78, less precise for n>78).
5852     inline double fibonacci(const int n) {
5853       if (n<0) return cimg::type<double>::nan();
5854       if (n<3) return 1;
5855       if (n<11) {
5856         cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
5857         for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
5858         return (double)fn;
5859       }
5860       if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
5861         return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
5862 
5863       if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
5864         cimg_uint64
5865           fn1 = (cimg_uint64)1304969544928657ULL,
5866           fn2 = (cimg_uint64)806515533049393ULL,
5867           fn = 0;
5868         for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
5869         return (double)fn;
5870       }
5871       return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
5872     }
5873 
5874     //! Calculate greatest common divisor.
5875     inline long gcd(long a, long b) {
5876       while (a) { const long c = a; a = b%a; b = c; }
5877       return b;
5878     }
5879 
5880     //! Convert ascii character to lower case.
5881     inline char lowercase(const char x) {
5882       return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
5883     }
5884     inline double lowercase(const double x) {
5885       return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
5886     }
5887 
5888     //! Convert C-string to lower case.
5889     inline void lowercase(char *const str) {
5890       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
5891     }
5892 
5893     //! Convert ascii character to upper case.
5894     inline char uppercase(const char x) {
5895       return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
5896     }
5897 
5898     inline double uppercase(const double x) {
5899       return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
5900     }
5901 
5902     //! Convert C-string to upper case.
5903     inline void uppercase(char *const str) {
5904       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
5905     }
5906 
5907     //! Read value in a C-string.
5908     /**
5909        \param str C-string containing the float value to read.
5910        \return Read value.
5911        \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
5912        as in <em>"1/2"</em>.
5913     **/
5914     inline double atof(const char *const str) {
5915       double x = 0, y = 1;
5916       return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
5917     }
5918 
5919     //! Compare the first \p l characters of two C-strings, ignoring the case.
5920     /**
5921        \param str1 C-string.
5922        \param str2 C-string.
5923        \param l Number of characters to compare.
5924        \return \c 0 if the two strings are equal, something else otherwise.
5925        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
5926     **/
5927     inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
5928       if (!l) return 0;
5929       if (!str1) return str2?-1:0;
5930       const char *nstr1 = str1, *nstr2 = str2;
5931       int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
5932       return k!=l?diff:0;
5933     }
5934 
5935     //! Compare two C-strings, ignoring the case.
5936     /**
5937        \param str1 C-string.
5938        \param str2 C-string.
5939        \return \c 0 if the two strings are equal, something else otherwise.
5940        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
5941     **/
5942     inline int strcasecmp(const char *const str1, const char *const str2) {
5943       if (!str1) return str2?-1:0;
5944       const int
5945         l1 = (int)std::strlen(str1),
5946         l2 = (int)std::strlen(str2);
5947       return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
5948     }
5949 
5950     //! Ellipsize a string.
5951     /**
5952        \param str C-string.
5953        \param l Max number of characters.
5954        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
5955     **/
5956     inline char *strellipsize(char *const str, const unsigned int l=64,
5957                               const bool is_ending=true) {
5958       if (!str) return str;
5959       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
5960       if (ls<=nl) return str;
5961       if (is_ending) std::strcpy(str + nl - 5,"(...)");
5962       else {
5963         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
5964         std::strcpy(str + ll,"(...)");
5965         std::memmove(str + ll + 5,str + ls - lr,lr);
5966       }
5967       str[nl] = 0;
5968       return str;
5969     }
5970 
5971     //! Ellipsize a string.
5972     /**
5973        \param str C-string.
5974        \param res output C-string.
5975        \param l Max number of characters.
5976        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
5977     **/
5978     inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
5979                               const bool is_ending=true) {
5980       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
5981       if (ls<=nl) { std::strcpy(res,str); return res; }
5982       if (is_ending) {
5983         std::strncpy(res,str,nl - 5);
5984         std::strcpy(res + nl -5,"(...)");
5985       } else {
5986         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
5987         std::strncpy(res,str,ll);
5988         std::strcpy(res + ll,"(...)");
5989         std::strncpy(res + ll + 5,str + ls - lr,lr);
5990       }
5991       res[nl] = 0;
5992       return res;
5993     }
5994 
5995     //! Remove delimiters on the start and/or end of a C-string.
5996     /**
5997        \param[in,out] str C-string to work with (modified at output).
5998        \param delimiter Delimiter character code to remove.
5999        \param is_symmetric Tells if the removal is done only if delimiters are symmetric
6000        (both at the beginning and the end of \c s).
6001        \param is_iterative Tells if the removal is done if several iterations are possible.
6002        \return \c true if delimiters have been removed, \c false otherwise.
6003    **/
6004     inline bool strpare(char *const str, const char delimiter,
6005                         const bool is_symmetric, const bool is_iterative) {
6006       if (!str) return false;
6007       const int l = (int)std::strlen(str);
6008       int p, q;
6009       if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
6010           --q; ++p; if (!is_iterative) break;
6011         } else {
6012         for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
6013         for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
6014       }
6015       const int n = q - p + 1;
6016       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6017       return false;
6018     }
6019 
6020     //! Remove white spaces on the start and/or end of a C-string.
6021     inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
6022       if (!str) return false;
6023       const int l = (int)std::strlen(str);
6024       int p, q;
6025       if (is_symmetric) for (p = 0, q = l - 1; p<q && (signed char)str[p]<=' ' && (signed char)str[q]<=' '; ) {
6026           --q; ++p; if (!is_iterative) break;
6027         } else {
6028         for (p = 0; p<l && (signed char)str[p]<=' '; ) { ++p; if (!is_iterative) break; }
6029         for (q = l - 1; q>p && (signed char)str[q]<=' '; ) { --q; if (!is_iterative) break; }
6030       }
6031       const int n = q - p + 1;
6032       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6033       return false;
6034     }
6035 
6036     //! Replace reserved characters (for Windows filename) by another character.
6037     /**
6038        \param[in,out] str C-string to work with (modified at output).
6039        \param[in] c Replacement character.
6040     **/
6041     inline void strwindows_reserved(char *const str, const char c='_') {
6042       for (char *s = str; *s; ++s) {
6043         const char i = *s;
6044         if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
6045       }
6046     }
6047 
6048     //! Replace escape sequences in C-strings by their binary ascii values.
6049     /**
6050        \param[in,out] str C-string to work with (modified at output).
6051     **/
6052     inline void strunescape(char *const str) {
6053 #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
6054       unsigned int val = 0;
6055       for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
6056             cimg_strunescape('a','\a');
6057             cimg_strunescape('b','\b');
6058             cimg_strunescape('e',0x1B);
6059             cimg_strunescape('f','\f');
6060             cimg_strunescape('n','\n');
6061             cimg_strunescape('r','\r');
6062             cimg_strunescape('t','\t');
6063             cimg_strunescape('v','\v');
6064             cimg_strunescape('\\','\\');
6065             cimg_strunescape('\'','\'');
6066             cimg_strunescape('\"','\"');
6067             cimg_strunescape('\?','\?');
6068           case 0 : *nd = 0; break;
6069           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
6070             cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
6071             *nd = (char)val; break;
6072           case 'x' :
6073             cimg_sscanf(++ns,"%x",&val);
6074             while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
6075             *nd = (char)val; break;
6076           default : *nd = *(ns++);
6077           } else *nd = *(ns++);
6078     }
6079 
6080     // Return a temporary string describing the size of a memory buffer.
6081     inline const char *strbuffersize(const cimg_ulong size);
6082 
6083     // Return string that identifies the running OS.
6084     inline const char *stros() {
6085 #if defined(linux) || defined(__linux) || defined(__linux__)
6086       static const char *const str = "Linux";
6087 #elif defined(sun) || defined(__sun)
6088       static const char *const str = "Sun OS";
6089 #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
6090       static const char *const str = "BSD";
6091 #elif defined(sgi) || defined(__sgi)
6092       static const char *const str = "Irix";
6093 #elif defined(__MACOSX__) || defined(__APPLE__)
6094       static const char *const str = "Mac OS";
6095 #elif defined(unix) || defined(__unix) || defined(__unix__)
6096       static const char *const str = "Generic Unix";
6097 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) || \
6098   defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6099       static const char *const str = "Windows";
6100 #else
6101       const char
6102         *const _str1 = std::getenv("OSTYPE"),
6103         *const _str2 = _str1?_str1:std::getenv("OS"),
6104         *const str = _str2?_str2:"Unknown OS";
6105 #endif
6106       return str;
6107     }
6108 
6109     //! Return the basename of a filename.
6110     inline const char* basename(const char *const s, const char separator=cimg_file_separator)  {
6111       const char *p = 0, *np = s;
6112       while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
6113       return p;
6114     }
6115 
6116     // Return a random filename.
6117     inline const char* filenamerand() {
6118       cimg::mutex(6);
6119       static char randomid[9];
6120       cimg::srand();
6121       for (unsigned int k = 0; k<8; ++k) {
6122         const int v = (int)cimg::rand(65535)%3;
6123         randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
6124                              (v==1?('a' + ((int)cimg::rand(65535)%26)):
6125                               ('A' + ((int)cimg::rand(65535)%26))));
6126       }
6127       cimg::mutex(6,0);
6128       return randomid;
6129     }
6130 
6131     // Convert filename as a Windows-style filename (short path name).
6132     inline void winformat_string(char *const str) {
6133       if (str && *str) {
6134 #if cimg_OS==2
6135         char *const nstr = new char[MAX_PATH];
6136         if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
6137         delete[] nstr;
6138 #endif
6139       }
6140     }
6141 
6142     // Open a file (with wide character support on Windows).
6143     inline std::FILE *win_fopen(const char *const path, const char *const mode);
6144 
6145     //! Open a file.
6146     /**
6147        \param path Path of the filename to open.
6148        \param mode C-string describing the opening mode.
6149        \return Opened file.
6150        \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
6151        the specified file cannot be opened, instead of returning \c 0.
6152     **/
6153     inline std::FILE *fopen(const char *const path, const char *const mode) {
6154       if (!path)
6155         throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
6156       if (!mode)
6157         throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
6158                                     path);
6159       std::FILE *res = 0;
6160       if (*path=='-' && (!path[1] || path[1]=='.')) {
6161         res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
6162 #if cimg_OS==2
6163         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
6164 #ifdef __BORLANDC__
6165           if (setmode(_fileno(res),0x8000)==-1) res = 0;
6166 #else
6167           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
6168 #endif
6169         }
6170 #endif
6171       } else res = std_fopen(path,mode);
6172       if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
6173                                       path,mode);
6174       return res;
6175     }
6176 
6177     //! Close a file.
6178     /**
6179        \param file File to close.
6180        \return \c 0 if file has been closed properly, something else otherwise.
6181        \note Same as <tt>std::fclose()</tt> but display a warning message if
6182        the file has not been closed properly.
6183     **/
6184     inline int fclose(std::FILE *file) {
6185       if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
6186       if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
6187       const int errn = std::fclose(file);
6188       if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
6189                         errn);
6190       return errn;
6191     }
6192 
6193     //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
6194     inline int fseek(FILE *stream, cimg_long offset, int origin) {
6195 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6196       return _fseeki64(stream,(__int64)offset,origin);
6197 #else
6198       return std::fseek(stream,offset,origin);
6199 #endif
6200     }
6201 
6202     //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
6203     inline cimg_long ftell(FILE *stream) {
6204 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6205       return (cimg_long)_ftelli64(stream);
6206 #else
6207       return (cimg_long)std::ftell(stream);
6208 #endif
6209     }
6210 
6211     //! Check if a path is a directory.
6212     /**
6213        \param path Specified path to test.
6214     **/
6215     inline bool is_directory(const char *const path) {
6216       if (!path || !*path) return false;
6217 #if cimg_OS==1
6218       struct stat st_buf;
6219       return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
6220 #elif cimg_OS==2
6221       const unsigned int res = (unsigned int)GetFileAttributesA(path);
6222       return res==INVALID_FILE_ATTRIBUTES?false:(res&16);
6223 #else
6224       return false;
6225 #endif
6226     }
6227 
6228     //! Check if a path is a file.
6229     /**
6230        \param path Specified path to test.
6231     **/
6232     inline bool is_file(const char *const path) {
6233       if (!path || !*path) return false;
6234       std::FILE *const file = std_fopen(path,"rb");
6235       if (!file) return false;
6236       std::fclose(file);
6237       return !is_directory(path);
6238     }
6239 
6240     //! Get file size.
6241     /**
6242        \param filename Specified filename to get size from.
6243        \return File size or '-1' if file does not exist.
6244     **/
6245     inline cimg_int64 fsize(const char *const filename) {
6246       std::FILE *const file = std::fopen(filename,"rb");
6247       if (!file) return (cimg_int64)-1;
6248       std::fseek(file,0,SEEK_END);
6249       const cimg_int64 siz = (cimg_int64)std::ftell(file);
6250       std::fclose(file);
6251       return siz;
6252     }
6253 
6254     //! Get last write time of a given file or directory (multiple-attributes version).
6255     /**
6256        \param path Specified path to get attributes from.
6257        \param[in,out] attr Type of requested time attributes.
6258                       Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6259                       Replaced by read attributes after return (or -1 if an error occured).
6260        \param nb_attr Number of attributes to read/write.
6261        \return Latest read attribute.
6262     **/
6263     template<typename T>
6264     inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
6265 #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
6266       int res = -1;
6267       if (!path || !*path) { _cimg_fdate_err(); return -1; }
6268       cimg::mutex(6);
6269 #if cimg_OS==2
6270       HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
6271       if (file!=INVALID_HANDLE_VALUE) {
6272         FILETIME _ft;
6273         SYSTEMTIME ft;
6274         if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
6275           for (unsigned int i = 0; i<nb_attr; ++i) {
6276             res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
6277                         attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
6278                         attr[i]==6?ft.wSecond:-1);
6279             attr[i] = (T)res;
6280           }
6281         } else _cimg_fdate_err();
6282         CloseHandle(file);
6283       } else _cimg_fdate_err();
6284 #elif cimg_OS==1
6285       struct stat st_buf;
6286       if (!stat(path,&st_buf)) {
6287         const time_t _ft = st_buf.st_mtime;
6288         const struct tm& ft = *std::localtime(&_ft);
6289         for (unsigned int i = 0; i<nb_attr; ++i) {
6290           res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
6291                       attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
6292                       attr[i]==6?ft.tm_sec:-1);
6293           attr[i] = (T)res;
6294         }
6295       } else _cimg_fdate_err();
6296 #endif
6297       cimg::mutex(6,0);
6298       return res;
6299     }
6300 
6301     //! Get last write time of a given file or directory (single-attribute version).
6302     /**
6303        \param path Specified path to get attributes from.
6304        \param attr Type of requested time attributes.
6305                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6306        \return Specified attribute or -1 if an error occured.
6307     **/
6308     inline int fdate(const char *const path, unsigned int attr) {
6309       int out = (int)attr;
6310       return fdate(path,&out,1);
6311     }
6312 
6313     //! Get current local time (multiple-attributes version).
6314     /**
6315        \param[in,out] attr Type of requested time attributes.
6316                            Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6317                            Replaced by read attributes after return (or -1 if an error occured).
6318        \param nb_attr Number of attributes to read/write.
6319        \return Latest read attribute.
6320     **/
6321     template<typename T>
6322     inline int date(T *attr, const unsigned int nb_attr) {
6323       int res = -1;
6324       cimg::mutex(6);
6325 #if cimg_OS==2
6326       SYSTEMTIME st;
6327       GetLocalTime(&st);
6328       for (unsigned int i = 0; i<nb_attr; ++i) {
6329         res = (int)(attr[i]==0?st.wYear:attr[i]==1?st.wMonth:attr[i]==2?st.wDay:
6330                     attr[i]==3?st.wDayOfWeek:attr[i]==4?st.wHour:attr[i]==5?st.wMinute:
6331                     attr[i]==6?st.wSecond:-1);
6332         attr[i] = (T)res;
6333       }
6334 #else
6335       time_t _st;
6336       std::time(&_st);
6337       struct tm *st = std::localtime(&_st);
6338       for (unsigned int i = 0; i<nb_attr; ++i) {
6339         res = (int)(attr[i]==0?st->tm_year + 1900:attr[i]==1?st->tm_mon + 1:attr[i]==2?st->tm_mday:
6340                     attr[i]==3?st->tm_wday:attr[i]==4?st->tm_hour:attr[i]==5?st->tm_min:
6341                     attr[i]==6?st->tm_sec:-1);
6342         attr[i] = (T)res;
6343       }
6344 #endif
6345       cimg::mutex(6,0);
6346       return res;
6347     }
6348 
6349     //! Get current local time (single-attribute version).
6350     /**
6351        \param attr Type of requested time attribute.
6352                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6353        \return Specified attribute or -1 if an error occured.
6354     **/
6355     inline int date(unsigned int attr) {
6356       int out = (int)attr;
6357       return date(&out,1);
6358     }
6359 
6360     // Get/set path to store temporary files.
6361     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
6362 
6363     // Get/set path to the <i>Program Files/</i> directory (Windows only).
6364 #if cimg_OS==2
6365     inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false);
6366 #endif
6367 
6368     // Get/set path to the ImageMagick's \c convert binary.
6369     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
6370 
6371     // Get/set path to the GraphicsMagick's \c gm binary.
6372     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
6373 
6374     // Get/set path to the XMedcon's \c medcon binary.
6375     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
6376 
6377     // Get/set path to the FFMPEG's \c ffmpeg binary.
6378     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
6379 
6380     // Get/set path to the \c gzip binary.
6381     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
6382 
6383     // Get/set path to the \c gunzip binary.
6384     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
6385 
6386     // Get/set path to the \c dcraw binary.
6387     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
6388 
6389     // Get/set path to the \c wget binary.
6390     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
6391 
6392     // Get/set path to the \c curl binary.
6393     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
6394 
6395     //! Split filename into two C-strings \c body and \c extension.
6396     /**
6397        filename and body must not overlap!
6398     **/
6399     inline const char *split_filename(const char *const filename, char *const body=0) {
6400       if (!filename) { if (body) *body = 0; return 0; }
6401       const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.') + 1) {}
6402       if (p==filename) {
6403         if (body) std::strcpy(body,filename);
6404         return filename + std::strlen(filename);
6405       }
6406       const unsigned int l = (unsigned int)(p - filename - 1);
6407       if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
6408       return p;
6409     }
6410 
6411     //! Generate a numbered version of a filename.
6412     inline char* number_filename(const char *const filename, const int number,
6413                                  const unsigned int digits, char *const str) {
6414       if (!filename) { if (str) *str = 0; return 0; }
6415       char *const format = new char[1024], *const body = new char[1024];
6416       const char *const ext = cimg::split_filename(filename,body);
6417       if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits);
6418       else cimg_snprintf(format,1024,"%%s_%%.%ud",digits);
6419       cimg_sprintf(str,format,body,number,ext);
6420       delete[] format; delete[] body;
6421       return str;
6422     }
6423 
6424     //! Read data from file.
6425     /**
6426        \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
6427        \param nmemb Number of elements to read.
6428        \param stream File to read data from.
6429        \return Number of read elements.
6430        \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
6431     **/
6432     template<typename T>
6433     inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
6434       if (!ptr || !stream)
6435         throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
6436                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
6437       if (!nmemb) return 0;
6438       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
6439       size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
6440       do {
6441         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
6442         l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
6443         al_read+=l_al_read;
6444         to_read-=l_al_read;
6445       } while (l_to_read==l_al_read && to_read>0);
6446       if (to_read>0)
6447         warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
6448              (unsigned long)al_read,(unsigned long)nmemb);
6449       return al_read;
6450     }
6451 
6452     //! Write data to file.
6453     /**
6454        \param ptr Pointer to memory buffer containing the binary data to write on file.
6455        \param nmemb Number of elements to write.
6456        \param[out] stream File to write data on.
6457        \return Number of written elements.
6458        \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
6459     **/
6460     template<typename T>
6461     inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
6462       if (!ptr || !stream)
6463         throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
6464                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
6465       if (!nmemb) return 0;
6466       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
6467       size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
6468       do {
6469         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
6470         l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
6471         al_write+=l_al_write;
6472         to_write-=l_al_write;
6473       } while (l_to_write==l_al_write && to_write>0);
6474       if (to_write>0)
6475         warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
6476              (unsigned long)al_write,(unsigned long)nmemb);
6477       return al_write;
6478     }
6479 
6480     //! Create an empty file.
6481     /**
6482        \param file Input file (can be \c 0 if \c filename is set).
6483        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
6484     **/
6485     inline void fempty(std::FILE *const file, const char *const filename) {
6486       if (!file && !filename)
6487         throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
6488       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
6489       if (!file) cimg::fclose(nfile);
6490     }
6491 
6492     // Try to guess format from an image file.
6493     inline const char *ftype(std::FILE *const file, const char *const filename);
6494 
6495     // Load file from network as a local temporary file.
6496     inline char *load_network(const char *const url, char *const filename_local,
6497                               const unsigned int timeout=0, const bool try_fallback=false,
6498                               const char *const referer=0);
6499 
6500     //! Return options specified on the command line.
6501     inline const char* option(const char *const name, const int argc, const char *const *const argv,
6502                               const char *const defaut, const char *const usage, const bool reset_static) {
6503       static bool first = true, visu = false;
6504       if (reset_static) { first = true; return 0; }
6505       const char *res = 0;
6506       if (first) {
6507         first = false;
6508         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
6509         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
6510         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
6511       }
6512       if (!name && visu) {
6513         if (usage) {
6514           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
6515           std::fprintf(cimg::output(),": %s",usage);
6516           std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
6517         }
6518         if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
6519       }
6520       if (name) {
6521         if (argc>0) {
6522           int k = 0;
6523           while (k<argc && std::strcmp(argv[k],name)) ++k;
6524           res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
6525         } else res = defaut;
6526         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
6527                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",
6528                                         cimg::t_green,usage,cimg::t_normal);
6529       }
6530       return res;
6531     }
6532 
6533     inline const char* option(const char *const name, const int argc, const char *const *const argv,
6534                               const char *const defaut, const char *const usage=0) {
6535       return option(name,argc,argv,defaut,usage,false);
6536     }
6537 
6538     inline bool option(const char *const name, const int argc, const char *const *const argv,
6539                        const bool defaut, const char *const usage=0) {
6540       const char *const s = cimg::option(name,argc,argv,(char*)0);
6541       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
6542       cimg::option(name,0,0,res?"true":"false",usage);
6543       return res;
6544     }
6545 
6546     inline int option(const char *const name, const int argc, const char *const *const argv,
6547                       const int defaut, const char *const usage=0) {
6548       const char *const s = cimg::option(name,argc,argv,(char*)0);
6549       const int res = s?std::atoi(s):defaut;
6550       char *const tmp = new char[256];
6551       cimg_snprintf(tmp,256,"%d",res);
6552       cimg::option(name,0,0,tmp,usage);
6553       delete[] tmp;
6554       return res;
6555     }
6556 
6557     inline char option(const char *const name, const int argc, const char *const *const argv,
6558                        const char defaut, const char *const usage=0) {
6559       const char *const s = cimg::option(name,argc,argv,(char*)0);
6560       const char res = s?*s:defaut;
6561       char tmp[8];
6562       *tmp = res; tmp[1] = 0;
6563       cimg::option(name,0,0,tmp,usage);
6564       return res;
6565     }
6566 
6567     inline float option(const char *const name, const int argc, const char *const *const argv,
6568                         const float defaut, const char *const usage=0) {
6569       const char *const s = cimg::option(name,argc,argv,(char*)0);
6570       const float res = s?(float)cimg::atof(s):defaut;
6571       char *const tmp = new char[256];
6572       cimg_snprintf(tmp,256,"%g",res);
6573       cimg::option(name,0,0,tmp,usage);
6574       delete[] tmp;
6575       return res;
6576     }
6577 
6578     inline double option(const char *const name, const int argc, const char *const *const argv,
6579                          const double defaut, const char *const usage=0) {
6580       const char *const s = cimg::option(name,argc,argv,(char*)0);
6581       const double res = s?cimg::atof(s):defaut;
6582       char *const tmp = new char[256];
6583       cimg_snprintf(tmp,256,"%g",res);
6584       cimg::option(name,0,0,tmp,usage);
6585       delete[] tmp;
6586       return res;
6587     }
6588 
6589     //! Print information about \CImg environement variables.
6590     /**
6591        \note Output is done on the default output stream.
6592     **/
6593     inline void info() {
6594       std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
6595                    cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
6596                    cimg::t_normal,cimg_date,cimg_time);
6597 
6598       std::fprintf(cimg::output(),"  > Operating System:       %s%-13s%s %s('cimg_OS'=%d)%s\n",
6599                    cimg::t_bold,
6600                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
6601                    cimg::t_normal,cimg::t_green,
6602                    cimg_OS,
6603                    cimg::t_normal);
6604 
6605       std::fprintf(cimg::output(),"  > CPU endianness:         %s%s Endian%s\n",
6606                    cimg::t_bold,
6607                    cimg::endianness()?"Big":"Little",
6608                    cimg::t_normal);
6609 
6610       std::fprintf(cimg::output(),"  > Verbosity mode:         %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
6611                    cimg::t_bold,
6612                    cimg_verbosity==0?"Quiet":
6613                    cimg_verbosity==1?"Console":
6614                    cimg_verbosity==2?"Dialog":
6615                    cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
6616                    cimg::t_normal,cimg::t_green,
6617                    cimg_verbosity,
6618                    cimg::t_normal);
6619 
6620       std::fprintf(cimg::output(),"  > Stricts warnings:       %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
6621                    cimg::t_bold,
6622 #ifdef cimg_strict_warnings
6623                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6624 #else
6625                    "No",cimg::t_normal,cimg::t_green,"undefined",
6626 #endif
6627                    cimg::t_normal);
6628 
6629       std::fprintf(cimg::output(),"  > Support for C++11:      %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
6630                    cimg::t_bold,
6631                    cimg_use_cpp11?"Yes":"No",
6632                    cimg::t_normal,cimg::t_green,
6633                    (int)cimg_use_cpp11,
6634                    cimg::t_normal);
6635 
6636       std::fprintf(cimg::output(),"  > Using VT100 messages:   %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
6637                    cimg::t_bold,
6638 #ifdef cimg_use_vt100
6639                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6640 #else
6641                    "No",cimg::t_normal,cimg::t_green,"undefined",
6642 #endif
6643                    cimg::t_normal);
6644 
6645       std::fprintf(cimg::output(),"  > Display type:           %s%-13s%s %s('cimg_display'=%d)%s\n",
6646                    cimg::t_bold,
6647                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
6648                    cimg::t_normal,cimg::t_green,
6649                    (int)cimg_display,
6650                    cimg::t_normal);
6651 
6652 #if cimg_display==1
6653       std::fprintf(cimg::output(),"  > Using XShm for X11:     %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
6654                    cimg::t_bold,
6655 #ifdef cimg_use_xshm
6656                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6657 #else
6658                    "No",cimg::t_normal,cimg::t_green,"undefined",
6659 #endif
6660                    cimg::t_normal);
6661 
6662       std::fprintf(cimg::output(),"  > Using XRand for X11:    %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
6663                    cimg::t_bold,
6664 #ifdef cimg_use_xrandr
6665                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6666 #else
6667                    "No",cimg::t_normal,cimg::t_green,"undefined",
6668 #endif
6669                    cimg::t_normal);
6670 #endif
6671       std::fprintf(cimg::output(),"  > Using OpenMP:           %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
6672                    cimg::t_bold,
6673 #ifdef cimg_use_openmp
6674                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6675 #else
6676                    "No",cimg::t_normal,cimg::t_green,"undefined",
6677 #endif
6678                    cimg::t_normal);
6679       std::fprintf(cimg::output(),"  > Using PNG library:      %s%-13s%s %s('cimg_use_png' %s)%s\n",
6680                    cimg::t_bold,
6681 #ifdef cimg_use_png
6682                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6683 #else
6684                    "No",cimg::t_normal,cimg::t_green,"undefined",
6685 #endif
6686                    cimg::t_normal);
6687       std::fprintf(cimg::output(),"  > Using JPEG library:     %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
6688                    cimg::t_bold,
6689 #ifdef cimg_use_jpeg
6690                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6691 #else
6692                    "No",cimg::t_normal,cimg::t_green,"undefined",
6693 #endif
6694                    cimg::t_normal);
6695 
6696       std::fprintf(cimg::output(),"  > Using TIFF library:     %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
6697                    cimg::t_bold,
6698 #ifdef cimg_use_tiff
6699                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6700 #else
6701                    "No",cimg::t_normal,cimg::t_green,"undefined",
6702 #endif
6703                    cimg::t_normal);
6704 
6705       std::fprintf(cimg::output(),"  > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
6706                    cimg::t_bold,
6707 #ifdef cimg_use_magick
6708                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6709 #else
6710                    "No",cimg::t_normal,cimg::t_green,"undefined",
6711 #endif
6712                    cimg::t_normal);
6713 
6714       std::fprintf(cimg::output(),"  > Using FFTW3 library:    %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
6715                    cimg::t_bold,
6716 #ifdef cimg_use_fftw3
6717                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6718 #else
6719                    "No",cimg::t_normal,cimg::t_green,"undefined",
6720 #endif
6721                    cimg::t_normal);
6722 
6723       std::fprintf(cimg::output(),"  > Using LAPACK library:   %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
6724                    cimg::t_bold,
6725 #ifdef cimg_use_lapack
6726                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6727 #else
6728                    "No",cimg::t_normal,cimg::t_green,"undefined",
6729 #endif
6730                    cimg::t_normal);
6731 
6732       char *const tmp = new char[1024];
6733       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
6734       std::fprintf(cimg::output(),"  > Path of ImageMagick:    %s%-13s%s\n",
6735                    cimg::t_bold,
6736                    tmp,
6737                    cimg::t_normal);
6738 
6739       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
6740       std::fprintf(cimg::output(),"  > Path of GraphicsMagick: %s%-13s%s\n",
6741                    cimg::t_bold,
6742                    tmp,
6743                    cimg::t_normal);
6744 
6745       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
6746       std::fprintf(cimg::output(),"  > Path of 'medcon':       %s%-13s%s\n",
6747                    cimg::t_bold,
6748                    tmp,
6749                    cimg::t_normal);
6750 
6751       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
6752       std::fprintf(cimg::output(),"  > Temporary path:         %s%-13s%s\n",
6753                    cimg::t_bold,
6754                    tmp,
6755                    cimg::t_normal);
6756 
6757       std::fprintf(cimg::output(),"\n");
6758       delete[] tmp;
6759     }
6760 
6761     // Declare LAPACK function signatures if LAPACK support is enabled.
6762 #ifdef cimg_use_lapack
6763     template<typename T>
6764     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
6765       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
6766     }
6767 
6768     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
6769       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
6770     }
6771 
6772     template<typename T>
6773     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
6774       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
6775     }
6776 
6777     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
6778       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
6779     }
6780 
6781     template<typename T>
6782     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
6783                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
6784       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
6785     }
6786 
6787     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
6788                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
6789       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
6790     }
6791 
6792     template<typename T>
6793     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
6794       int one = 1;
6795       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
6796     }
6797 
6798     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
6799       int one = 1;
6800       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
6801     }
6802 
6803     template<typename T>
6804     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
6805       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
6806     }
6807 
6808     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
6809       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
6810     }
6811 
6812     template<typename T>
6813     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
6814 		      T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
6815       dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
6816     }
6817 
6818     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
6819 		      float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
6820       sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
6821     }
6822 
6823 #endif
6824 
6825     // End of the 'cimg' namespace
6826   }
6827 
6828   /*------------------------------------------------
6829    #
6830    #
6831    #   Definition of mathematical operators and
6832    #   external functions.
6833    #
6834    #
6835    -------------------------------------------------*/
6836 
6837 #define _cimg_create_ext_operators(typ) \
6838   template<typename T> \
6839   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
6840     return img + val; \
6841   } \
6842   template<typename T> \
6843   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
6844     typedef typename cimg::superset<T,typ>::type Tt; \
6845     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
6846   } \
6847   template<typename T> \
6848   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
6849     return img*val; \
6850   } \
6851   template<typename T> \
6852   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
6853     return val*img.get_invert(); \
6854   } \
6855   template<typename T> \
6856   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
6857     return img & val; \
6858   } \
6859   template<typename T> \
6860   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
6861     return img | val; \
6862   } \
6863   template<typename T> \
6864   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
6865     return img ^ val; \
6866   } \
6867   template<typename T> \
6868   inline bool operator==(const typ val, const CImg<T>& img) {   \
6869     return img == val; \
6870   } \
6871   template<typename T> \
6872   inline bool operator!=(const typ val, const CImg<T>& img) { \
6873     return img != val; \
6874   }
6875 
6876   _cimg_create_ext_operators(bool)
6877   _cimg_create_ext_operators(unsigned char)
6878   _cimg_create_ext_operators(char)
6879   _cimg_create_ext_operators(signed char)
6880   _cimg_create_ext_operators(unsigned short)
6881   _cimg_create_ext_operators(short)
6882   _cimg_create_ext_operators(unsigned int)
6883   _cimg_create_ext_operators(int)
6884   _cimg_create_ext_operators(cimg_uint64)
6885   _cimg_create_ext_operators(cimg_int64)
6886   _cimg_create_ext_operators(float)
6887   _cimg_create_ext_operators(double)
6888   _cimg_create_ext_operators(long double)
6889 
6890   template<typename T>
6891   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
6892     return img + expression;
6893   }
6894 
6895   template<typename T>
6896   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
6897     return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
6898   }
6899 
6900   template<typename T>
6901   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
6902     return img*expression;
6903   }
6904 
6905   template<typename T>
6906   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
6907     return expression*img.get_invert();
6908   }
6909 
6910   template<typename T>
6911   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
6912     return img & expression;
6913   }
6914 
6915   template<typename T>
6916   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
6917     return img | expression;
6918   }
6919 
6920   template<typename T>
6921   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
6922     return img ^ expression;
6923   }
6924 
6925   template<typename T>
6926   inline bool operator==(const char *const expression, const CImg<T>& img) {
6927     return img==expression;
6928   }
6929 
6930   template<typename T>
6931   inline bool operator!=(const char *const expression, const CImg<T>& img) {
6932     return img!=expression;
6933   }
6934 
6935   template<typename T>
6936   inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
6937     return instance.get_sqr();
6938   }
6939 
6940   template<typename T>
6941   inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
6942     return instance.get_sqrt();
6943   }
6944 
6945   template<typename T>
6946   inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
6947     return instance.get_exp();
6948   }
6949 
6950   template<typename T>
6951   inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
6952     return instance.get_log();
6953   }
6954 
6955   template<typename T>
6956   inline CImg<_cimg_Tfloat> log2(const CImg<T>& instance) {
6957     return instance.get_log2();
6958   }
6959 
6960   template<typename T>
6961   inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
6962     return instance.get_log10();
6963   }
6964 
6965   template<typename T>
6966   inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
6967     return instance.get_abs();
6968   }
6969 
6970   template<typename T>
6971   inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
6972     return instance.get_sign();
6973   }
6974 
6975   template<typename T>
6976   inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
6977     return instance.get_cos();
6978   }
6979 
6980   template<typename T>
6981   inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
6982     return instance.get_sin();
6983   }
6984 
6985   template<typename T>
6986   inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
6987     return instance.get_sinc();
6988   }
6989 
6990   template<typename T>
6991   inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
6992     return instance.get_tan();
6993   }
6994 
6995   template<typename T>
6996   inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
6997     return instance.get_acos();
6998   }
6999 
7000   template<typename T>
7001   inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
7002     return instance.get_asin();
7003   }
7004 
7005   template<typename T>
7006   inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
7007     return instance.get_atan();
7008   }
7009 
7010   template<typename T>
7011   inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
7012     return instance.get_cosh();
7013   }
7014 
7015   template<typename T>
7016   inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
7017     return instance.get_sinh();
7018   }
7019 
7020   template<typename T>
7021   inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
7022     return instance.get_tanh();
7023   }
7024 
7025   template<typename T>
7026   inline CImg<T> transpose(const CImg<T>& instance) {
7027     return instance.get_transpose();
7028   }
7029 
7030   template<typename T>
7031   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
7032     return instance.get_invert();
7033   }
7034 
7035   template<typename T>
7036   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
7037     return instance.get_pseudoinvert();
7038   }
7039 
7040   /*-----------------------------------
7041    #
7042    # Define the CImgDisplay structure
7043    #
7044    ----------------------------------*/
7045   //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
7046   /**
7047      CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
7048      (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
7049      If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
7050      a minimal mode where warning messages will be outputed each time the program is trying to call one of the
7051      CImgDisplay method.
7052 
7053      The configuration variable \c cimg_display tells about the graphic library used.
7054      It is set automatically by \CImg when one of these graphic libraries has been detected.
7055      But, you can override its value if necessary. Valid choices are:
7056      - 0: Disable display capabilities.
7057      - 1: Use \b X-Window (X11) library.
7058      - 2: Use \b GDI32 library.
7059 
7060      Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
7061   **/
7062   struct CImgDisplay {
7063     cimg_ulong _timer, _fps_frames, _fps_timer;
7064     unsigned int _width, _height, _normalization;
7065     float _fps_fps, _min, _max;
7066     bool _is_fullscreen;
7067     char *_title;
7068     unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
7069     int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
7070     bool _is_closed, _is_resized, _is_moved, _is_event,
7071       _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
7072       _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
7073       _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
7074       _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
7075       _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
7076       _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
7077       _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
7078       _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
7079       _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
7080       _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
7081       _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
7082       _is_keyPADMUL, _is_keyPADDIV;
7083 
7084     //@}
7085     //---------------------------
7086     //
7087     //! \name Plugins
7088     //@{
7089     //---------------------------
7090 
7091 #ifdef cimgdisplay_plugin
7092 #include cimgdisplay_plugin
7093 #endif
7094 #ifdef cimgdisplay_plugin1
7095 #include cimgdisplay_plugin1
7096 #endif
7097 #ifdef cimgdisplay_plugin2
7098 #include cimgdisplay_plugin2
7099 #endif
7100 #ifdef cimgdisplay_plugin3
7101 #include cimgdisplay_plugin3
7102 #endif
7103 #ifdef cimgdisplay_plugin4
7104 #include cimgdisplay_plugin4
7105 #endif
7106 #ifdef cimgdisplay_plugin5
7107 #include cimgdisplay_plugin5
7108 #endif
7109 #ifdef cimgdisplay_plugin6
7110 #include cimgdisplay_plugin6
7111 #endif
7112 #ifdef cimgdisplay_plugin7
7113 #include cimgdisplay_plugin7
7114 #endif
7115 #ifdef cimgdisplay_plugin8
7116 #include cimgdisplay_plugin8
7117 #endif
7118 
7119     //@}
7120     //--------------------------------------------------------
7121     //
7122     //! \name Constructors / Destructor / Instance Management
7123     //@{
7124     //--------------------------------------------------------
7125 
7126     //! Destructor.
7127     /**
7128        \note If the associated window is visible on the screen, it is closed by the call to the destructor.
7129     **/
7130     ~CImgDisplay() {
7131       assign();
7132       delete[] _keys;
7133       delete[] _released_keys;
7134     }
7135 
7136     //! Construct an empty display.
7137     /**
7138        \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
7139        display of valid data is performed.
7140        \par Example
7141        \code
7142        CImgDisplay disp;  // Does actually nothing.
7143        ...
7144        disp.display(img); // Construct new window and display image in it.
7145        \endcode
7146     **/
7147     CImgDisplay():
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();
7157     }
7158 
7159     //! Construct a display with specified dimensions.
7160     /** \param width Window width.
7161         \param height Window height.
7162         \param title Window title.
7163         \param normalization Normalization type
7164         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
7165         \param is_fullscreen Tells if fullscreen mode is enabled.
7166         \param is_closed Tells if associated window is initially visible or not.
7167         \note A black background is initially displayed on the associated window.
7168     **/
7169     CImgDisplay(const unsigned int width, const unsigned int height,
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(width,height,title,normalization,is_fullscreen,is_closed);
7181     }
7182 
7183     //! Construct a display from an image.
7184     /** \param img Image used as a model to create the window.
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 The pixels of the input image are initially displayed on the associated window.
7191     **/
7192     template<typename T>
7193     explicit CImgDisplay(const CImg<T>& img,
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(img,title,normalization,is_fullscreen,is_closed);
7205     }
7206 
7207     //! Construct a display from an image list.
7208     /** \param list The images list to display.
7209         \param title Window title.
7210         \param normalization Normalization type
7211         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
7212         \param is_fullscreen Tells if fullscreen mode is enabled.
7213         \param is_closed Tells if associated window is initially visible or not.
7214         \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
7215     **/
7216     template<typename T>
7217     explicit CImgDisplay(const CImgList<T>& list,
7218                          const char *const title=0, const unsigned int normalization=3,
7219                          const bool is_fullscreen=false, const bool is_closed=false):
7220       _width(0),_height(0),_normalization(0),
7221       _min(0),_max(0),
7222       _is_fullscreen(false),
7223       _title(0),
7224       _window_width(0),_window_height(0),_button(0),
7225       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7226       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7227       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7228       assign(list,title,normalization,is_fullscreen,is_closed);
7229     }
7230 
7231     //! Construct a display as a copy of an existing one.
7232     /**
7233         \param disp Display instance to copy.
7234         \note The pixel buffer of the input window is initially displayed on the associated window.
7235     **/
7236     CImgDisplay(const CImgDisplay& disp):
7237       _width(0),_height(0),_normalization(0),
7238       _min(0),_max(0),
7239       _is_fullscreen(false),
7240       _title(0),
7241       _window_width(0),_window_height(0),_button(0),
7242       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7243       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7244       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7245       assign(disp);
7246     }
7247 
7248     //! Take a screenshot.
7249     /**
7250        \param[out] img Output screenshot. Can be empty on input
7251     **/
7252     template<typename T>
7253     static void screenshot(CImg<T>& img) {
7254       return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
7255     }
7256 
7257 #if cimg_display==0
7258 
7259     static void _no_display_exception() {
7260       throw CImgDisplayException("CImgDisplay(): No display available.");
7261     }
7262 
7263     //! Destructor - Empty constructor \inplace.
7264     /**
7265        \note Replace the current instance by an empty display.
7266     **/
7267     CImgDisplay& assign() {
7268       return flush();
7269     }
7270 
7271     //! Construct a display with specified dimensions \inplace.
7272     /**
7273     **/
7274     CImgDisplay& assign(const unsigned int width, const unsigned int height,
7275                         const char *const title=0, const unsigned int normalization=3,
7276                         const bool is_fullscreen=false, const bool is_closed=false) {
7277       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
7278       _no_display_exception();
7279       return assign();
7280     }
7281 
7282     //! Construct a display from an image \inplace.
7283     /**
7284     **/
7285     template<typename T>
7286     CImgDisplay& assign(const CImg<T>& img,
7287                         const char *const title=0, const unsigned int normalization=3,
7288                         const bool is_fullscreen=false, const bool is_closed=false) {
7289       _no_display_exception();
7290       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
7291     }
7292 
7293     //! Construct a display from an image list \inplace.
7294     /**
7295     **/
7296     template<typename T>
7297     CImgDisplay& assign(const CImgList<T>& list,
7298                         const char *const title=0, const unsigned int normalization=3,
7299                         const bool is_fullscreen=false, const bool is_closed=false) {
7300       _no_display_exception();
7301       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
7302     }
7303 
7304     //! Construct a display as a copy of another one \inplace.
7305     /**
7306     **/
7307     CImgDisplay& assign(const CImgDisplay &disp) {
7308       _no_display_exception();
7309       return assign(disp._width,disp._height);
7310     }
7311 
7312 #endif
7313 
7314     //! Return a reference to an empty display.
7315     /**
7316        \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
7317        must have a default value.
7318        \par Example
7319        \code
7320        void foo(CImgDisplay& disp=CImgDisplay::empty());
7321        \endcode
7322     **/
7323     static CImgDisplay& empty() {
7324       static CImgDisplay _empty;
7325       return _empty.assign();
7326     }
7327 
7328     //! Return a reference to an empty display \const.
7329     static const CImgDisplay& const_empty() {
7330       static const CImgDisplay _empty;
7331       return _empty;
7332     }
7333 
7334 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false), \
7335                                  CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
7336     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
7337                                    const int dmin, const int dmax,const bool return_y) {
7338       const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
7339       unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
7340       const unsigned int
7341         sw = (unsigned int)CImgDisplay::screen_width(),
7342         sh = (unsigned int)CImgDisplay::screen_height(),
7343         mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
7344         mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
7345         Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
7346         Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
7347       if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0); nw = mw; }
7348       if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0); nh = mh; }
7349       if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; }
7350       if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; }
7351       if (nw<mw) nw = mw;
7352       if (nh<mh) nh = mh;
7353       return return_y?nh:nw;
7354     }
7355 
7356     //@}
7357     //------------------------------------------
7358     //
7359     //! \name Overloaded Operators
7360     //@{
7361     //------------------------------------------
7362 
7363     //! Display image on associated window.
7364     /**
7365        \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
7366     **/
7367     template<typename t>
7368     CImgDisplay& operator=(const CImg<t>& img) {
7369       return display(img);
7370     }
7371 
7372     //! Display list of images on associated window.
7373     /**
7374        \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
7375     **/
7376     template<typename t>
7377     CImgDisplay& operator=(const CImgList<t>& list) {
7378       return display(list);
7379     }
7380 
7381     //! Construct a display as a copy of another one \inplace.
7382     /**
7383        \note Equivalent to assign(const CImgDisplay&).
7384      **/
7385     CImgDisplay& operator=(const CImgDisplay& disp) {
7386       return assign(disp);
7387     }
7388 
7389     //! Return \c false if display is empty, \c true otherwise.
7390     /**
7391        \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
7392     **/
7393     operator bool() const {
7394       return !is_empty();
7395     }
7396 
7397     //@}
7398     //------------------------------------------
7399     //
7400     //! \name Instance Checking
7401     //@{
7402     //------------------------------------------
7403 
7404     //! Return \c true if display is empty, \c false otherwise.
7405     /**
7406     **/
7407     bool is_empty() const {
7408       return !(_width && _height);
7409     }
7410 
7411     //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
7412     /**
7413        \note
7414        - When a user physically closes the associated window, the display is set to closed.
7415        - A closed display is not destroyed. Its associated window can be show again on the screen using show().
7416     **/
7417     bool is_closed() const {
7418       return _is_closed;
7419     }
7420 
7421     //! Return \c true if associated window has been resized on the screen, \c false otherwise.
7422     /**
7423     **/
7424     bool is_resized() const {
7425       return _is_resized;
7426     }
7427 
7428     //! Return \c true if associated window has been moved on the screen, \c false otherwise.
7429     /**
7430     **/
7431     bool is_moved() const {
7432       return _is_moved;
7433     }
7434 
7435     //! Return \c true if any event has occured on the associated window, \c false otherwise.
7436     /**
7437     **/
7438     bool is_event() const {
7439       return _is_event;
7440     }
7441 
7442     //! Return \c true if current display is in fullscreen mode, \c false otherwise.
7443     /**
7444     **/
7445     bool is_fullscreen() const {
7446       return _is_fullscreen;
7447     }
7448 
7449     //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
7450     /**
7451        \note The methods below do the same only for specific keys.
7452     **/
7453     bool is_key() const {
7454       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
7455         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
7456         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
7457         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
7458         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
7459         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
7460         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
7461         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
7462         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
7463         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
7464         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
7465         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
7466         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
7467         _is_keyK || _is_keyL || _is_keyENTER ||
7468         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
7469         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
7470         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
7471         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
7472         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
7473         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
7474         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
7475         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
7476         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
7477         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
7478         _is_keyPADMUL || _is_keyPADDIV;
7479     }
7480 
7481     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
7482     /**
7483        \param keycode Keycode to test.
7484        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7485        your code stay portable (see cimg::keyESC).
7486        \par Example
7487        \code
7488        CImgDisplay disp(400,400);
7489        while (!disp.is_closed()) {
7490          if (disp.key(cimg::keyTAB)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
7491          disp.wait();
7492        }
7493        \endcode
7494     **/
7495     bool is_key(const unsigned int keycode) const {
7496 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
7497       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
7498       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
7499       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
7500       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
7501       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
7502       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
7503       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
7504       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
7505       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
7506       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
7507       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
7508       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
7509       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
7510       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
7511       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
7512       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
7513       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
7514       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
7515       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
7516       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
7517       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
7518       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
7519       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
7520       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
7521       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
7522       return false;
7523     }
7524 
7525     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
7526     /**
7527        \param keycode C-string containing the keycode label of the key to test.
7528        \note Use it when the key you want to test can be dynamically set by the user.
7529        \par Example
7530        \code
7531        CImgDisplay disp(400,400);
7532        const char *const keycode = "TAB";
7533        while (!disp.is_closed()) {
7534          if (disp.is_key(keycode)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
7535          disp.wait();
7536        }
7537        \endcode
7538     **/
7539     bool& is_key(const char *const keycode) {
7540       static bool f = false;
7541       f = false;
7542 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
7543       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
7544       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
7545       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
7546       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
7547       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
7548       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
7549       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
7550       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
7551       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
7552       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
7553       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
7554       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
7555       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
7556       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
7557       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
7558       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
7559       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
7560       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
7561       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
7562       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
7563       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
7564       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
7565       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
7566       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
7567       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
7568       return f;
7569     }
7570 
7571     //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
7572     /**
7573        \param keycodes_sequence Buffer of keycodes to test.
7574        \param length Number of keys in the \c keycodes_sequence buffer.
7575        \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
7576        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7577        your code stay portable (see cimg::keyESC).
7578        \par Example
7579        \code
7580        CImgDisplay disp(400,400);
7581        const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
7582        while (!disp.is_closed()) {
7583          if (disp.is_key_sequence(key_seq,2)) { ... }  // Test for the 'CTRL+D' keyboard event.
7584          disp.wait();
7585        }
7586        \endcode
7587     **/
7588     bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
7589                          const bool remove_sequence=false) {
7590       if (keycodes_sequence && length) {
7591         const unsigned int
7592           *const ps_end = keycodes_sequence + length - 1,
7593           *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
7594           k = *ps_end;
7595         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
7596           if (*(pk++)==k) {
7597             bool res = true;
7598             const unsigned int *ps = ps_end, *pk2 = pk;
7599             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
7600             if (res) {
7601               if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
7602               return true;
7603             }
7604           }
7605         }
7606       }
7607       return false;
7608     }
7609 
7610 #define _cimg_iskey_def(k) \
7611     bool is_key##k() const { \
7612       return _is_key##k; \
7613     }
7614 
7615     //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
7616     /**
7617        \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
7618     **/
7619     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
7620     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
7621     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
7622     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
7623     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
7624     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
7625     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
7626     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
7627     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
7628     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
7629     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
7630     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
7631     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
7632     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
7633     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
7634     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
7635     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
7636     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
7637     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
7638     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
7639     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
7640     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
7641     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
7642     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
7643     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
7644 
7645     //@}
7646     //------------------------------------------
7647     //
7648     //! \name Instance Characteristics
7649     //@{
7650     //------------------------------------------
7651 
7652 #if cimg_display==0
7653 
7654     //! Return width of the screen (current resolution along the X-axis).
7655     /**
7656     **/
7657     static int screen_width() {
7658       _no_display_exception();
7659       return 0;
7660     }
7661 
7662     //! Return height of the screen (current resolution along the Y-axis).
7663     /**
7664     **/
7665     static int screen_height() {
7666       _no_display_exception();
7667       return 0;
7668     }
7669 
7670 #endif
7671 
7672     //! Return display width.
7673     /**
7674        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
7675        may be different from the actual width of the associated window.
7676     **/
7677     int width() const {
7678       return (int)_width;
7679     }
7680 
7681     //! Return display height.
7682     /**
7683        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
7684        may be different from the actual height of the associated window.
7685     **/
7686     int height() const {
7687       return (int)_height;
7688     }
7689 
7690     //! Return normalization type of the display.
7691     /**
7692        The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
7693        correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
7694        If the range of values of the data to display is different, a normalization may be required for displaying
7695        the data in a correct way. The normalization type can be one of:
7696        - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
7697        CImgDisplay instance have values in range <tt>[0,255]</tt>.
7698        - \c 1: Value normalization is always performed (this is the default behavior).
7699        Before displaying an input image, its values will be (virtually) stretched
7700        in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
7701        Use this mode for images whose minimum and maximum values are not prescribed to known values
7702        (e.g. float-valued images).
7703        Note that when normalized versions of images are computed for display purposes, the actual values of these
7704        images are not modified.
7705        - \c 2: Value normalization is performed once (on the first image display), then the same normalization
7706        coefficients are kept for next displayed frames.
7707        - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
7708        the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
7709        for <tt>unsigned char</tt>).
7710        For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
7711        data instead.
7712     **/
7713     unsigned int normalization() const {
7714       return _normalization;
7715     }
7716 
7717     //! Return title of the associated window as a C-string.
7718     /**
7719        \note Window title may be not visible, depending on the used window manager or if the current display is
7720        in fullscreen mode.
7721     **/
7722     const char *title() const {
7723       return _title?_title:"";
7724     }
7725 
7726     //! Return width of the associated window.
7727     /**
7728        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
7729        may be different from the actual width of the associated window.
7730     **/
7731     int window_width() const {
7732       return (int)_window_width;
7733     }
7734 
7735     //! Return height of the associated window.
7736     /**
7737        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
7738        may be different from the actual height of the associated window.
7739     **/
7740     int window_height() const {
7741       return (int)_window_height;
7742     }
7743 
7744     //! Return X-coordinate of the associated window.
7745     /**
7746        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
7747     **/
7748     int window_x() const {
7749       return _window_x;
7750     }
7751 
7752     //! Return Y-coordinate of the associated window.
7753     /**
7754        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
7755     **/
7756     int window_y() const {
7757       return _window_y;
7758     }
7759 
7760     //! Return X-coordinate of the mouse pointer.
7761     /**
7762        \note
7763        - If the mouse pointer is outside window area, \c -1 is returned.
7764        - Otherwise, the returned value is in the range [0,width()-1].
7765     **/
7766     int mouse_x() const {
7767       return _mouse_x;
7768     }
7769 
7770     //! Return Y-coordinate of the mouse pointer.
7771     /**
7772        \note
7773        - If the mouse pointer is outside window area, \c -1 is returned.
7774        - Otherwise, the returned value is in the range [0,height()-1].
7775     **/
7776     int mouse_y() const {
7777       return _mouse_y;
7778     }
7779 
7780     //! Return current state of the mouse buttons.
7781     /**
7782        \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
7783        value is set:
7784        - bit \c 0 (value \c 0x1): State of the left mouse button.
7785        - bit \c 1 (value \c 0x2): State of the right mouse button.
7786        - bit \c 2 (value \c 0x4): State of the middle mouse button.
7787 
7788        Several bits can be activated if more than one button are pressed at the same time.
7789        \par Example
7790        \code
7791        CImgDisplay disp(400,400);
7792        while (!disp.is_closed()) {
7793          if (disp.button()&1) { // Left button clicked.
7794            ...
7795          }
7796          if (disp.button()&2) { // Right button clicked.
7797            ...
7798          }
7799          if (disp.button()&4) { // Middle button clicked.
7800            ...
7801          }
7802          disp.wait();
7803        }
7804        \endcode
7805     **/
7806     unsigned int button() const {
7807       return _button;
7808     }
7809 
7810     //! Return current state of the mouse wheel.
7811     /**
7812        \note
7813        - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
7814        forward or backward.
7815        - Scrolling the wheel forward add \c 1 to the wheel value.
7816        - Scrolling the wheel backward substract \c 1 to the wheel value.
7817        - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
7818        or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
7819        the wheel counter when an action has been performed regarding the current wheel value.
7820        Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
7821        (as many in forward as in backward directions).
7822        \par Example
7823        \code
7824        CImgDisplay disp(400,400);
7825        while (!disp.is_closed()) {
7826          if (disp.wheel()) {
7827            int counter = disp.wheel();  // Read the state of the mouse wheel.
7828            ...                          // Do what you want with 'counter'.
7829            disp.set_wheel();            // Reset the wheel value to 0.
7830          }
7831          disp.wait();
7832        }
7833        \endcode
7834     **/
7835     int wheel() const {
7836       return _wheel;
7837     }
7838 
7839     //! Return one entry from the pressed keys history.
7840     /**
7841        \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry).
7842        \return Keycode of a pressed key or \c 0 for a released key.
7843        \note
7844        - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
7845        its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
7846        This means that up to the 64 last pressed keys may be read from the pressed keys history.
7847        When a new value is stored, the pressed keys history is shifted so that the latest entry is always
7848        stored at position \c 0.
7849        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7850        your code stay portable (see cimg::keyESC).
7851     **/
7852     unsigned int key(const unsigned int pos=0) const {
7853       return pos<128?_keys[pos]:0;
7854     }
7855 
7856     //! Return one entry from the released keys history.
7857     /**
7858        \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry).
7859        \return Keycode of a released key or \c 0 for a pressed key.
7860        \note
7861        - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
7862        its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
7863        This means that up to the 64 last released keys may be read from the released keys history.
7864        When a new value is stored, the released keys history is shifted so that the latest entry is always
7865        stored at position \c 0.
7866        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7867        your code stay portable (see cimg::keyESC).
7868     **/
7869     unsigned int released_key(const unsigned int pos=0) const {
7870       return pos<128?_released_keys[pos]:0;
7871     }
7872 
7873     //! Return keycode corresponding to the specified string.
7874     /**
7875        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7876        your code stay portable (see cimg::keyESC).
7877        \par Example
7878        \code
7879        const unsigned int keyTAB = CImgDisplay::keycode("TAB");  // Return cimg::keyTAB.
7880        \endcode
7881     **/
7882     static unsigned int keycode(const char *const keycode) {
7883 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
7884       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
7885       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
7886       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
7887       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
7888       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
7889       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
7890       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
7891       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
7892       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
7893       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
7894       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
7895       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
7896       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
7897       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
7898       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
7899       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
7900       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
7901       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
7902       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
7903       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
7904       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
7905       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
7906       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
7907       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
7908       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
7909       return 0;
7910     }
7911 
7912     //! Return the current refresh rate, in frames per second.
7913     /**
7914        \note Returns a significant value when the current instance is used to display successive frames.
7915        It measures the delay between successive calls to frames_per_second().
7916     **/
7917     float frames_per_second() {
7918       if (!_fps_timer) _fps_timer = cimg::time();
7919       const float delta = (cimg::time() - _fps_timer)/1000.0f;
7920       ++_fps_frames;
7921       if (delta>=1) {
7922         _fps_fps = _fps_frames/delta;
7923         _fps_frames = 0;
7924         _fps_timer = cimg::time();
7925       }
7926       return _fps_fps;
7927     }
7928 
7929     //@}
7930     //---------------------------------------
7931     //
7932     //! \name Window Manipulation
7933     //@{
7934     //---------------------------------------
7935 
7936 #if cimg_display==0
7937 
7938     //! Display image on associated window.
7939     /**
7940        \param img Input image to display.
7941        \note This method returns immediately.
7942     **/
7943     template<typename T>
7944     CImgDisplay& display(const CImg<T>& img) {
7945       return assign(img);
7946     }
7947 
7948 #endif
7949 
7950     //! Display list of images on associated window.
7951     /**
7952        \param list List of images to display.
7953        \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
7954        \param align Relative position of aligned images when displaying lists with images of different sizes
7955        (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
7956        \note This method returns immediately.
7957     **/
7958     template<typename T>
7959     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
7960       if (list._width==1) {
7961         const CImg<T>& img = list[0];
7962         if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
7963       }
7964       CImgList<typename CImg<T>::ucharT> visu(list._width);
7965       unsigned int dims = 0;
7966       cimglist_for(list,l) {
7967         const CImg<T>& img = list._data[l];
7968         img.__get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
7969                          (img._depth - 1)/2).move_to(visu[l]);
7970         dims = std::max(dims,visu[l]._spectrum);
7971       }
7972       cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
7973       visu.get_append(axis,align).display(*this);
7974       return *this;
7975     }
7976 
7977 #if cimg_display==0
7978 
7979     //! Show (closed) associated window on the screen.
7980     /**
7981        \note
7982        - Force the associated window of a display to be visible on the screen, even if it has been closed before.
7983        - Using show() on a visible display does nothing.
7984     **/
7985     CImgDisplay& show() {
7986       return assign();
7987     }
7988 
7989     //! Close (visible) associated window and make it disappear from the screen.
7990     /**
7991        \note
7992        - A closed display only means the associated window is not visible anymore. This does not mean the display has
7993        been destroyed.
7994        Use show() to make the associated window reappear.
7995        - Using close() on a closed display does nothing.
7996     **/
7997     CImgDisplay& close() {
7998       return assign();
7999     }
8000 
8001     //! Move associated window to a new location.
8002     /**
8003        \param pos_x X-coordinate of the new window location.
8004        \param pos_y Y-coordinate of the new window location.
8005        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
8006        nevertheless).
8007     **/
8008     CImgDisplay& move(const int pos_x, const int pos_y) {
8009       return assign(pos_x,pos_y);
8010     }
8011 
8012 #endif
8013 
8014     //! Resize display to the size of the associated window.
8015     /**
8016        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
8017        \note
8018        - Calling this method ensures that width() and window_width() become equal, as well as height() and
8019        window_height().
8020        - The associated window is also resized to specified dimensions.
8021     **/
8022     CImgDisplay& resize(const bool force_redraw=true) {
8023       resize(window_width(),window_height(),force_redraw);
8024       return *this;
8025     }
8026 
8027 #if cimg_display==0
8028 
8029     //! Resize display to the specified size.
8030     /**
8031        \param width Requested display width.
8032        \param height Requested display height.
8033        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
8034        \note The associated window is also resized to specified dimensions.
8035     **/
8036     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
8037       return assign(width,height,0,3,force_redraw);
8038     }
8039 
8040 #endif
8041 
8042     //! Resize display to the size of an input image.
8043     /**
8044        \param img Input image to take size from.
8045        \param force_redraw Tells if the previous window content must be resized and updated as well.
8046        \note
8047        - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
8048        <tt>img.height()</tt>.
8049        - The associated window is also resized to specified dimensions.
8050     **/
8051     template<typename T>
8052     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
8053       return resize(img._width,img._height,force_redraw);
8054     }
8055 
8056     //! Resize display to the size of another CImgDisplay instance.
8057     /**
8058        \param disp Input display to take size from.
8059        \param force_redraw Tells if the previous window content must be resized and updated as well.
8060        \note
8061        - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
8062        <tt>disp.height()</tt>.
8063        - The associated window is also resized to specified dimensions.
8064     **/
8065     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
8066       return resize(disp.width(),disp.height(),force_redraw);
8067     }
8068 
8069     // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
8070     template<typename t, typename T>
8071     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
8072                                t *ptrd, const unsigned int wd, const unsigned int hd) {
8073       unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd + 1], *poffx, *poffy;
8074       float s, curr, old;
8075       s = (float)ws/wd;
8076       poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) {
8077         old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old;
8078       }
8079       s = (float)hs/hd;
8080       poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) {
8081         old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old);
8082       }
8083       *poffy = 0;
8084       poffy = offy;
8085       for (unsigned int y = 0; y<hd; ) {
8086         const T *ptr = ptrs;
8087         poffx = offx;
8088         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
8089         ++y;
8090         unsigned int dy = *(poffy++);
8091         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
8092         ptrs+=dy;
8093       }
8094       delete[] offx; delete[] offy;
8095     }
8096 
8097     //! Set normalization type.
8098     /**
8099        \param normalization New normalization mode.
8100     **/
8101     CImgDisplay& set_normalization(const unsigned int normalization) {
8102       _normalization = normalization;
8103       _min = _max = 0;
8104       return *this;
8105     }
8106 
8107 #if cimg_display==0
8108 
8109     //! Set title of the associated window.
8110     /**
8111        \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
8112        \warning As the first argument is a format string, it is highly recommended to write
8113        \code
8114        disp.set_title("%s",window_title);
8115        \endcode
8116        instead of
8117        \code
8118        disp.set_title(window_title);
8119        \endcode
8120        if \c window_title can be arbitrary, to prevent nasty memory access.
8121     **/
8122     CImgDisplay& set_title(const char *const format, ...) {
8123       return assign(0,0,format);
8124     }
8125 
8126 #endif
8127 
8128     //! Enable or disable fullscreen mode.
8129     /**
8130        \param is_fullscreen Tells is the fullscreen mode must be activated or not.
8131        \param force_redraw Tells if the previous window content must be displayed as well.
8132        \note
8133        - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
8134        current display is not modified.
8135        - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
8136        as possible.
8137        For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
8138        resolution change (requires the X11 extensions to be enabled).
8139     **/
8140     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
8141       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
8142       return toggle_fullscreen(force_redraw);
8143     }
8144 
8145 #if cimg_display==0
8146 
8147     //! Toggle fullscreen mode.
8148     /**
8149        \param force_redraw Tells if the previous window content must be displayed as well.
8150        \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
8151     **/
8152     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8153       return assign(_width,_height,0,3,force_redraw);
8154     }
8155 
8156     //! Show mouse pointer.
8157     /**
8158        \note Depending on the window manager behavior, this method may not succeed
8159        (no exceptions are thrown nevertheless).
8160     **/
8161     CImgDisplay& show_mouse() {
8162       return assign();
8163     }
8164 
8165     //! Hide mouse pointer.
8166     /**
8167        \note Depending on the window manager behavior, this method may not succeed
8168        (no exceptions are thrown nevertheless).
8169     **/
8170     CImgDisplay& hide_mouse() {
8171       return assign();
8172     }
8173 
8174     //! Move mouse pointer to a specified location.
8175     /**
8176        \note Depending on the window manager behavior, this method may not succeed
8177        (no exceptions are thrown nevertheless).
8178     **/
8179     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
8180       return assign(pos_x,pos_y);
8181     }
8182 
8183 #endif
8184 
8185     //! Simulate a mouse button release event.
8186     /**
8187        \note All mouse buttons are considered released at the same time.
8188     **/
8189     CImgDisplay& set_button() {
8190       _button = 0;
8191       _is_event = true;
8192 #if cimg_display==1
8193       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8194 #elif cimg_display==2
8195       SetEvent(cimg::Win32_attr().wait_event);
8196 #endif
8197       return *this;
8198     }
8199 
8200     //! Simulate a mouse button press or release event.
8201     /**
8202        \param button Buttons event code, where each button is associated to a single bit.
8203        \param is_pressed Tells if the mouse button is considered as pressed or released.
8204     **/
8205     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
8206       const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
8207       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
8208       _is_event = buttoncode?true:false;
8209       if (buttoncode) {
8210 #if cimg_display==1
8211         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8212 #elif cimg_display==2
8213         SetEvent(cimg::Win32_attr().wait_event);
8214 #endif
8215       }
8216       return *this;
8217     }
8218 
8219     //! Flush all mouse wheel events.
8220     /**
8221        \note Make wheel() to return \c 0, if called afterwards.
8222     **/
8223     CImgDisplay& set_wheel() {
8224       _wheel = 0;
8225       _is_event = true;
8226 #if cimg_display==1
8227       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8228 #elif cimg_display==2
8229       SetEvent(cimg::Win32_attr().wait_event);
8230 #endif
8231       return *this;
8232     }
8233 
8234     //! Simulate a wheel event.
8235     /**
8236        \param amplitude Amplitude of the wheel scrolling to simulate.
8237        \note Make wheel() to return \c amplitude, if called afterwards.
8238     **/
8239     CImgDisplay& set_wheel(const int amplitude) {
8240       _wheel+=amplitude;
8241       _is_event = amplitude?true:false;
8242       if (amplitude) {
8243 #if cimg_display==1
8244         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8245 #elif cimg_display==2
8246         SetEvent(cimg::Win32_attr().wait_event);
8247 #endif
8248       }
8249       return *this;
8250     }
8251 
8252     //! Flush all key events.
8253     /**
8254        \note Make key() to return \c 0, if called afterwards.
8255     **/
8256     CImgDisplay& set_key() {
8257       std::memset((void*)_keys,0,128*sizeof(unsigned int));
8258       std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
8259       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
8260         _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
8261         _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
8262         _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
8263         _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
8264         _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
8265         _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
8266         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
8267         _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
8268         _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
8269         _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
8270         _is_keyPADDIV = false;
8271       _is_event = true;
8272 #if cimg_display==1
8273       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8274 #elif cimg_display==2
8275       SetEvent(cimg::Win32_attr().wait_event);
8276 #endif
8277       return *this;
8278     }
8279 
8280     //! Simulate a keyboard press/release event.
8281     /**
8282        \param keycode Keycode of the associated key.
8283        \param is_pressed Tells if the key is considered as pressed or released.
8284        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8285        your code stay portable (see cimg::keyESC).
8286     **/
8287     CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
8288 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
8289       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
8290       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
8291       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
8292       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
8293       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
8294       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
8295       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
8296       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
8297       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
8298       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
8299       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
8300       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
8301       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
8302       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
8303       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
8304       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
8305       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
8306       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
8307       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
8308       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
8309       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
8310       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
8311       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
8312       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
8313       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
8314       if (is_pressed) {
8315         if (*_keys)
8316           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
8317         *_keys = keycode;
8318         if (*_released_keys) {
8319           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
8320           *_released_keys = 0;
8321         }
8322       } else {
8323         if (*_keys) {
8324           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
8325           *_keys = 0;
8326         }
8327         if (*_released_keys)
8328           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
8329         *_released_keys = keycode;
8330       }
8331       _is_event = keycode?true:false;
8332       if (keycode) {
8333 #if cimg_display==1
8334         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8335 #elif cimg_display==2
8336         SetEvent(cimg::Win32_attr().wait_event);
8337 #endif
8338       }
8339       return *this;
8340     }
8341 
8342     //! Flush all display events.
8343     /**
8344        \note Remove all passed events from the current display.
8345     **/
8346     CImgDisplay& flush() {
8347       set_key().set_button().set_wheel();
8348       _is_resized = _is_moved = _is_event = false;
8349       _fps_timer = _fps_frames = _timer = 0;
8350       _fps_fps = 0;
8351       return *this;
8352     }
8353 
8354     //! Wait for any user event occuring on the current display.
8355     CImgDisplay& wait() {
8356       wait(*this);
8357       return *this;
8358     }
8359 
8360     //! Wait for a given number of milliseconds since the last call to wait().
8361     /**
8362        \param milliseconds Number of milliseconds to wait for.
8363        \note Similar to cimg::wait().
8364     **/
8365     CImgDisplay& wait(const unsigned int milliseconds) {
8366       cimg::_wait(milliseconds,_timer);
8367       return *this;
8368     }
8369 
8370     //! Wait for any event occuring on the display \c disp1.
8371     static void wait(CImgDisplay& disp1) {
8372       disp1._is_event = false;
8373       while (!disp1._is_closed && !disp1._is_event) wait_all();
8374     }
8375 
8376     //! Wait for any event occuring either on the display \c disp1 or \c disp2.
8377     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
8378       disp1._is_event = disp2._is_event = false;
8379       while ((!disp1._is_closed || !disp2._is_closed) &&
8380              !disp1._is_event && !disp2._is_event) wait_all();
8381     }
8382 
8383     //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
8384     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
8385       disp1._is_event = disp2._is_event = disp3._is_event = false;
8386       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
8387              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
8388     }
8389 
8390     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
8391     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
8392       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
8393       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
8394              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
8395     }
8396 
8397     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
8398     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
8399                      CImgDisplay& disp5) {
8400       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
8401       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
8402              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
8403         wait_all();
8404     }
8405 
8406     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
8407     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8408                      CImgDisplay& disp6) {
8409       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8410         disp6._is_event = false;
8411       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8412               !disp6._is_closed) &&
8413              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8414              !disp6._is_event) wait_all();
8415     }
8416 
8417     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
8418     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8419                      CImgDisplay& disp6, CImgDisplay& disp7) {
8420       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8421         disp6._is_event = disp7._is_event = false;
8422       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8423               !disp6._is_closed || !disp7._is_closed) &&
8424              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8425              !disp6._is_event && !disp7._is_event) wait_all();
8426     }
8427 
8428     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
8429     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8430                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
8431       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8432         disp6._is_event = disp7._is_event = disp8._is_event = false;
8433       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8434               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
8435              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8436              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
8437     }
8438 
8439     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
8440     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8441                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
8442       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8443         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
8444       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8445               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
8446              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8447              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
8448     }
8449 
8450     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
8451     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8452                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
8453                      CImgDisplay& disp10) {
8454       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8455         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
8456       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8457               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
8458              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8459              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
8460         wait_all();
8461     }
8462 
8463 #if cimg_display==0
8464 
8465     //! Wait for any window event occuring in any opened CImgDisplay.
8466     static void wait_all() {
8467       return _no_display_exception();
8468     }
8469 
8470     //! Render image into internal display buffer.
8471     /**
8472        \param img Input image data to render.
8473        \note
8474        - Convert image data representation into the internal display buffer (architecture-dependent structure).
8475        - The content of the associated window is not modified, until paint() is called.
8476        - Should not be used for common CImgDisplay uses, since display() is more useful.
8477     **/
8478     template<typename T>
8479     CImgDisplay& render(const CImg<T>& img) {
8480       return assign(img);
8481     }
8482 
8483     //! Paint internal display buffer on associated window.
8484     /**
8485        \note
8486        - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
8487        - Should not be used for common CImgDisplay uses, since display() is more useful.
8488     **/
8489     CImgDisplay& paint() {
8490       return assign();
8491     }
8492 
8493 
8494     //! Take a snapshot of the current screen content.
8495     /**
8496        \param x0 X-coordinate of the upper left corner.
8497        \param y0 Y-coordinate of the upper left corner.
8498        \param x1 X-coordinate of the lower right corner.
8499        \param y1 Y-coordinate of the lower right corner.
8500        \param[out] img Output screenshot. Can be empty on input
8501     **/
8502     template<typename T>
8503     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
8504       cimg::unused(x0,y0,x1,y1,&img);
8505       _no_display_exception();
8506     }
8507 
8508     //! Take a snapshot of the associated window content.
8509     /**
8510        \param[out] img Output snapshot. Can be empty on input.
8511     **/
8512     template<typename T>
8513     const CImgDisplay& snapshot(CImg<T>& img) const {
8514       cimg::unused(img);
8515       _no_display_exception();
8516       return *this;
8517     }
8518 #endif
8519 
8520     // X11-based implementation
8521     //--------------------------
8522 #if cimg_display==1
8523 
8524     Atom _wm_window_atom, _wm_protocol_atom;
8525     Window _window, _background_window;
8526     Colormap _colormap;
8527     XImage *_image;
8528     void *_data;
8529 #ifdef cimg_use_xshm
8530     XShmSegmentInfo *_shminfo;
8531 #endif
8532 
8533     static int screen_width() {
8534       Display *const dpy = cimg::X11_attr().display;
8535       int res = 0;
8536       if (!dpy) {
8537         Display *const _dpy = XOpenDisplay(0);
8538         if (!_dpy)
8539           throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
8540         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
8541         XCloseDisplay(_dpy);
8542       } else {
8543 #ifdef cimg_use_xrandr
8544         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
8545           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
8546         else res = DisplayWidth(dpy,DefaultScreen(dpy));
8547 #else
8548         res = DisplayWidth(dpy,DefaultScreen(dpy));
8549 #endif
8550       }
8551       return res;
8552     }
8553 
8554     static int screen_height() {
8555       Display *const dpy = cimg::X11_attr().display;
8556       int res = 0;
8557       if (!dpy) {
8558         Display *const _dpy = XOpenDisplay(0);
8559         if (!_dpy)
8560           throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
8561         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
8562         XCloseDisplay(_dpy);
8563       } else {
8564 #ifdef cimg_use_xrandr
8565         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
8566           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
8567         else res = DisplayHeight(dpy,DefaultScreen(dpy));
8568 #else
8569         res = DisplayHeight(dpy,DefaultScreen(dpy));
8570 #endif
8571       }
8572       return res;
8573     }
8574 
8575     static void wait_all() {
8576       if (!cimg::X11_attr().display) return;
8577       pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
8578       pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
8579       pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
8580     }
8581 
8582     void _handle_events(const XEvent *const pevent) {
8583       Display *const dpy = cimg::X11_attr().display;
8584       XEvent event = *pevent;
8585       switch (event.type) {
8586       case ClientMessage : {
8587         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
8588             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
8589           XUnmapWindow(cimg::X11_attr().display,_window);
8590           _is_closed = _is_event = true;
8591           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8592         }
8593       } break;
8594       case ConfigureNotify : {
8595         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
8596         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
8597         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
8598         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
8599           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
8600           XResizeWindow(dpy,_window,_window_width,_window_height);
8601           _is_resized = _is_event = true;
8602           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8603         }
8604         if (nx!=_window_x || ny!=_window_y) {
8605           _window_x = nx; _window_y = ny; _is_moved = _is_event = true;
8606           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8607         }
8608       } break;
8609       case Expose : {
8610         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
8611         _paint(false);
8612         if (_is_fullscreen) {
8613           XWindowAttributes attr;
8614           XGetWindowAttributes(dpy,_window,&attr);
8615           while (attr.map_state!=IsViewable) XSync(dpy,0);
8616           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
8617         }
8618       } break;
8619       case ButtonPress : {
8620         do {
8621           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
8622           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8623           switch (event.xbutton.button) {
8624           case 1 : set_button(1); break;
8625           case 3 : set_button(2); break;
8626           case 2 : set_button(3); break;
8627           }
8628         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
8629       } break;
8630       case ButtonRelease : {
8631         do {
8632           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
8633           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8634           switch (event.xbutton.button) {
8635           case 1 : set_button(1,false); break;
8636           case 3 : set_button(2,false); break;
8637           case 2 : set_button(3,false); break;
8638           case 4 : set_wheel(1); break;
8639           case 5 : set_wheel(-1); break;
8640           }
8641         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
8642       } break;
8643       case KeyPress : {
8644         char tmp = 0; KeySym ksym;
8645         XLookupString(&event.xkey,&tmp,1,&ksym,0);
8646         set_key((unsigned int)ksym,true);
8647       } break;
8648       case KeyRelease : {
8649         char keys_return[32];  // Check that the key has been physically unpressed.
8650         XQueryKeymap(dpy,keys_return);
8651         const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
8652         const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
8653         if (!is_key_pressed) {
8654           char tmp = 0; KeySym ksym;
8655           XLookupString(&event.xkey,&tmp,1,&ksym,0);
8656           set_key((unsigned int)ksym,false);
8657         }
8658       } break;
8659       case EnterNotify: {
8660         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
8661         _mouse_x = event.xmotion.x;
8662         _mouse_y = event.xmotion.y;
8663         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8664       } break;
8665       case LeaveNotify : {
8666         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
8667         _mouse_x = _mouse_y = -1; _is_event = true;
8668         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8669       } break;
8670       case MotionNotify : {
8671         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
8672         _mouse_x = event.xmotion.x;
8673         _mouse_y = event.xmotion.y;
8674         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8675         _is_event = true;
8676         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8677       } break;
8678       }
8679     }
8680 
8681     static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows.
8682       Display *const dpy = cimg::X11_attr().display;
8683       XEvent event;
8684       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
8685       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
8686       if (!arg) for ( ; ; ) {
8687         cimg_lock_display();
8688         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
8689         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
8690                                                       ExposureMask | StructureNotifyMask | ButtonPressMask |
8691                                                       KeyPressMask | PointerMotionMask | EnterWindowMask |
8692                                                       LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
8693         if (event_flag)
8694           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
8695             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
8696               cimg::X11_attr().wins[i]->_handle_events(&event);
8697         cimg_unlock_display();
8698         pthread_testcancel();
8699         cimg::sleep(8);
8700       }
8701       return 0;
8702     }
8703 
8704     void _set_colormap(Colormap& _colormap, const unsigned int dim) {
8705       XColor *const colormap = new XColor[256];
8706       switch (dim) {
8707       case 1 : { // colormap for greyscale images
8708         for (unsigned int index = 0; index<256; ++index) {
8709           colormap[index].pixel = index;
8710           colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
8711           colormap[index].flags = DoRed | DoGreen | DoBlue;
8712         }
8713       } break;
8714       case 2 : { // colormap for RG images
8715         for (unsigned int index = 0, r = 8; r<256; r+=16)
8716           for (unsigned int g = 8; g<256; g+=16) {
8717             colormap[index].pixel = index;
8718             colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
8719             colormap[index].green = (unsigned short)(g<<8);
8720             colormap[index++].flags = DoRed | DoGreen | DoBlue;
8721           }
8722       } break;
8723       default : { // colormap for RGB images
8724         for (unsigned int index = 0, r = 16; r<256; r+=32)
8725           for (unsigned int g = 16; g<256; g+=32)
8726             for (unsigned int b = 32; b<256; b+=64) {
8727               colormap[index].pixel = index;
8728               colormap[index].red = (unsigned short)(r<<8);
8729               colormap[index].green = (unsigned short)(g<<8);
8730               colormap[index].blue = (unsigned short)(b<<8);
8731               colormap[index++].flags = DoRed | DoGreen | DoBlue;
8732             }
8733       }
8734       }
8735       XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
8736       delete[] colormap;
8737     }
8738 
8739     void _map_window() {
8740       Display *const dpy = cimg::X11_attr().display;
8741       bool is_exposed = false, is_mapped = false;
8742       XWindowAttributes attr;
8743       XEvent event;
8744       XMapRaised(dpy,_window);
8745       do { // Wait for the window to be mapped.
8746         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
8747         switch (event.type) {
8748         case MapNotify : is_mapped = true; break;
8749         case Expose : is_exposed = true; break;
8750         }
8751       } while (!is_exposed || !is_mapped);
8752       do { // Wait for the window to be visible.
8753         XGetWindowAttributes(dpy,_window,&attr);
8754         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
8755       } while (attr.map_state!=IsViewable);
8756       _window_x = attr.x;
8757       _window_y = attr.y;
8758     }
8759 
8760     void _paint(const bool wait_expose=true) {
8761       if (_is_closed || !_image) return;
8762       Display *const dpy = cimg::X11_attr().display;
8763       if (wait_expose) { // Send an expose event sticked to display window to force repaint.
8764         XEvent event;
8765         event.xexpose.type = Expose;
8766         event.xexpose.serial = 0;
8767         event.xexpose.send_event = 1;
8768         event.xexpose.display = dpy;
8769         event.xexpose.window = _window;
8770         event.xexpose.x = 0;
8771         event.xexpose.y = 0;
8772         event.xexpose.width = width();
8773         event.xexpose.height = height();
8774         event.xexpose.count = 0;
8775         XSendEvent(dpy,_window,0,0,&event);
8776       } else { // Repaint directly (may be called from the expose event).
8777         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
8778 #ifdef cimg_use_xshm
8779         if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
8780         else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
8781 #else
8782         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
8783 #endif
8784       }
8785     }
8786 
8787     template<typename T>
8788     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
8789       Display *const dpy = cimg::X11_attr().display;
8790       cimg::unused(pixel_type);
8791 
8792 #ifdef cimg_use_xshm
8793       if (_shminfo) {
8794         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
8795         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
8796                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
8797         if (!nimage) { delete nshminfo; return; }
8798         else {
8799           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
8800           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
8801           else {
8802             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
8803             if (nshminfo->shmaddr==(char*)-1) {
8804               shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
8805             } else {
8806               nshminfo->readOnly = 0;
8807               cimg::X11_attr().is_shm_enabled = true;
8808               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
8809               XShmAttach(dpy,nshminfo);
8810               XFlush(dpy);
8811               XSetErrorHandler(oldXErrorHandler);
8812               if (!cimg::X11_attr().is_shm_enabled) {
8813                 shmdt(nshminfo->shmaddr);
8814                 shmctl(nshminfo->shmid,IPC_RMID,0);
8815                 XDestroyImage(nimage);
8816                 delete nshminfo;
8817                 return;
8818               } else {
8819                 T *const ndata = (T*)nimage->data;
8820                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
8821                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
8822                 XShmDetach(dpy,_shminfo);
8823                 XDestroyImage(_image);
8824                 shmdt(_shminfo->shmaddr);
8825                 shmctl(_shminfo->shmid,IPC_RMID,0);
8826                 delete _shminfo;
8827                 _shminfo = nshminfo;
8828                 _image = nimage;
8829                 _data = (void*)ndata;
8830               }
8831             }
8832           }
8833         }
8834       } else
8835 #endif
8836         {
8837           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
8838           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
8839           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
8840           _data = (void*)ndata;
8841           XDestroyImage(_image);
8842           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
8843                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
8844         }
8845     }
8846 
8847     void _init_fullscreen() {
8848       if (!_is_fullscreen || _is_closed) return;
8849       Display *const dpy = cimg::X11_attr().display;
8850       _background_window = 0;
8851 
8852 #ifdef cimg_use_xrandr
8853       int foo;
8854       if (XRRQueryExtension(dpy,&foo,&foo)) {
8855         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
8856         if (!cimg::X11_attr().resolutions) {
8857           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
8858           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
8859         }
8860         if (cimg::X11_attr().resolutions) {
8861           cimg::X11_attr().curr_resolution = 0;
8862           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
8863             const unsigned int
8864               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
8865               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
8866             if (nw>=_width && nh>=_height &&
8867                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
8868                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
8869               cimg::X11_attr().curr_resolution = i;
8870           }
8871           if (cimg::X11_attr().curr_resolution>0) {
8872             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
8873             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
8874                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
8875             XRRFreeScreenConfigInfo(config);
8876             XSync(dpy,0);
8877           }
8878         }
8879       }
8880       if (!cimg::X11_attr().resolutions)
8881         cimg::warn(_cimgdisplay_instance
8882                    "init_fullscreen(): Xrandr extension not supported by the X server.",
8883                    cimgdisplay_instance);
8884 #endif
8885 
8886       const unsigned int sx = screen_width(), sy = screen_height();
8887       if (sx==_width && sy==_height) return;
8888       XSetWindowAttributes winattr;
8889       winattr.override_redirect = 1;
8890       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
8891                                          InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
8892       const cimg_ulong buf_size = (cimg_ulong)sx*sy*(cimg::X11_attr().nb_bits==8?1:
8893                                                      (cimg::X11_attr().nb_bits==16?2:4));
8894       void *background_data = std::malloc(buf_size);
8895       std::memset(background_data,0,buf_size);
8896       XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
8897                                               ZPixmap,0,(char*)background_data,sx,sy,8,0);
8898       XEvent event;
8899       XSelectInput(dpy,_background_window,StructureNotifyMask);
8900       XMapRaised(dpy,_background_window);
8901       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
8902       while (event.type!=MapNotify);
8903       GC gc = DefaultGC(dpy,DefaultScreen(dpy));
8904 #ifdef cimg_use_xshm
8905       if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
8906       else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
8907 #else
8908       XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
8909 #endif
8910       XWindowAttributes attr;
8911       XGetWindowAttributes(dpy,_background_window,&attr);
8912       while (attr.map_state!=IsViewable) XSync(dpy,0);
8913       XDestroyImage(background_image);
8914     }
8915 
8916     void _desinit_fullscreen() {
8917       if (!_is_fullscreen) return;
8918       Display *const dpy = cimg::X11_attr().display;
8919       XUngrabKeyboard(dpy,CurrentTime);
8920 #ifdef cimg_use_xrandr
8921       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
8922         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
8923         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
8924         XRRFreeScreenConfigInfo(config);
8925         XSync(dpy,0);
8926         cimg::X11_attr().curr_resolution = 0;
8927       }
8928 #endif
8929       if (_background_window) XDestroyWindow(dpy,_background_window);
8930       _background_window = 0;
8931       _is_fullscreen = false;
8932     }
8933 
8934     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
8935       cimg::unused(dpy,error);
8936       cimg::X11_attr().is_shm_enabled = false;
8937       return 0;
8938     }
8939 
8940     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8941                  const unsigned int normalization_type=3,
8942                  const bool fullscreen_flag=false, const bool closed_flag=false) {
8943       cimg::mutex(14);
8944 
8945       // Allocate space for window title
8946       const char *const nptitle = ptitle?ptitle:"";
8947       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
8948       char *const tmp_title = s?new char[s]:0;
8949       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8950 
8951       // Destroy previous display window if existing
8952       if (!is_empty()) assign();
8953 
8954       // Open X11 display and retrieve graphical properties.
8955       Display* &dpy = cimg::X11_attr().display;
8956       if (!dpy) {
8957         dpy = XOpenDisplay(0);
8958         if (!dpy)
8959           throw CImgDisplayException(_cimgdisplay_instance
8960                                      "assign(): Failed to open X11 display.",
8961                                      cimgdisplay_instance);
8962 
8963         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
8964         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
8965             cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
8966           throw CImgDisplayException(_cimgdisplay_instance
8967                                      "assign(): Invalid %u bits screen mode detected "
8968                                      "(only 8, 16, 24 and 32 bits modes are managed).",
8969                                      cimgdisplay_instance,
8970                                      cimg::X11_attr().nb_bits);
8971         XVisualInfo vtemplate;
8972         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
8973         int nb_visuals;
8974         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
8975         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
8976         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
8977 	XFree(vinfo);
8978 
8979         cimg_lock_display();
8980         cimg::X11_attr().events_thread = new pthread_t;
8981         pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
8982       } else cimg_lock_display();
8983 
8984       // Set display variables.
8985       _width = std::min(dimw,(unsigned int)screen_width());
8986       _height = std::min(dimh,(unsigned int)screen_height());
8987       _normalization = normalization_type<4?normalization_type:3;
8988       _is_fullscreen = fullscreen_flag;
8989       _window_x = _window_y = 0;
8990       _is_closed = closed_flag;
8991       _title = tmp_title;
8992       flush();
8993 
8994       // Create X11 window (and LUT, if 8bits display)
8995       if (_is_fullscreen) {
8996         if (!_is_closed) _init_fullscreen();
8997         const unsigned int sx = screen_width(), sy = screen_height();
8998         XSetWindowAttributes winattr;
8999         winattr.override_redirect = 1;
9000         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
9001                                 InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
9002       } else
9003         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
9004 
9005       XSelectInput(dpy,_window,
9006 		   ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
9007 		   EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
9008 
9009       XStoreName(dpy,_window,_title?_title:" ");
9010       if (cimg::X11_attr().nb_bits==8) {
9011         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
9012         _set_colormap(_colormap,3);
9013         XSetWindowColormap(dpy,_window,_colormap);
9014       }
9015 
9016       static const char *const _window_class = cimg_appname;
9017       XClassHint *const window_class = XAllocClassHint();
9018       window_class->res_name = (char*)_window_class;
9019       window_class->res_class = (char*)_window_class;
9020       XSetClassHint(dpy,_window,window_class);
9021       XFree(window_class);
9022 
9023       _window_width = _width;
9024       _window_height = _height;
9025 
9026       // Create XImage
9027 #ifdef cimg_use_xshm
9028       _shminfo = 0;
9029       if (XShmQueryExtension(dpy)) {
9030         _shminfo = new XShmSegmentInfo;
9031         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
9032                                  ZPixmap,0,_shminfo,_width,_height);
9033         if (!_image) { delete _shminfo; _shminfo = 0; }
9034         else {
9035           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
9036           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
9037           else {
9038             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
9039             if (_shminfo->shmaddr==(char*)-1) {
9040               shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
9041             } else {
9042               _shminfo->readOnly = 0;
9043               cimg::X11_attr().is_shm_enabled = true;
9044               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
9045               XShmAttach(dpy,_shminfo);
9046               XSync(dpy,0);
9047               XSetErrorHandler(oldXErrorHandler);
9048               if (!cimg::X11_attr().is_shm_enabled) {
9049                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
9050                 delete _shminfo; _shminfo = 0;
9051               }
9052             }
9053           }
9054         }
9055       }
9056       if (!_shminfo)
9057 #endif
9058         {
9059           const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
9060                                                                   (cimg::X11_attr().nb_bits==16?2:4));
9061           _data = std::malloc(buf_size);
9062           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
9063                                 ZPixmap,0,(char*)_data,_width,_height,8,0);
9064         }
9065 
9066       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
9067       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
9068       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
9069 
9070       if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
9071       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
9072       if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
9073       cimg_unlock_display();
9074       cimg::mutex(14,0);
9075     }
9076 
9077     CImgDisplay& assign() {
9078       if (is_empty()) return flush();
9079       Display *const dpy = cimg::X11_attr().display;
9080       cimg_lock_display();
9081 
9082       // Remove display window from event thread list.
9083       unsigned int i;
9084       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
9085       for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
9086       --cimg::X11_attr().nb_wins;
9087 
9088       // Destroy window, image, colormap and title.
9089       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
9090       XDestroyWindow(dpy,_window);
9091       _window = 0;
9092 #ifdef cimg_use_xshm
9093       if (_shminfo) {
9094         XShmDetach(dpy,_shminfo);
9095         XDestroyImage(_image);
9096         shmdt(_shminfo->shmaddr);
9097         shmctl(_shminfo->shmid,IPC_RMID,0);
9098         delete _shminfo;
9099         _shminfo = 0;
9100       } else
9101 #endif
9102         XDestroyImage(_image);
9103       _data = 0; _image = 0;
9104       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
9105       _colormap = 0;
9106       XSync(dpy,0);
9107 
9108       // Reset display variables.
9109       delete[] _title;
9110       _width = _height = _normalization = _window_width = _window_height = 0;
9111       _window_x = _window_y = 0;
9112       _is_fullscreen = false;
9113       _is_closed = true;
9114       _min = _max = 0;
9115       _title = 0;
9116       flush();
9117 
9118       cimg_unlock_display();
9119       return *this;
9120     }
9121 
9122     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
9123                         const unsigned int normalization_type=3,
9124                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9125       if (!dimw || !dimh) return assign();
9126       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
9127       _min = _max = 0;
9128       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
9129                            (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
9130                   (size_t)_width*_height);
9131       return paint();
9132     }
9133 
9134     template<typename T>
9135     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
9136                         const unsigned int normalization_type=3,
9137                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9138       if (!img) return assign();
9139       CImg<T> tmp;
9140       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
9141                                                                            (img._height - 1)/2,
9142                                                                            (img._depth - 1)/2));
9143       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
9144       if (_normalization==2) _min = (float)nimg.min_max(_max);
9145       return render(nimg).paint();
9146     }
9147 
9148     template<typename T>
9149     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
9150                         const unsigned int normalization_type=3,
9151                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9152       if (!list) return assign();
9153       CImg<T> tmp;
9154       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
9155                                                                                            (img._height - 1)/2,
9156                                                                                            (img._depth - 1)/2));
9157       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
9158       if (_normalization==2) _min = (float)nimg.min_max(_max);
9159       return render(nimg).paint();
9160     }
9161 
9162     CImgDisplay& assign(const CImgDisplay& disp) {
9163       if (!disp) return assign();
9164       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
9165       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
9166                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
9167                                     sizeof(unsigned int))*(size_t)_width*_height);
9168       return paint();
9169     }
9170 
9171     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
9172       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
9173       if (is_empty()) return assign(nwidth,nheight);
9174       Display *const dpy = cimg::X11_attr().display;
9175       const unsigned int
9176         tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
9177         tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
9178         dimx = tmpdimx?tmpdimx:1,
9179         dimy = tmpdimy?tmpdimy:1;
9180       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
9181         show();
9182         cimg_lock_display();
9183         if (_window_width!=dimx || _window_height!=dimy) {
9184           XWindowAttributes attr;
9185           for (unsigned int i = 0; i<10; ++i) {
9186             XResizeWindow(dpy,_window,dimx,dimy);
9187             XGetWindowAttributes(dpy,_window,&attr);
9188             if (attr.width==(int)dimx && attr.height==(int)dimy) break;
9189             cimg::wait(5);
9190           }
9191         }
9192         if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
9193           case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
9194           case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
9195           default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
9196           }
9197         _window_width = _width = dimx; _window_height = _height = dimy;
9198         cimg_unlock_display();
9199       }
9200       _is_resized = false;
9201       if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
9202       if (force_redraw) return paint();
9203       return *this;
9204     }
9205 
9206     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
9207       if (is_empty()) return *this;
9208       if (force_redraw) {
9209         const cimg_ulong buf_size = (cimg_ulong)_width*_height*
9210           (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
9211         void *image_data = std::malloc(buf_size);
9212         std::memcpy(image_data,_data,buf_size);
9213         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9214         std::memcpy(_data,image_data,buf_size);
9215         std::free(image_data);
9216         return paint();
9217       }
9218       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9219     }
9220 
9221     CImgDisplay& show() {
9222       if (is_empty() || !_is_closed) return *this;
9223       cimg_lock_display();
9224       if (_is_fullscreen) _init_fullscreen();
9225       _map_window();
9226       _is_closed = false;
9227       cimg_unlock_display();
9228       return paint();
9229     }
9230 
9231     CImgDisplay& close() {
9232       if (is_empty() || _is_closed) return *this;
9233       Display *const dpy = cimg::X11_attr().display;
9234       cimg_lock_display();
9235       if (_is_fullscreen) _desinit_fullscreen();
9236       XUnmapWindow(dpy,_window);
9237       _window_x = _window_y = -1;
9238       _is_closed = true;
9239       cimg_unlock_display();
9240       return *this;
9241     }
9242 
9243     CImgDisplay& move(const int posx, const int posy) {
9244       if (is_empty()) return *this;
9245       if (_window_x!=posx || _window_y!=posy) {
9246         show();
9247         Display *const dpy = cimg::X11_attr().display;
9248         cimg_lock_display();
9249         XMoveWindow(dpy,_window,posx,posy);
9250         _window_x = posx; _window_y = posy;
9251         cimg_unlock_display();
9252       }
9253       _is_moved = false;
9254       return paint();
9255     }
9256 
9257     CImgDisplay& show_mouse() {
9258       if (is_empty()) return *this;
9259       Display *const dpy = cimg::X11_attr().display;
9260       cimg_lock_display();
9261       XUndefineCursor(dpy,_window);
9262       cimg_unlock_display();
9263       return *this;
9264     }
9265 
9266     CImgDisplay& hide_mouse() {
9267       if (is_empty()) return *this;
9268       Display *const dpy = cimg::X11_attr().display;
9269       cimg_lock_display();
9270       static const char pix_data[8] = { 0 };
9271       XColor col;
9272       col.red = col.green = col.blue = 0;
9273       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
9274       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
9275       XFreePixmap(dpy,pix);
9276       XDefineCursor(dpy,_window,cur);
9277       cimg_unlock_display();
9278       return *this;
9279     }
9280 
9281     CImgDisplay& set_mouse(const int posx, const int posy) {
9282       if (is_empty() || _is_closed) return *this;
9283       Display *const dpy = cimg::X11_attr().display;
9284       cimg_lock_display();
9285       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
9286       _mouse_x = posx; _mouse_y = posy;
9287       _is_moved = false;
9288       XSync(dpy,0);
9289       cimg_unlock_display();
9290       return *this;
9291     }
9292 
9293     CImgDisplay& set_title(const char *const format, ...) {
9294       if (is_empty()) return *this;
9295       char *const tmp = new char[1024];
9296       va_list ap;
9297       va_start(ap, format);
9298       cimg_vsnprintf(tmp,1024,format,ap);
9299       va_end(ap);
9300       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
9301       delete[] _title;
9302       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
9303       _title = new char[s];
9304       std::memcpy(_title,tmp,s*sizeof(char));
9305       Display *const dpy = cimg::X11_attr().display;
9306       cimg_lock_display();
9307       XStoreName(dpy,_window,tmp);
9308       cimg_unlock_display();
9309       delete[] tmp;
9310       return *this;
9311     }
9312 
9313     template<typename T>
9314     CImgDisplay& display(const CImg<T>& img) {
9315       if (!img)
9316         throw CImgArgumentException(_cimgdisplay_instance
9317                                     "display(): Empty specified image.",
9318                                     cimgdisplay_instance);
9319       if (is_empty()) return assign(img);
9320       return render(img).paint(false);
9321     }
9322 
9323     CImgDisplay& paint(const bool wait_expose=true) {
9324       if (is_empty()) return *this;
9325       cimg_lock_display();
9326       _paint(wait_expose);
9327       cimg_unlock_display();
9328       return *this;
9329     }
9330 
9331     template<typename T>
9332     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
9333       if (!img)
9334         throw CImgArgumentException(_cimgdisplay_instance
9335                                     "render(): Empty specified image.",
9336                                     cimgdisplay_instance);
9337       if (is_empty()) return *this;
9338       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
9339                                                              (img._depth - 1)/2));
9340       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
9341         return render(img.get_resize(_width,_height,1,-100,1));
9342       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
9343         static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
9344         return render(img.get_index(default_colormap,1,false));
9345       }
9346 
9347       const T
9348         *data1 = img._data,
9349         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
9350         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
9351 
9352       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
9353       cimg_lock_display();
9354 
9355       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
9356         _min = _max = 0;
9357         switch (cimg::X11_attr().nb_bits) {
9358         case 8 : { // 256 colormap, no normalization
9359           _set_colormap(_colormap,img._spectrum);
9360           unsigned char
9361             *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
9362             new unsigned char[(size_t)img._width*img._height],
9363             *ptrd = (unsigned char*)ndata;
9364           switch (img._spectrum) {
9365           case 1 :
9366             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9367               (*ptrd++) = (unsigned char)*(data1++);
9368             break;
9369           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9370 	      const unsigned char
9371                 R = (unsigned char)*(data1++),
9372                 G = (unsigned char)*(data2++);
9373 	      (*ptrd++) = (R&0xf0) | (G>>4);
9374 	    } break;
9375           default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9376 	      const unsigned char
9377                 R = (unsigned char)*(data1++),
9378                 G = (unsigned char)*(data2++),
9379                 B = (unsigned char)*(data3++);
9380 	      (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
9381 	    }
9382           }
9383           if (ndata!=_data) {
9384             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
9385             delete[] ndata;
9386           }
9387         } break;
9388         case 16 : { // 16 bits colors, no normalization
9389           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
9390             new unsigned short[(size_t)img._width*img._height];
9391           unsigned char *ptrd = (unsigned char*)ndata;
9392           const unsigned int M = 248;
9393           switch (img._spectrum) {
9394           case 1 :
9395             if (cimg::X11_attr().byte_order)
9396               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9397                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
9398                 ptrd[0] = (val&M) | (G>>3);
9399                 ptrd[1] = (G<<5) | (G>>1);
9400                 ptrd+=2;
9401               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9402                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
9403                 ptrd[0] = (G<<5) | (G>>1);
9404                 ptrd[1] = (val&M) | (G>>3);
9405                 ptrd+=2;
9406               }
9407             break;
9408           case 2 :
9409             if (cimg::X11_attr().byte_order)
9410               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9411                 const unsigned char G = (unsigned char)*(data2++)>>2;
9412                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
9413                 ptrd[1] = (G<<5);
9414                 ptrd+=2;
9415               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9416                 const unsigned char G = (unsigned char)*(data2++)>>2;
9417                 ptrd[0] = (G<<5);
9418                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
9419                 ptrd+=2;
9420               }
9421             break;
9422           default :
9423             if (cimg::X11_attr().byte_order)
9424               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9425                 const unsigned char G = (unsigned char)*(data2++)>>2;
9426                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
9427                 ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
9428                 ptrd+=2;
9429               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9430                 const unsigned char G = (unsigned char)*(data2++)>>2;
9431                 ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
9432                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
9433                 ptrd+=2;
9434               }
9435           }
9436           if (ndata!=_data) {
9437             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
9438             delete[] ndata;
9439           }
9440         } break;
9441         default : { // 24 bits colors, no normalization
9442           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
9443             new unsigned int[(size_t)img._width*img._height];
9444           if (sizeof(int)==4) { // 32 bits int uses optimized version
9445             unsigned int *ptrd = ndata;
9446             switch (img._spectrum) {
9447             case 1 :
9448               if (cimg::X11_attr().byte_order==cimg::endianness())
9449                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9450                   const unsigned char val = (unsigned char)*(data1++);
9451                   *(ptrd++) = (val<<16) | (val<<8) | val;
9452                 }
9453               else
9454                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9455                  const unsigned char val = (unsigned char)*(data1++);
9456                   *(ptrd++) = (val<<16) | (val<<8) | val;
9457                 }
9458               break;
9459             case 2 :
9460               if (cimg::X11_attr().byte_order==cimg::endianness())
9461                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9462                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
9463               else
9464                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9465                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
9466               break;
9467             default :
9468               if (cimg::X11_attr().byte_order==cimg::endianness())
9469                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9470                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
9471                     (unsigned char)*(data3++);
9472               else
9473                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9474                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
9475                     ((unsigned char)*(data1++)<<8);
9476             }
9477           } else {
9478             unsigned char *ptrd = (unsigned char*)ndata;
9479             switch (img._spectrum) {
9480             case 1 :
9481               if (cimg::X11_attr().byte_order)
9482                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9483                   ptrd[0] = 0;
9484                   ptrd[1] = (unsigned char)*(data1++);
9485                   ptrd[2] = 0;
9486                   ptrd[3] = 0;
9487                   ptrd+=4;
9488                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9489                   ptrd[0] = 0;
9490                   ptrd[1] = 0;
9491                   ptrd[2] = (unsigned char)*(data1++);
9492                   ptrd[3] = 0;
9493                   ptrd+=4;
9494                 }
9495               break;
9496             case 2 :
9497               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
9498               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9499                 ptrd[0] = 0;
9500                 ptrd[1] = (unsigned char)*(data2++);
9501                 ptrd[2] = (unsigned char)*(data1++);
9502                 ptrd[3] = 0;
9503                 ptrd+=4;
9504               }
9505               break;
9506             default :
9507               if (cimg::X11_attr().byte_order)
9508                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9509                   ptrd[0] = 0;
9510                   ptrd[1] = (unsigned char)*(data1++);
9511                   ptrd[2] = (unsigned char)*(data2++);
9512                   ptrd[3] = (unsigned char)*(data3++);
9513                   ptrd+=4;
9514                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9515                   ptrd[0] = (unsigned char)*(data3++);
9516                   ptrd[1] = (unsigned char)*(data2++);
9517                   ptrd[2] = (unsigned char)*(data1++);
9518                   ptrd[3] = 0;
9519                   ptrd+=4;
9520                 }
9521             }
9522           }
9523           if (ndata!=_data) {
9524             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
9525             delete[] ndata;
9526           }
9527         }
9528         }
9529       } else {
9530         if (_normalization==3) {
9531           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
9532           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
9533         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
9534         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
9535         switch (cimg::X11_attr().nb_bits) {
9536         case 8 : { // 256 colormap, with normalization
9537           _set_colormap(_colormap,img._spectrum);
9538           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
9539             new unsigned char[(size_t)img._width*img._height];
9540           unsigned char *ptrd = (unsigned char*)ndata;
9541           switch (img._spectrum) {
9542           case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9543               const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
9544               *(ptrd++) = R;
9545             } break;
9546           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9547               const unsigned char
9548                 R = (unsigned char)((*(data1++) - _min)*mm),
9549                 G = (unsigned char)((*(data2++) - _min)*mm);
9550             (*ptrd++) = (R&0xf0) | (G>>4);
9551           } break;
9552           default :
9553             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9554               const unsigned char
9555                 R = (unsigned char)((*(data1++) - _min)*mm),
9556                 G = (unsigned char)((*(data2++) - _min)*mm),
9557                 B = (unsigned char)((*(data3++) - _min)*mm);
9558               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
9559             }
9560           }
9561           if (ndata!=_data) {
9562             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
9563             delete[] ndata;
9564           }
9565         } break;
9566         case 16 : { // 16 bits colors, with normalization
9567           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
9568             new unsigned short[(size_t)img._width*img._height];
9569           unsigned char *ptrd = (unsigned char*)ndata;
9570           const unsigned int M = 248;
9571           switch (img._spectrum) {
9572           case 1 :
9573             if (cimg::X11_attr().byte_order)
9574               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9575                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
9576                 ptrd[0] = (val&M) | (G>>3);
9577                 ptrd[1] = (G<<5) | (val>>3);
9578                 ptrd+=2;
9579               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9580                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
9581                 ptrd[0] = (G<<5) | (val>>3);
9582                 ptrd[1] = (val&M) | (G>>3);
9583                 ptrd+=2;
9584               }
9585             break;
9586           case 2 :
9587             if (cimg::X11_attr().byte_order)
9588               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9589                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9590                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9591                 ptrd[1] = (G<<5);
9592                 ptrd+=2;
9593               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9594                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9595                 ptrd[0] = (G<<5);
9596                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9597                 ptrd+=2;
9598               }
9599             break;
9600           default :
9601             if (cimg::X11_attr().byte_order)
9602               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9603                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9604                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9605                 ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
9606                 ptrd+=2;
9607               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9608                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9609                 ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
9610                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9611                 ptrd+=2;
9612               }
9613           }
9614           if (ndata!=_data) {
9615             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
9616             delete[] ndata;
9617           }
9618         } break;
9619         default : { // 24 bits colors, with normalization
9620           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
9621             new unsigned int[(size_t)img._width*img._height];
9622           if (sizeof(int)==4) { // 32 bits int uses optimized version
9623             unsigned int *ptrd = ndata;
9624             switch (img._spectrum) {
9625             case 1 :
9626               if (cimg::X11_attr().byte_order==cimg::endianness())
9627                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9628                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9629                   *(ptrd++) = (val<<16) | (val<<8) | val;
9630                 }
9631               else
9632                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9633                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9634                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
9635                 }
9636               break;
9637             case 2 :
9638               if (cimg::X11_attr().byte_order==cimg::endianness())
9639                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9640                   *(ptrd++) =
9641                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
9642                     ((unsigned char)((*(data2++) - _min)*mm)<<8);
9643               else
9644                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9645                   *(ptrd++) =
9646                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
9647                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
9648               break;
9649             default :
9650               if (cimg::X11_attr().byte_order==cimg::endianness())
9651                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9652                   *(ptrd++) =
9653                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
9654                     ((unsigned char)((*(data2++) - _min)*mm)<<8) |
9655                     (unsigned char)((*(data3++) - _min)*mm);
9656               else
9657                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9658                   *(ptrd++) =
9659                     ((unsigned char)((*(data3++) - _min)*mm)<<24) |
9660                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
9661                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
9662             }
9663           } else {
9664             unsigned char *ptrd = (unsigned char*)ndata;
9665             switch (img._spectrum) {
9666             case 1 :
9667               if (cimg::X11_attr().byte_order)
9668                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9669                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9670                   ptrd[0] = 0;
9671                   ptrd[1] = val;
9672                   ptrd[2] = val;
9673                   ptrd[3] = val;
9674                   ptrd+=4;
9675                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9676                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9677                   ptrd[0] = val;
9678                   ptrd[1] = val;
9679                   ptrd[2] = val;
9680                   ptrd[3] = 0;
9681                   ptrd+=4;
9682                 }
9683               break;
9684             case 2 :
9685               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
9686               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9687                 ptrd[0] = 0;
9688                 ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
9689                 ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
9690                 ptrd[3] = 0;
9691                 ptrd+=4;
9692               }
9693               break;
9694             default :
9695               if (cimg::X11_attr().byte_order)
9696                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9697                   ptrd[0] = 0;
9698                   ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
9699                   ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
9700                   ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
9701                   ptrd+=4;
9702                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9703                   ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
9704                   ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
9705                   ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
9706                   ptrd[3] = 0;
9707                   ptrd+=4;
9708                 }
9709             }
9710           }
9711           if (ndata!=_data) {
9712             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
9713             delete[] ndata;
9714           }
9715 	}
9716         }
9717       }
9718       cimg_unlock_display();
9719       return *this;
9720     }
9721 
9722     template<typename T>
9723     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
9724       img.assign();
9725       Display *dpy = cimg::X11_attr().display;
9726       cimg_lock_display();
9727       if (!dpy) {
9728         dpy = XOpenDisplay(0);
9729         if (!dpy)
9730           throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
9731       }
9732       Window root = DefaultRootWindow(dpy);
9733       XWindowAttributes gwa;
9734       XGetWindowAttributes(dpy,root,&gwa);
9735       const int width = gwa.width, height = gwa.height;
9736       int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
9737       if (_x0>_x1) cimg::swap(_x0,_x1);
9738       if (_y0>_y1) cimg::swap(_y0,_y1);
9739 
9740       XImage *image = 0;
9741       if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
9742         _x0 = std::max(_x0,0);
9743         _y0 = std::max(_y0,0);
9744         _x1 = std::min(_x1,width - 1);
9745         _y1 = std::min(_y1,height - 1);
9746         image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
9747 
9748         if (image) {
9749           const unsigned long
9750             red_mask = image->red_mask,
9751             green_mask = image->green_mask,
9752             blue_mask = image->blue_mask;
9753           img.assign(image->width,image->height,1,3);
9754           T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
9755           cimg_forXY(img,x,y) {
9756             const unsigned long pixel = XGetPixel(image,x,y);
9757             *(pR++) = (T)((pixel & red_mask)>>16);
9758             *(pG++) = (T)((pixel & green_mask)>>8);
9759             *(pB++) = (T)(pixel & blue_mask);
9760           }
9761           XDestroyImage(image);
9762         }
9763       }
9764       if (!cimg::X11_attr().display) XCloseDisplay(dpy);
9765       cimg_unlock_display();
9766       if (img.is_empty())
9767         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
9768                                    "with coordinates (%d,%d)-(%d,%d).",
9769                                    x0,y0,x1,y1);
9770     }
9771 
9772     template<typename T>
9773     const CImgDisplay& snapshot(CImg<T>& img) const {
9774       if (is_empty()) { img.assign(); return *this; }
9775       const unsigned char *ptrs = (unsigned char*)_data;
9776       img.assign(_width,_height,1,3);
9777       T
9778         *data1 = img.data(0,0,0,0),
9779         *data2 = img.data(0,0,0,1),
9780         *data3 = img.data(0,0,0,2);
9781       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
9782       switch (cimg::X11_attr().nb_bits) {
9783       case 8 : {
9784         for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9785           const unsigned char val = *(ptrs++);
9786           *(data1++) = (T)(val&0xe0);
9787           *(data2++) = (T)((val&0x1c)<<3);
9788           *(data3++) = (T)(val<<6);
9789         }
9790       } break;
9791       case 16 : {
9792         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9793           const unsigned char
9794             val0 = ptrs[0],
9795             val1 = ptrs[1];
9796           ptrs+=2;
9797           *(data1++) = (T)(val0&0xf8);
9798           *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
9799           *(data3++) = (T)(val1<<3);
9800           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9801           const unsigned short
9802             val0 = ptrs[0],
9803             val1 = ptrs[1];
9804           ptrs+=2;
9805           *(data1++) = (T)(val1&0xf8);
9806           *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
9807           *(data3++) = (T)(val0<<3);
9808         }
9809       } break;
9810       default : {
9811         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9812           ++ptrs;
9813           *(data1++) = (T)ptrs[0];
9814           *(data2++) = (T)ptrs[1];
9815           *(data3++) = (T)ptrs[2];
9816           ptrs+=3;
9817           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9818             *(data3++) = (T)ptrs[0];
9819             *(data2++) = (T)ptrs[1];
9820             *(data1++) = (T)ptrs[2];
9821             ptrs+=3;
9822             ++ptrs;
9823           }
9824       }
9825       }
9826       return *this;
9827     }
9828 
9829     // Windows-based implementation.
9830     //-------------------------------
9831 #elif cimg_display==2
9832 
9833     bool _is_mouse_tracked, _is_cursor_visible;
9834     HANDLE _thread, _is_created, _mutex;
9835     HWND _window, _background_window;
9836     CLIENTCREATESTRUCT _ccs;
9837     unsigned int *_data;
9838     DEVMODE _curr_mode;
9839     BITMAPINFO _bmi;
9840     HDC _hdc;
9841 
9842     static int screen_width() {
9843       DEVMODE mode;
9844       mode.dmSize = sizeof(DEVMODE);
9845       mode.dmDriverExtra = 0;
9846       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
9847       return (int)mode.dmPelsWidth;
9848     }
9849 
9850     static int screen_height() {
9851       DEVMODE mode;
9852       mode.dmSize = sizeof(DEVMODE);
9853       mode.dmDriverExtra = 0;
9854       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
9855       return (int)mode.dmPelsHeight;
9856     }
9857 
9858     static void wait_all() {
9859       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
9860     }
9861 
9862     static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
9863 #ifdef _WIN64
9864       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
9865 #else
9866       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
9867 #endif
9868       MSG st_msg;
9869       switch (msg) {
9870       case WM_CLOSE :
9871         disp->_mouse_x = disp->_mouse_y = -1;
9872         disp->_window_x = disp->_window_y = 0;
9873         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
9874         ReleaseMutex(disp->_mutex);
9875         ShowWindow(disp->_window,SW_HIDE);
9876         disp->_is_event = true;
9877         SetEvent(cimg::Win32_attr().wait_event);
9878         return 0;
9879       case WM_SIZE : {
9880         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
9881         WaitForSingleObject(disp->_mutex,INFINITE);
9882         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
9883         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
9884           disp->_window_width = nw;
9885           disp->_window_height = nh;
9886           disp->_mouse_x = disp->_mouse_y = -1;
9887           disp->_is_resized = disp->_is_event = true;
9888           SetEvent(cimg::Win32_attr().wait_event);
9889         }
9890         ReleaseMutex(disp->_mutex);
9891       } break;
9892       case WM_MOVE : {
9893         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
9894         WaitForSingleObject(disp->_mutex,INFINITE);
9895         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
9896         if (nx!=disp->_window_x || ny!=disp->_window_y) {
9897           disp->_window_x = nx;
9898           disp->_window_y = ny;
9899           disp->_is_moved = disp->_is_event = true;
9900           SetEvent(cimg::Win32_attr().wait_event);
9901         }
9902         ReleaseMutex(disp->_mutex);
9903       } break;
9904       case WM_PAINT :
9905         disp->paint();
9906         cimg::mutex(15);
9907         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
9908         cimg::mutex(15,0);
9909         break;
9910       case WM_ERASEBKGND :
9911         //        return 0;
9912         break;
9913       case WM_KEYDOWN :
9914         disp->set_key((unsigned int)wParam);
9915         SetEvent(cimg::Win32_attr().wait_event);
9916         break;
9917       case WM_KEYUP :
9918         disp->set_key((unsigned int)wParam,false);
9919         SetEvent(cimg::Win32_attr().wait_event);
9920         break;
9921       case WM_MOUSEMOVE : {
9922         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
9923         disp->_mouse_x = LOWORD(lParam);
9924         disp->_mouse_y = HIWORD(lParam);
9925 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
9926         if (!disp->_is_mouse_tracked) {
9927           TRACKMOUSEEVENT tme;
9928           tme.cbSize = sizeof(TRACKMOUSEEVENT);
9929           tme.dwFlags = TME_LEAVE;
9930           tme.hwndTrack = disp->_window;
9931           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
9932         }
9933 #endif
9934         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
9935           disp->_mouse_x = disp->_mouse_y = -1;
9936         disp->_is_event = true;
9937         SetEvent(cimg::Win32_attr().wait_event);
9938         cimg::mutex(15);
9939 	if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
9940         cimg::mutex(15,0);
9941       }	break;
9942       case WM_MOUSELEAVE : {
9943         disp->_mouse_x = disp->_mouse_y = -1;
9944         disp->_is_mouse_tracked = false;
9945         cimg::mutex(15);
9946 	while (ShowCursor(TRUE)<0) {}
9947         cimg::mutex(15,0);
9948       } break;
9949       case WM_LBUTTONDOWN :
9950         disp->set_button(1);
9951         SetEvent(cimg::Win32_attr().wait_event);
9952         break;
9953       case WM_RBUTTONDOWN :
9954         disp->set_button(2);
9955         SetEvent(cimg::Win32_attr().wait_event);
9956         break;
9957       case WM_MBUTTONDOWN :
9958         disp->set_button(3);
9959         SetEvent(cimg::Win32_attr().wait_event);
9960         break;
9961       case WM_LBUTTONUP :
9962         disp->set_button(1,false);
9963         SetEvent(cimg::Win32_attr().wait_event);
9964         break;
9965       case WM_RBUTTONUP :
9966         disp->set_button(2,false);
9967         SetEvent(cimg::Win32_attr().wait_event);
9968         break;
9969       case WM_MBUTTONUP :
9970         disp->set_button(3,false);
9971         SetEvent(cimg::Win32_attr().wait_event);
9972         break;
9973       case 0x020A : // WM_MOUSEWHEEL:
9974         disp->set_wheel((int)((short)HIWORD(wParam))/120);
9975         SetEvent(cimg::Win32_attr().wait_event);
9976       }
9977       return DefWindowProc(window,msg,wParam,lParam);
9978     }
9979 
9980     static DWORD WINAPI _events_thread(void* arg) {
9981       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
9982       const char *const title = (const char*)(((void**)arg)[1]);
9983       MSG msg;
9984       delete[] (void**)arg;
9985       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
9986       disp->_bmi.bmiHeader.biWidth = disp->width();
9987       disp->_bmi.bmiHeader.biHeight = -disp->height();
9988       disp->_bmi.bmiHeader.biPlanes = 1;
9989       disp->_bmi.bmiHeader.biBitCount = 32;
9990       disp->_bmi.bmiHeader.biCompression = BI_RGB;
9991       disp->_bmi.bmiHeader.biSizeImage = 0;
9992       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
9993       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
9994       disp->_bmi.bmiHeader.biClrUsed = 0;
9995       disp->_bmi.bmiHeader.biClrImportant = 0;
9996       disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
9997       if (!disp->_is_fullscreen) { // Normal window
9998         RECT rect;
9999         rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
10000         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10001         const int
10002           border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
10003           border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1);
10004         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
10005                                      WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
10006                                      disp->_width + 2*border1, disp->_height + border1 + border2,
10007                                      0,0,0,&(disp->_ccs));
10008         if (!disp->_is_closed) {
10009           GetWindowRect(disp->_window,&rect);
10010           disp->_window_x = rect.left + border1;
10011           disp->_window_y = rect.top + border2;
10012         } else disp->_window_x = disp->_window_y = 0;
10013       } else { // Fullscreen window
10014         const unsigned int
10015           sx = (unsigned int)screen_width(),
10016           sy = (unsigned int)screen_height();
10017         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
10018                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE),
10019                                       (sx - disp->_width)/2,
10020                                       (sy - disp->_height)/2,
10021                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
10022         disp->_window_x = disp->_window_y = 0;
10023       }
10024       SetForegroundWindow(disp->_window);
10025       disp->_hdc = GetDC(disp->_window);
10026       disp->_window_width = disp->_width;
10027       disp->_window_height = disp->_height;
10028       disp->flush();
10029 #ifdef _WIN64
10030       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
10031       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
10032 #else
10033       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
10034       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
10035 #endif
10036       SetEvent(disp->_is_created);
10037       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
10038       return 0;
10039     }
10040 
10041     CImgDisplay& _update_window_pos() {
10042       if (_is_closed) _window_x = _window_y = -1;
10043       else {
10044         RECT rect;
10045         rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
10046         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10047         const int
10048           border1 = (int)((rect.right - rect.left + 1 - _width)/2),
10049           border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
10050         GetWindowRect(_window,&rect);
10051         _window_x = rect.left + border1;
10052         _window_y = rect.top + border2;
10053       }
10054       return *this;
10055     }
10056 
10057     void _init_fullscreen() {
10058       _background_window = 0;
10059       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
10060       else {
10061         DEVMODE mode;
10062         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
10063         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
10064           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
10065           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
10066             bestbpp = mode.dmBitsPerPel;
10067             ibest = imode;
10068             bw = nw; bh = nh;
10069           }
10070         }
10071         if (bestbpp) {
10072           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
10073           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
10074           EnumDisplaySettings(0,ibest,&mode);
10075           ChangeDisplaySettings(&mode,0);
10076         } else _curr_mode.dmSize = 0;
10077 
10078         const unsigned int
10079           sx = (unsigned int)screen_width(),
10080           sy = (unsigned int)screen_height();
10081         if (sx!=_width || sy!=_height) {
10082           CLIENTCREATESTRUCT background_ccs;
10083           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
10084           SetForegroundWindow(_background_window);
10085         }
10086       }
10087     }
10088 
10089     void _desinit_fullscreen() {
10090       if (!_is_fullscreen) return;
10091       if (_background_window) DestroyWindow(_background_window);
10092       _background_window = 0;
10093       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
10094       _is_fullscreen = false;
10095     }
10096 
10097     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
10098                          const unsigned int normalization_type=3,
10099                          const bool fullscreen_flag=false, const bool closed_flag=false) {
10100 
10101       // Allocate space for window title
10102       const char *const nptitle = ptitle?ptitle:"";
10103       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
10104       char *const tmp_title = s?new char[s]:0;
10105       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
10106 
10107       // Destroy previous window if existing
10108       if (!is_empty()) assign();
10109 
10110       // Set display variables
10111       _width = std::min(dimw,(unsigned int)screen_width());
10112       _height = std::min(dimh,(unsigned int)screen_height());
10113       _normalization = normalization_type<4?normalization_type:3;
10114       _is_fullscreen = fullscreen_flag;
10115       _window_x = _window_y = 0;
10116       _is_closed = closed_flag;
10117       _is_cursor_visible = true;
10118       _is_mouse_tracked = false;
10119       _title = tmp_title;
10120       flush();
10121       if (_is_fullscreen) _init_fullscreen();
10122 
10123       // Create event thread
10124       void *const arg = (void*)(new void*[2]);
10125       ((void**)arg)[0] = (void*)this;
10126       ((void**)arg)[1] = (void*)_title;
10127       _mutex = CreateMutex(0,FALSE,0);
10128       _is_created = CreateEvent(0,FALSE,FALSE,0);
10129       _thread = CreateThread(0,0,_events_thread,arg,0,0);
10130       WaitForSingleObject(_is_created,INFINITE);
10131       return *this;
10132     }
10133 
10134     CImgDisplay& assign() {
10135       if (is_empty()) return flush();
10136       DestroyWindow(_window);
10137       TerminateThread(_thread,0);
10138       delete[] _data;
10139       delete[] _title;
10140       _data = 0;
10141       _title = 0;
10142       if (_is_fullscreen) _desinit_fullscreen();
10143       _width = _height = _normalization = _window_width = _window_height = 0;
10144       _window_x = _window_y = 0;
10145       _is_fullscreen = false;
10146       _is_closed = true;
10147       _min = _max = 0;
10148       _title = 0;
10149       flush();
10150       return *this;
10151     }
10152 
10153     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
10154                         const unsigned int normalization_type=3,
10155                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10156       if (!dimw || !dimh) return assign();
10157       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
10158       _min = _max = 0;
10159       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
10160       return paint();
10161     }
10162 
10163     template<typename T>
10164     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
10165                         const unsigned int normalization_type=3,
10166                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10167       if (!img) return assign();
10168       CImg<T> tmp;
10169       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10170                                                                            (img._height - 1)/2,
10171                                                                            (img._depth - 1)/2));
10172       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10173       if (_normalization==2) _min = (float)nimg.min_max(_max);
10174       return display(nimg);
10175     }
10176 
10177     template<typename T>
10178     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
10179                         const unsigned int normalization_type=3,
10180                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10181       if (!list) return assign();
10182       CImg<T> tmp;
10183       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10184                                                                                            (img._height - 1)/2,
10185                                                                                            (img._depth - 1)/2));
10186       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10187       if (_normalization==2) _min = (float)nimg.min_max(_max);
10188       return display(nimg);
10189     }
10190 
10191     CImgDisplay& assign(const CImgDisplay& disp) {
10192       if (!disp) return assign();
10193       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
10194       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
10195       return paint();
10196     }
10197 
10198     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
10199       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
10200       if (is_empty()) return assign(nwidth,nheight);
10201       const unsigned int
10202         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
10203         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
10204         dimx = tmpdimx?tmpdimx:1,
10205         dimy = tmpdimy?tmpdimy:1;
10206       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
10207         if (_window_width!=dimx || _window_height!=dimy) {
10208           RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
10209           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10210           const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
10211           SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
10212         }
10213         if (_width!=dimx || _height!=dimy) {
10214           unsigned int *const ndata = new unsigned int[dimx*dimy];
10215           if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
10216           else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
10217           delete[] _data;
10218           _data = ndata;
10219           _bmi.bmiHeader.biWidth = (LONG)dimx;
10220           _bmi.bmiHeader.biHeight = -(int)dimy;
10221           _width = dimx;
10222           _height = dimy;
10223         }
10224         _window_width = dimx; _window_height = dimy;
10225         show();
10226       }
10227       _is_resized = false;
10228       if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
10229       if (force_redraw) return paint();
10230       return *this;
10231     }
10232 
10233     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
10234       if (is_empty()) return *this;
10235       if (force_redraw) {
10236         const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
10237         void *odata = std::malloc(buf_size);
10238         if (odata) {
10239           std::memcpy(odata,_data,buf_size);
10240           assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10241           std::memcpy(_data,odata,buf_size);
10242           std::free(odata);
10243         }
10244         return paint();
10245       }
10246       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10247     }
10248 
10249     CImgDisplay& show() {
10250       if (is_empty() || !_is_closed) return *this;
10251       _is_closed = false;
10252       if (_is_fullscreen) _init_fullscreen();
10253       ShowWindow(_window,SW_SHOW);
10254       _update_window_pos();
10255       return paint();
10256     }
10257 
10258     CImgDisplay& close() {
10259       if (is_empty() || _is_closed) return *this;
10260       _is_closed = true;
10261       if (_is_fullscreen) _desinit_fullscreen();
10262       ShowWindow(_window,SW_HIDE);
10263       _window_x = _window_y = 0;
10264       return *this;
10265     }
10266 
10267     CImgDisplay& move(const int posx, const int posy) {
10268       if (is_empty()) return *this;
10269       if (_window_x!=posx || _window_y!=posy) {
10270         if (!_is_fullscreen) {
10271           RECT rect;
10272           rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1;
10273           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10274           const int
10275             border1 = (int)((rect.right - rect.left + 1 -_width)/2),
10276             border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
10277           SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
10278         } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
10279         _window_x = posx;
10280         _window_y = posy;
10281         show();
10282       }
10283       _is_moved = false;
10284       return *this;
10285     }
10286 
10287     CImgDisplay& show_mouse() {
10288       if (is_empty()) return *this;
10289       _is_cursor_visible = true;
10290       return *this;
10291     }
10292 
10293     CImgDisplay& hide_mouse() {
10294       if (is_empty()) return *this;
10295       _is_cursor_visible = false;
10296       return *this;
10297     }
10298 
10299     CImgDisplay& set_mouse(const int posx, const int posy) {
10300       if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
10301       _update_window_pos();
10302       const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
10303       if (res) { _mouse_x = posx; _mouse_y = posy; }
10304       return *this;
10305     }
10306 
10307     CImgDisplay& set_title(const char *const format, ...) {
10308       if (is_empty()) return *this;
10309       char *const tmp = new char[1024];
10310       va_list ap;
10311       va_start(ap, format);
10312       cimg_vsnprintf(tmp,1024,format,ap);
10313       va_end(ap);
10314       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
10315       delete[] _title;
10316       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
10317       _title = new char[s];
10318       std::memcpy(_title,tmp,s*sizeof(char));
10319       SetWindowTextA(_window, tmp);
10320       delete[] tmp;
10321       return *this;
10322     }
10323 
10324     template<typename T>
10325     CImgDisplay& display(const CImg<T>& img) {
10326       if (!img)
10327         throw CImgArgumentException(_cimgdisplay_instance
10328                                     "display(): Empty specified image.",
10329                                     cimgdisplay_instance);
10330       if (is_empty()) return assign(img);
10331       return render(img).paint();
10332     }
10333 
10334     CImgDisplay& paint() {
10335       if (_is_closed) return *this;
10336       WaitForSingleObject(_mutex,INFINITE);
10337       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
10338       ReleaseMutex(_mutex);
10339       return *this;
10340     }
10341 
10342     template<typename T>
10343     CImgDisplay& render(const CImg<T>& img) {
10344       if (!img)
10345         throw CImgArgumentException(_cimgdisplay_instance
10346                                     "render(): Empty specified image.",
10347                                     cimgdisplay_instance);
10348 
10349       if (is_empty()) return *this;
10350       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
10351                                                              (img._depth - 1)/2));
10352 
10353       const T
10354         *data1 = img._data,
10355         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
10356         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
10357 
10358       WaitForSingleObject(_mutex,INFINITE);
10359       unsigned int
10360         *const ndata = (img._width==_width && img._height==_height)?_data:
10361         new unsigned int[(size_t)img._width*img._height],
10362         *ptrd = ndata;
10363 
10364       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
10365         _min = _max = 0;
10366         switch (img._spectrum) {
10367         case 1 : {
10368           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10369             const unsigned char val = (unsigned char)*(data1++);
10370             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
10371           }
10372         } break;
10373         case 2 : {
10374           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10375             const unsigned char
10376               R = (unsigned char)*(data1++),
10377               G = (unsigned char)*(data2++);
10378             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
10379           }
10380         } break;
10381         default : {
10382           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10383             const unsigned char
10384               R = (unsigned char)*(data1++),
10385               G = (unsigned char)*(data2++),
10386               B = (unsigned char)*(data3++);
10387             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
10388           }
10389         }
10390         }
10391       } else {
10392         if (_normalization==3) {
10393           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
10394           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
10395         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
10396         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
10397         switch (img._spectrum) {
10398         case 1 : {
10399           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10400             const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10401             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
10402           }
10403         } break;
10404         case 2 : {
10405           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10406             const unsigned char
10407               R = (unsigned char)((*(data1++) - _min)*mm),
10408               G = (unsigned char)((*(data2++) - _min)*mm);
10409             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
10410           }
10411         } break;
10412         default : {
10413           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10414             const unsigned char
10415               R = (unsigned char)((*(data1++) - _min)*mm),
10416               G = (unsigned char)((*(data2++) - _min)*mm),
10417               B = (unsigned char)((*(data3++) - _min)*mm);
10418             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
10419           }
10420         }
10421         }
10422       }
10423       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
10424       ReleaseMutex(_mutex);
10425       return *this;
10426     }
10427 
10428     template<typename T>
10429     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
10430       img.assign();
10431       HDC hScreen = GetDC(GetDesktopWindow());
10432       if (hScreen) {
10433         const int
10434           width = GetDeviceCaps(hScreen,HORZRES),
10435           height = GetDeviceCaps(hScreen,VERTRES);
10436         int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
10437         if (_x0>_x1) cimg::swap(_x0,_x1);
10438         if (_y0>_y1) cimg::swap(_y0,_y1);
10439         if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
10440           _x0 = std::max(_x0,0);
10441           _y0 = std::max(_y0,0);
10442           _x1 = std::min(_x1,width - 1);
10443           _y1 = std::min(_y1,height - 1);
10444           const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
10445           HDC hdcMem = CreateCompatibleDC(hScreen);
10446           if (hdcMem) {
10447             HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
10448             if (hBitmap) {
10449               HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
10450               if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
10451                 BITMAPINFOHEADER bmi;
10452                 bmi.biSize = sizeof(BITMAPINFOHEADER);
10453                 bmi.biWidth = bw;
10454                 bmi.biHeight = -bh;
10455                 bmi.biPlanes = 1;
10456                 bmi.biBitCount = 32;
10457                 bmi.biCompression = BI_RGB;
10458                 bmi.biSizeImage = 0;
10459                 bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
10460                 bmi.biClrUsed = bmi.biClrImportant = 0;
10461                 unsigned char *buf = new unsigned char[4*bw*bh];
10462                 if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
10463                   img.assign(bw,bh,1,3);
10464                   const unsigned char *ptrs = buf;
10465                   T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
10466                   cimg_forXY(img,x,y) {
10467                     *(pR++) = (T)ptrs[2];
10468                     *(pG++) = (T)ptrs[1];
10469                     *(pB++) = (T)ptrs[0];
10470                     ptrs+=4;
10471                   }
10472                 }
10473                 delete[] buf;
10474               }
10475               DeleteObject(hBitmap);
10476             }
10477             DeleteDC(hdcMem);
10478           }
10479         }
10480         ReleaseDC(GetDesktopWindow(),hScreen);
10481       }
10482       if (img.is_empty())
10483         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
10484                                    "with coordinates (%d,%d)-(%d,%d).",
10485                                    x0,y0,x1,y1);
10486     }
10487 
10488     template<typename T>
10489     const CImgDisplay& snapshot(CImg<T>& img) const {
10490       if (is_empty()) { img.assign(); return *this; }
10491       const unsigned int *ptrs = _data;
10492       img.assign(_width,_height,1,3);
10493       T
10494         *data1 = img.data(0,0,0,0),
10495         *data2 = img.data(0,0,0,1),
10496         *data3 = img.data(0,0,0,2);
10497       for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10498         const unsigned int val = *(ptrs++);
10499         *(data1++) = (T)(unsigned char)(val>>16);
10500         *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
10501         *(data3++) = (T)(unsigned char)(val&0xFF);
10502       }
10503       return *this;
10504     }
10505 #endif
10506 
10507     //@}
10508   };
10509 
10510   /*
10511    #--------------------------------------
10512    #
10513    #
10514    #
10515    # Definition of the CImg<T> structure
10516    #
10517    #
10518    #
10519    #--------------------------------------
10520    */
10521 
10522   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
10523   /**
10524      This is the main class of the %CImg Library. It declares and constructs
10525      an image, allows access to its pixel values, and is able to perform various image operations.
10526 
10527      \par Image representation
10528 
10529      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
10530      each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
10531      and number of channels.
10532      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
10533      while the number of channels is rather used as a vector-valued dimension
10534      (it may describe the R,G,B color channels for instance).
10535      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
10536 
10537      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
10538      as well as images with less dimensions (1d scalar signal, 2d color images, ...).
10539      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
10540 
10541      Concerning the pixel value type \c T:
10542      fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
10543      unsigned long, long, float, double, ... </tt>.
10544      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
10545      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
10546      images that have floating-point pixel values. The default value for the template T is \c float.
10547      Using your own template types may be possible. However, you will certainly have to define the complete set
10548      of arithmetic and logical operators for your class.
10549 
10550      \par Image structure
10551 
10552      The \c CImg<T> structure contains \e six fields:
10553      - \c _width defines the number of \a columns of the image (size along the X-axis).
10554      - \c _height defines the number of \a rows of the image (size along the Y-axis).
10555      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
10556      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
10557      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
10558      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
10559        another image.
10560 
10561      You can access these fields publicly although it is recommended to use the dedicated functions
10562      width(), height(), depth(), spectrum() and ptr() to do so.
10563      Image dimensions are not limited to a specific range (as long as you got enough available memory).
10564      A value of \e 1 usually means that the corresponding dimension is \a flat.
10565      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
10566      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
10567      (a CImgInstanceException will be thrown instead).
10568      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
10569 
10570      \par Image declaration and construction
10571 
10572      Declaring an image can be done by using one of the several available constructors.
10573      Here is a list of the most used:
10574 
10575      - Construct images from arbitrary dimensions:
10576          - <tt>CImg<char> img;</tt> declares an empty image.
10577          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
10578          \c unsigned \c char pixel values.
10579          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
10580          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
10581          (colors are stored as an image with three channels).
10582          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
10583          (with \c double pixel values).
10584          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
10585          (with \c float pixels, which is the default value of the template parameter \c T).
10586          - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
10587          do it, or use the specific constructor taking 5 parameters like this:
10588          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
10589 
10590      - Construct images from filenames:
10591          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
10592          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
10593          file "analyze.hdr".
10594          - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
10595          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
10596 
10597      - Construct images from C-style arrays:
10598          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
10599          \c data_buffer (of size 256x256=65536).
10600          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
10601          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
10602 
10603          The complete list of constructors can be found <a href="#constructors">here</a>.
10604 
10605      \par Most useful functions
10606 
10607      The \c CImg<T> class contains a lot of functions that operates on images.
10608      Some of the most useful are:
10609 
10610      - operator()(): Read or write pixel values.
10611      - display(): displays the image in a new window.
10612   **/
10613   template<typename T>
10614   struct CImg {
10615 
10616     unsigned int _width, _height, _depth, _spectrum;
10617     bool _is_shared;
10618     T *_data;
10619 
10620     //! Simple iterator type, to loop through each pixel value of an image instance.
10621     /**
10622        \note
10623        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
10624        - You will seldom have to use iterators in %CImg, most classical operations
10625          being achieved (often in a faster way) using methods of \c CImg<T>.
10626        \par Example
10627        \code
10628        CImg<float> img("reference.jpg");                                         // Load image from file.
10629        // Set all pixels to '0', with a CImg iterator.
10630        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
10631        img.fill(0);                                                              // Do the same with a built-in method.
10632        \endcode
10633    **/
10634     typedef T* iterator;
10635 
10636     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
10637     /**
10638        \note
10639        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
10640        - You will seldom have to use iterators in %CImg, most classical operations
10641          being achieved (often in a faster way) using methods of \c CImg<T>.
10642        \par Example
10643        \code
10644        const CImg<float> img("reference.jpg");                                    // Load image from file.
10645        float sum = 0;
10646        // Compute sum of all pixel values, with a CImg iterator.
10647        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
10648        const float sum2 = img.sum();                                              // Do the same with a built-in method.
10649        \endcode
10650     **/
10651     typedef const T* const_iterator;
10652 
10653     //! Pixel value type.
10654     /**
10655        Refer to the type of the pixel values of an image instance.
10656        \note
10657        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
10658        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
10659          compatibility with STL naming conventions.
10660     **/
10661     typedef T value_type;
10662 
10663     // Define common types related to template type T.
10664     typedef typename cimg::superset<T,bool>::type Tbool;
10665     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
10666     typedef typename cimg::superset<T,char>::type Tchar;
10667     typedef typename cimg::superset<T,unsigned short>::type Tushort;
10668     typedef typename cimg::superset<T,short>::type Tshort;
10669     typedef typename cimg::superset<T,unsigned int>::type Tuint;
10670     typedef typename cimg::superset<T,int>::type Tint;
10671     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
10672     typedef typename cimg::superset<T,cimg_long>::type Tlong;
10673     typedef typename cimg::superset<T,float>::type Tfloat;
10674     typedef typename cimg::superset<T,double>::type Tdouble;
10675     typedef typename cimg::last<T,bool>::type boolT;
10676     typedef typename cimg::last<T,unsigned char>::type ucharT;
10677     typedef typename cimg::last<T,char>::type charT;
10678     typedef typename cimg::last<T,unsigned short>::type ushortT;
10679     typedef typename cimg::last<T,short>::type shortT;
10680     typedef typename cimg::last<T,unsigned int>::type uintT;
10681     typedef typename cimg::last<T,int>::type intT;
10682     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
10683     typedef typename cimg::last<T,cimg_long>::type longT;
10684     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
10685     typedef typename cimg::last<T,cimg_int64>::type int64T;
10686     typedef typename cimg::last<T,float>::type floatT;
10687     typedef typename cimg::last<T,double>::type doubleT;
10688 
10689     //@}
10690     //---------------------------
10691     //
10692     //! \name Plugins
10693     //@{
10694     //---------------------------
10695 #ifdef cimg_plugin
10696 #include cimg_plugin
10697 #endif
10698 #ifdef cimg_plugin1
10699 #include cimg_plugin1
10700 #endif
10701 #ifdef cimg_plugin2
10702 #include cimg_plugin2
10703 #endif
10704 #ifdef cimg_plugin3
10705 #include cimg_plugin3
10706 #endif
10707 #ifdef cimg_plugin4
10708 #include cimg_plugin4
10709 #endif
10710 #ifdef cimg_plugin5
10711 #include cimg_plugin5
10712 #endif
10713 #ifdef cimg_plugin6
10714 #include cimg_plugin6
10715 #endif
10716 #ifdef cimg_plugin7
10717 #include cimg_plugin7
10718 #endif
10719 #ifdef cimg_plugin8
10720 #include cimg_plugin8
10721 #endif
10722 
10723     //@}
10724     //---------------------------------------------------------
10725     //
10726     //! \name Constructors / Destructor / Instance Management
10727     //@{
10728     //---------------------------------------------------------
10729 
10730     //! Destroy image.
10731     /**
10732        \note
10733        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
10734        - Destroying an empty or shared image does nothing actually.
10735        \warning
10736        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
10737          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
10738          (to a deallocated buffer).
10739     **/
10740     ~CImg() {
10741       if (!_is_shared) delete[] _data;
10742     }
10743 
10744     //! Construct empty image.
10745     /**
10746        \note
10747        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
10748          are set to \c 0, as well as its pixel buffer pointer data().
10749        - An empty image may be re-assigned afterwards, e.g. with the family of
10750          assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
10751          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
10752        - An empty image is never shared.
10753        \par Example
10754        \code
10755        CImg<float> img1, img2;      // Construct two empty images.
10756        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image.
10757        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'.
10758        img2.assign();               // Re-assign 'img2' to be an empty image again.
10759        \endcode
10760     **/
10761     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
10762 
10763     //! Construct image with specified size.
10764     /**
10765        \param size_x Image width().
10766        \param size_y Image height().
10767        \param size_z Image depth().
10768        \param size_c Image spectrum() (number of channels).
10769        \note
10770        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
10771          for each constructed image instance.
10772        - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
10773          an \e empty image.
10774        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
10775          (e.g. when requested size is too big for available memory).
10776        \warning
10777        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
10778          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
10779          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
10780        \par Example
10781        \code
10782        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values.
10783        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'.
10784        \endcode
10785     **/
10786     explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
10787                   const unsigned int size_z=1, const unsigned int size_c=1):
10788       _is_shared(false) {
10789       size_t siz = (size_t)size_x*size_y*size_z*size_c;
10790       if (siz) {
10791         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10792         try { _data = new T[siz]; } catch (...) {
10793           _width = _height = _depth = _spectrum = 0; _data = 0;
10794           throw CImgInstanceException(_cimg_instance
10795                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10796                                       cimg_instance,
10797                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10798                                       size_x,size_y,size_z,size_c);
10799         }
10800       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
10801     }
10802 
10803     //! Construct image with specified size and initialize pixel values.
10804     /**
10805        \param size_x Image width().
10806        \param size_y Image height().
10807        \param size_z Image depth().
10808        \param size_c Image spectrum() (number of channels).
10809        \param value Initialization value.
10810        \note
10811        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
10812          but it also fills the pixel buffer with the specified \c value.
10813        \warning
10814        - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
10815          (e.g. RGB vector, for color images).
10816          For this task, you may use fillC() after construction.
10817     **/
10818     CImg(const unsigned int size_x, const unsigned int size_y,
10819          const unsigned int size_z, const unsigned int size_c, const T& value):
10820       _is_shared(false) {
10821       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
10822       if (siz) {
10823         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10824         try { _data = new T[siz]; } catch (...) {
10825           _width = _height = _depth = _spectrum = 0; _data = 0;
10826           throw CImgInstanceException(_cimg_instance
10827                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10828                                       cimg_instance,
10829                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10830                                       size_x,size_y,size_z,size_c);
10831         }
10832         fill(value);
10833       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
10834     }
10835 
10836     //! Construct image with specified size and initialize pixel values from a sequence of integers.
10837     /**
10838        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
10839        with pixels of type \c T, and initialize pixel
10840        values from the specified sequence of integers \c value0,\c value1,\c ...
10841        \param size_x Image width().
10842        \param size_y Image height().
10843        \param size_z Image depth().
10844        \param size_c Image spectrum() (number of channels).
10845        \param value0 First value of the initialization sequence (must be an \e integer).
10846        \param value1 Second value of the initialization sequence (must be an \e integer).
10847        \param ...
10848        \note
10849        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
10850          the pixel buffer with a sequence of specified integer values.
10851        \warning
10852        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
10853          Otherwise, the constructor may crash or fill your image pixels with garbage.
10854        \par Example
10855        \code
10856        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
10857                              0,255,0,255,  // Set the 4 values for the red component.
10858                              0,0,255,255,  // Set the 4 values for the green component.
10859                              64,64,64,64); // Set the 4 values for the blue component.
10860        img.resize(150,150).display();
10861        \endcode
10862        \image html ref_constructor1.jpg
10863      **/
10864     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
10865          const int value0, const int value1, ...):
10866       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10867 #define _CImg_stdarg(img,a0,a1,N,t) { \
10868 	size_t _siz = (size_t)N; \
10869 	if (_siz--) { \
10870 	  va_list ap; \
10871 	  va_start(ap,a1); \
10872 	  T *ptrd = (img)._data; \
10873 	  *(ptrd++) = (T)a0; \
10874 	  if (_siz--) { \
10875 	    *(ptrd++) = (T)a1; \
10876 	    for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
10877 	  } \
10878 	  va_end(ap); \
10879 	} \
10880       }
10881       assign(size_x,size_y,size_z,size_c);
10882       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
10883     }
10884 
10885 #if cimg_use_cpp11==1
10886     //! Construct image with specified size and initialize pixel values from an initializer list of integers.
10887     /**
10888        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
10889        with pixels of type \c T, and initialize pixel
10890        values from the specified initializer list of integers { \c value0,\c value1,\c ... }
10891        \param size_x Image width().
10892        \param size_y Image height().
10893        \param size_z Image depth().
10894        \param size_c Image spectrum() (number of channels).
10895        \param { value0, value1, ... } Initialization list
10896        \param repeat_values Tells if the value filling process is repeated over the image.
10897 
10898        \note
10899        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
10900          the pixel buffer with a sequence of specified integer values.
10901        \par Example
10902        \code
10903        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
10904                              { 0,255,0,255,    // Set the 4 values for the red component.
10905                                0,0,255,255,    // Set the 4 values for the green component.
10906                                64,64,64,64 }); // Set the 4 values for the blue component.
10907        img.resize(150,150).display();
10908        \endcode
10909        \image html ref_constructor1.jpg
10910     **/
10911     template<typename t>
10912     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
10913          const 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 #define _cimg_constructor_cpp11(repeat_values) \
10917   auto it = values.begin(); \
10918   size_t siz = size(); \
10919   if (repeat_values) for (T *ptrd = _data; siz--; ) { \
10920     *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
10921   else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
10922       assign(size_x,size_y,size_z,size_c);
10923       _cimg_constructor_cpp11(repeat_values);
10924     }
10925 
10926     template<typename t>
10927     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
10928          std::initializer_list<t> values,
10929 	 const bool repeat_values=true):
10930       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10931       assign(size_x,size_y,size_z);
10932       _cimg_constructor_cpp11(repeat_values);
10933     }
10934 
10935     template<typename t>
10936     CImg(const unsigned int size_x, const unsigned int size_y,
10937          std::initializer_list<t> values,
10938 	 const bool repeat_values=true):
10939       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10940       assign(size_x,size_y);
10941       _cimg_constructor_cpp11(repeat_values);
10942     }
10943 
10944     template<typename t>
10945     CImg(const unsigned int size_x,
10946          std::initializer_list<t> values,
10947          const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10948       assign(size_x);
10949       _cimg_constructor_cpp11(repeat_values);
10950     }
10951 
10952     //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
10953     /**
10954        Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
10955        with pixels of type \c T, and initialize pixel
10956        values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
10957        given by the size of the initializer list.
10958        \param { value0, value1, ... } Initialization list
10959        \note
10960        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
10961          but it also fills the pixel buffer with a sequence of specified integer values.
10962        \par Example
10963        \code
10964        const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values.
10965        img.resize(150,150).display();
10966        \endcode
10967        \image html ref_constructor1.jpg
10968      **/
10969     template<typename t>
10970     CImg(const std::initializer_list<t> values):
10971       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10972       assign(values.size(),1,1,1);
10973       auto it = values.begin();
10974       unsigned int siz = _width;
10975       for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
10976     }
10977 
10978     template<typename t>
10979     CImg<T> & operator=(std::initializer_list<t> values) {
10980       _cimg_constructor_cpp11(siz>values.size());
10981       return *this;
10982     }
10983 #endif
10984 
10985     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
10986     /**
10987        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,
10988        and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
10989        \param size_x Image width().
10990        \param size_y Image height().
10991        \param size_z Image depth().
10992        \param size_c Image spectrum() (number of channels).
10993        \param value0 First value of the initialization sequence (must be a \e double).
10994        \param value1 Second value of the initialization sequence (must be a \e double).
10995        \param ...
10996        \note
10997        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
10998          takes a sequence of double values instead of integers.
10999        \warning
11000        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
11001          Otherwise, the constructor may crash or fill your image with garbage.
11002          For instance, the code below will probably crash on most platforms:
11003          \code
11004          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
11005          \endcode
11006      **/
11007     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11008          const double value0, const double value1, ...):
11009       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11010       assign(size_x,size_y,size_z,size_c);
11011       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
11012     }
11013 
11014     //! Construct image with specified size and initialize pixel values from a value string.
11015     /**
11016        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,
11017        and initializes pixel values from the specified string \c values.
11018        \param size_x Image width().
11019        \param size_y Image height().
11020        \param size_z Image depth().
11021        \param size_c Image spectrum() (number of channels).
11022        \param values Value string describing the way pixel values are set.
11023        \param repeat_values Tells if the value filling process is repeated over the image.
11024        \note
11025        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11026          the pixel buffer with values described in the value string \c values.
11027        - Value string \c values may describe two different filling processes:
11028          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
11029            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
11030          - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
11031            In this case, parameter \c repeat_values is pointless.
11032        - For both cases, specifying \c repeat_values is mandatory.
11033          It disambiguates the possible overloading of constructor
11034          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
11035        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
11036        \par Example
11037        \code
11038        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence.
11039                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula.
11040        (img1,img2).display();
11041        \endcode
11042        \image html ref_constructor2.jpg
11043      **/
11044     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11045 	 const char *const values, const bool repeat_values):_is_shared(false) {
11046       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11047       if (siz) {
11048         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11049         try { _data = new T[siz]; } catch (...) {
11050           _width = _height = _depth = _spectrum = 0; _data = 0;
11051           throw CImgInstanceException(_cimg_instance
11052                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11053                                       cimg_instance,
11054                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11055                                       size_x,size_y,size_z,size_c);
11056         }
11057         fill(values,repeat_values);
11058       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11059     }
11060 
11061     //! Construct image with specified size and initialize pixel values from a memory buffer.
11062     /**
11063        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,
11064        and initializes pixel values from the specified \c t* memory buffer.
11065        \param values Pointer to the input memory buffer.
11066        \param size_x Image width().
11067        \param size_y Image height().
11068        \param size_z Image depth().
11069        \param size_c Image spectrum() (number of channels).
11070        \param is_shared Tells if input memory buffer must be shared by the current instance.
11071        \note
11072        - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
11073          and values from the specified input buffer are copied to the instance buffer.
11074          If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
11075        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
11076          own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
11077          image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
11078        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11079          (e.g. when requested size is too big for available memory).
11080        \warning
11081        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
11082          (e.g. already deallocated).
11083        \par Example
11084        \code
11085        unsigned char tab[256*256] = { 0 };
11086        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'.
11087                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'.
11088        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'.
11089        \endcode
11090     **/
11091     template<typename t>
11092     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
11093          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
11094       if (is_shared) {
11095         _width = _height = _depth = _spectrum = 0; _data = 0;
11096         throw CImgArgumentException(_cimg_instance
11097                                     "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
11098                                     "from a (%s*) buffer (pixel types are different).",
11099                                     cimg_instance,
11100                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
11101       }
11102       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11103       if (values && siz) {
11104         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11105         try { _data = new T[siz]; } catch (...) {
11106           _width = _height = _depth = _spectrum = 0; _data = 0;
11107           throw CImgInstanceException(_cimg_instance
11108                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11109                                       cimg_instance,
11110                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11111                                       size_x,size_y,size_z,size_c);
11112 
11113         }
11114         const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11115       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11116     }
11117 
11118     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
11119     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
11120          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
11121       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11122       if (values && siz) {
11123         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
11124         if (_is_shared) _data = const_cast<T*>(values);
11125         else {
11126           try { _data = new T[siz]; } catch (...) {
11127             _width = _height = _depth = _spectrum = 0; _data = 0;
11128             throw CImgInstanceException(_cimg_instance
11129                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11130                                         cimg_instance,
11131                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11132                                         size_x,size_y,size_z,size_c);
11133           }
11134           std::memcpy(_data,values,siz*sizeof(T));
11135         }
11136       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11137     }
11138 
11139     //! Construct image from reading an image file.
11140     /**
11141        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
11142        an image file.
11143        \param filename Filename, as a C-string.
11144        \note
11145        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
11146          dimensions and pixel values from the specified image file.
11147        - The recognition of the image file format by %CImg higly depends on the tools installed on your system
11148          and on the external libraries you used to link your code against.
11149        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
11150          file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
11151        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
11152          recognized.
11153        \par Example
11154        \code
11155        const CImg<float> img("reference.jpg");
11156        img.display();
11157        \endcode
11158        \image html ref_image.jpg
11159     **/
11160     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11161       assign(filename);
11162     }
11163 
11164     //! Construct image copy.
11165     /**
11166        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
11167        \param img Input image to copy.
11168        \note
11169        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
11170          input image \c img.
11171        - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
11172          \e shared, and shares its pixel buffer with \c img.
11173          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
11174          This behavior is needful to allow functions to return shared images.
11175        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
11176          image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
11177          \c t are different.
11178        - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
11179          with different types.
11180        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11181          (e.g. not enough available memory).
11182     **/
11183     template<typename t>
11184     CImg(const CImg<t>& img):_is_shared(false) {
11185       const size_t siz = (size_t)img.size();
11186       if (img._data && siz) {
11187         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11188         try { _data = new T[siz]; } catch (...) {
11189           _width = _height = _depth = _spectrum = 0; _data = 0;
11190           throw CImgInstanceException(_cimg_instance
11191                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11192                                       cimg_instance,
11193                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11194                                       img._width,img._height,img._depth,img._spectrum);
11195         }
11196         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11197       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11198     }
11199 
11200     //! Construct image copy \specialization.
11201     CImg(const CImg<T>& img) {
11202       const size_t siz = (size_t)img.size();
11203       if (img._data && siz) {
11204         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11205         _is_shared = img._is_shared;
11206         if (_is_shared) _data = const_cast<T*>(img._data);
11207         else {
11208           try { _data = new T[siz]; } catch (...) {
11209             _width = _height = _depth = _spectrum = 0; _data = 0;
11210             throw CImgInstanceException(_cimg_instance
11211                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11212                                         cimg_instance,
11213                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11214                                         img._width,img._height,img._depth,img._spectrum);
11215 
11216           }
11217           std::memcpy(_data,img._data,siz*sizeof(T));
11218         }
11219       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11220     }
11221 
11222     //! Advanced copy constructor.
11223     /**
11224        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
11225        while forcing the shared state of the constructed copy.
11226        \param img Input image to copy.
11227        \param is_shared Tells about the shared state of the constructed copy.
11228        \note
11229        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
11230          the constructed image, which does not depend anymore on the shared state of the input image \c img:
11231          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
11232            For that case, the pixel types \c T and \c t \e must be the same.
11233          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
11234            image \c img is shared or not.
11235        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
11236     **/
11237     template<typename t>
11238     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
11239       if (is_shared) {
11240         _width = _height = _depth = _spectrum = 0; _data = 0;
11241         throw CImgArgumentException(_cimg_instance
11242                                     "CImg(): Invalid construction request of a shared instance from a "
11243                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
11244                                     cimg_instance,
11245                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
11246       }
11247       const size_t siz = (size_t)img.size();
11248       if (img._data && siz) {
11249         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11250         try { _data = new T[siz]; } catch (...) {
11251           _width = _height = _depth = _spectrum = 0; _data = 0;
11252           throw CImgInstanceException(_cimg_instance
11253                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11254                                       cimg_instance,
11255                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11256                                       img._width,img._height,img._depth,img._spectrum);
11257         }
11258         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11259       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11260     }
11261 
11262     //! Advanced copy constructor \specialization.
11263     CImg(const CImg<T>& img, const bool is_shared) {
11264       const size_t siz = (size_t)img.size();
11265       if (img._data && siz) {
11266         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11267         _is_shared = is_shared;
11268         if (_is_shared) _data = const_cast<T*>(img._data);
11269         else {
11270           try { _data = new T[siz]; } catch (...) {
11271             _width = _height = _depth = _spectrum = 0; _data = 0;
11272             throw CImgInstanceException(_cimg_instance
11273                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11274                                         cimg_instance,
11275                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11276                                         img._width,img._height,img._depth,img._spectrum);
11277           }
11278           std::memcpy(_data,img._data,siz*sizeof(T));
11279         }
11280       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11281     }
11282 
11283     //! Construct image with dimensions borrowed from another image.
11284     /**
11285        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
11286        \c CImg<t> instance.
11287        \param img Input image from which dimensions are borrowed.
11288        \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
11289        \note
11290        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
11291          (\e not its pixel values) from an existing \c CImg<t> instance.
11292        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
11293          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
11294          instead.
11295        \par Example
11296        \code
11297        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image.
11298                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image.
11299                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image.
11300                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0').
11301        \endcode
11302      **/
11303     template<typename t>
11304     CImg(const CImg<t>& img, const char *const dimensions):
11305       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11306       assign(img,dimensions);
11307     }
11308 
11309     //! Construct image with dimensions borrowed from another image and initialize pixel values.
11310     /**
11311        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
11312        \c CImg<t> instance, and set all pixel values to specified \c value.
11313        \param img Input image from which dimensions are borrowed.
11314        \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
11315        \param value Value used for initialization.
11316        \note
11317        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
11318      **/
11319     template<typename t>
11320     CImg(const CImg<t>& img, const char *const dimensions, const T& value):
11321       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11322       assign(img,dimensions).fill(value);
11323     }
11324 
11325     //! Construct image from a display window.
11326     /**
11327        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
11328        \param disp Input display window.
11329        \note
11330        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
11331        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
11332          (i.e. a 2d color image).
11333        - The image pixels are read as 8-bits RGB values.
11334      **/
11335     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11336       disp.snapshot(*this);
11337     }
11338 
11339     // Constructor and assignment operator for rvalue references (c++11).
11340     // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
11341 #if cimg_use_cpp11==1
11342     CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11343       swap(img);
11344     }
11345     CImg<T>& operator=(CImg<T>&& img) {
11346       if (_is_shared) return assign(img);
11347       return img.swap(*this);
11348     }
11349 #endif
11350 
11351     //! Construct empty image \inplace.
11352     /**
11353        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
11354     **/
11355     CImg<T>& assign() {
11356       if (!_is_shared) delete[] _data;
11357       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
11358       return *this;
11359     }
11360 
11361     //! Construct image with specified size \inplace.
11362     /**
11363        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
11364     **/
11365     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
11366                     const unsigned int size_z=1, const unsigned int size_c=1) {
11367       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11368       if (!siz) return assign();
11369       const size_t curr_siz = (size_t)size();
11370       if (siz!=curr_siz) {
11371 	if (_is_shared)
11372           throw CImgArgumentException(_cimg_instance
11373                                       "assign(): Invalid assignement request of shared instance from specified "
11374                                       "image (%u,%u,%u,%u).",
11375                                       cimg_instance,
11376                                       size_x,size_y,size_z,size_c);
11377 	else {
11378           delete[] _data;
11379           try { _data = new T[siz]; } catch (...) {
11380             _width = _height = _depth = _spectrum = 0; _data = 0;
11381             throw CImgInstanceException(_cimg_instance
11382                                         "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11383                                         cimg_instance,
11384                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11385                                         size_x,size_y,size_z,size_c);
11386           }
11387         }
11388       }
11389       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11390       return *this;
11391     }
11392 
11393     //! Construct image with specified size and initialize pixel values \inplace.
11394     /**
11395        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
11396     **/
11397     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11398                     const unsigned int size_z, const unsigned int size_c, const T& value) {
11399       return assign(size_x,size_y,size_z,size_c).fill(value);
11400     }
11401 
11402     //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
11403     /**
11404        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
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 int value0, const int value1, ...) {
11409       assign(size_x,size_y,size_z,size_c);
11410       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
11411       return *this;
11412     }
11413 
11414     //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
11415     /**
11416        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
11417     **/
11418     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11419                     const unsigned int size_z, const unsigned int size_c,
11420                     const double value0, const double value1, ...) {
11421       assign(size_x,size_y,size_z,size_c);
11422       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
11423       return *this;
11424     }
11425 
11426     //! Construct image with specified size and initialize pixel values from a value string \inplace.
11427     /**
11428        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
11429     **/
11430     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11431                     const unsigned int size_z, const unsigned int size_c,
11432                     const char *const values, const bool repeat_values) {
11433       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
11434     }
11435 
11436     //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
11437     /**
11438        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
11439     **/
11440     template<typename t>
11441     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
11442                     const unsigned int size_z=1, const unsigned int size_c=1) {
11443       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11444       if (!values || !siz) return assign();
11445       assign(size_x,size_y,size_z,size_c);
11446       const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11447       return *this;
11448     }
11449 
11450     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
11451     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
11452                     const unsigned int size_z=1, const unsigned int size_c=1) {
11453       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11454       if (!values || !siz) return assign();
11455       const size_t curr_siz = (size_t)size();
11456       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
11457       if (_is_shared || values + siz<_data || values>=_data + size()) {
11458         assign(size_x,size_y,size_z,size_c);
11459         if (_is_shared) std::memmove(_data,values,siz*sizeof(T));
11460         else std::memcpy(_data,values,siz*sizeof(T));
11461       } else {
11462         T *new_data = 0;
11463         try { new_data = new T[siz]; } catch (...) {
11464           _width = _height = _depth = _spectrum = 0; _data = 0;
11465           throw CImgInstanceException(_cimg_instance
11466                                       "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11467                                       cimg_instance,
11468                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11469                                       size_x,size_y,size_z,size_c);
11470         }
11471         std::memcpy(new_data,values,siz*sizeof(T));
11472         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11473       }
11474       return *this;
11475     }
11476 
11477     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
11478     template<typename t>
11479     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
11480                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
11481       if (is_shared)
11482         throw CImgArgumentException(_cimg_instance
11483                                     "assign(): Invalid assignment request of shared instance from (%s*) buffer"
11484                                     "(pixel types are different).",
11485                                     cimg_instance,
11486                                     CImg<t>::pixel_type());
11487       return assign(values,size_x,size_y,size_z,size_c);
11488     }
11489 
11490     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
11491     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
11492                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
11493       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11494       if (!values || !siz) return assign();
11495       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
11496       else {
11497 	if (!_is_shared) {
11498 	  if (values + siz<_data || values>=_data + size()) assign();
11499 	  else cimg::warn(_cimg_instance
11500                           "assign(): Shared image instance has overlapping memory.",
11501                           cimg_instance);
11502 	}
11503 	_width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
11504 	_data = const_cast<T*>(values);
11505       }
11506       return *this;
11507     }
11508 
11509     //! Construct image from reading an image file \inplace.
11510     /**
11511        In-place version of the constructor CImg(const char*).
11512     **/
11513     CImg<T>& assign(const char *const filename) {
11514       return load(filename);
11515     }
11516 
11517     //! Construct image copy \inplace.
11518     /**
11519        In-place version of the constructor CImg(const CImg<t>&).
11520     **/
11521     template<typename t>
11522     CImg<T>& assign(const CImg<t>& img) {
11523       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
11524     }
11525 
11526     //! In-place version of the advanced copy constructor.
11527     /**
11528        In-place version of the constructor CImg(const CImg<t>&,bool).
11529      **/
11530     template<typename t>
11531     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
11532       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
11533     }
11534 
11535     //! Construct image with dimensions borrowed from another image \inplace.
11536     /**
11537        In-place version of the constructor CImg(const CImg<t>&,const char*).
11538     **/
11539     template<typename t>
11540     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
11541       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
11542       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
11543       CImg<charT> item(256);
11544       for (const char *s = dimensions; *s && k<4; ++k) {
11545         if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
11546         if (*s) {
11547           unsigned int val = 0; char sep = 0;
11548           if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
11549             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
11550             else siz[k] = val;
11551             while (*s>='0' && *s<='9') ++s;
11552             if (sep=='%') ++s;
11553           } else switch (cimg::lowercase(*s)) {
11554           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
11555           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
11556           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
11557           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
11558           default :
11559             throw CImgArgumentException(_cimg_instance
11560                                         "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
11561                                         cimg_instance,
11562                                         *s,dimensions);
11563           }
11564         }
11565       }
11566       return assign(siz[0],siz[1],siz[2],siz[3]);
11567     }
11568 
11569     //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
11570     /**
11571        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
11572     **/
11573     template<typename t>
11574     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
11575       return assign(img,dimensions).fill(value);
11576     }
11577 
11578     //! Construct image from a display window \inplace.
11579     /**
11580        In-place version of the constructor CImg(const CImgDisplay&).
11581     **/
11582     CImg<T>& assign(const CImgDisplay &disp) {
11583       disp.snapshot(*this);
11584       return *this;
11585     }
11586 
11587     //! Construct empty image \inplace.
11588     /**
11589        Equivalent to assign().
11590        \note
11591        - It has been defined for compatibility with STL naming conventions.
11592     **/
11593     CImg<T>& clear() {
11594       return assign();
11595     }
11596 
11597     //! Transfer content of an image instance into another one.
11598     /**
11599        Transfer the dimensions and the pixel buffer content of an image instance into another one,
11600        and replace instance by an empty image. It avoids the copy of the pixel buffer
11601        when possible.
11602        \param img Destination image.
11603        \note
11604        - Pixel types \c T and \c t of source and destination images can be different, though the process is
11605          designed to be instantaneous when \c T and \c t are the same.
11606        \par Example
11607        \code
11608        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'.
11609                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image.
11610        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image.
11611        \endcode
11612     **/
11613     template<typename t>
11614     CImg<t>& move_to(CImg<t>& img) {
11615       img.assign(*this);
11616       assign();
11617       return img;
11618     }
11619 
11620     //! Transfer content of an image instance into another one \specialization.
11621     CImg<T>& move_to(CImg<T>& img) {
11622       if (_is_shared || img._is_shared) img.assign(*this);
11623       else swap(img);
11624       assign();
11625       return img;
11626     }
11627 
11628     //! Transfer content of an image instance into a new image in an image list.
11629     /**
11630        Transfer the dimensions and the pixel buffer content of an image instance
11631        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
11632        \param list Destination list.
11633        \param pos Position of the newly inserted image in the list.
11634        \note
11635        - When optional parameter \c pos is ommited, the image instance is transfered as a new
11636          image at the end of the specified \c list.
11637        - It is convenient to sequentially insert new images into image lists, with no
11638          additional copies of memory buffer.
11639        \par Example
11640        \code
11641        CImgList<float> list;             // Construct an empty image list.
11642        CImg<float> img("reference.jpg"); // Read image from filename.
11643        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy).
11644        \endcode
11645     **/
11646     template<typename t>
11647     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
11648       const unsigned int npos = pos>list._width?list._width:pos;
11649       move_to(list.insert(1,npos)[npos]);
11650       return list;
11651     }
11652 
11653     //! Swap fields of two image instances.
11654     /**
11655       \param img Image to swap fields with.
11656       \note
11657       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
11658         with algorithms requiring two swapping buffers.
11659       \par Example
11660       \code
11661       CImg<float> img1("lena.jpg"),
11662                   img2("milla.jpg");
11663       img1.swap(img2);               // Now, 'img1' is 'milla' and 'img2' is 'lena'.
11664       \endcode
11665     **/
11666     CImg<T>& swap(CImg<T>& img) {
11667       cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
11668       cimg::swap(_data,img._data);
11669       cimg::swap(_is_shared,img._is_shared);
11670       return img;
11671     }
11672 
11673     //! Return a reference to an empty image.
11674     /**
11675        \note
11676        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
11677        e.g.
11678        \code
11679        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
11680        \endcode
11681      **/
11682     static CImg<T>& empty() {
11683       static CImg<T> _empty;
11684       return _empty.assign();
11685     }
11686 
11687     //! Return a reference to an empty image \const.
11688     static const CImg<T>& const_empty() {
11689       static const CImg<T> _empty;
11690       return _empty;
11691     }
11692 
11693     //@}
11694     //------------------------------------------
11695     //
11696     //! \name Overloaded Operators
11697     //@{
11698     //------------------------------------------
11699 
11700     //! Access to a pixel value.
11701     /**
11702        Return a reference to a located pixel value of the image instance,
11703        being possibly \e const, whether the image instance is \e const or not.
11704        This is the standard method to get/set pixel values in \c CImg<T> images.
11705        \param x X-coordinate of the pixel value.
11706        \param y Y-coordinate of the pixel value.
11707        \param z Z-coordinate of the pixel value.
11708        \param c C-coordinate of the pixel value.
11709        \note
11710        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
11711          <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
11712        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
11713          corresponding dimension is equal to \c 1.
11714          For instance, pixels of a 2d image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
11715          <tt>img(x,y,0,c)</tt>.
11716        \warning
11717        - There is \e no boundary checking done in this operator, to make it as fast as possible.
11718          You \e must take care of out-of-bounds access by yourself, if necessary.
11719          For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
11720          checking operations in this operator. In that case, warning messages will be printed on the error output
11721          when accessing out-of-bounds pixels.
11722        \par Example
11723        \code
11724        CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'.
11725        const float
11726           valR = img(10,10,0,0), // Read red value at coordinates (10,10).
11727           valG = img(10,10,0,1), // Read green value at coordinates (10,10)
11728           valB = img(10,10,2),   // Read blue value at coordinates (10,10) (Z-coordinate can be omitted).
11729           avg = (valR + valG + valB)/3; // Compute average pixel value.
11730        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value.
11731        \endcode
11732     **/
11733 #if cimg_verbosity>=3
11734     T& operator()(const unsigned int x, const unsigned int y=0,
11735                   const unsigned int z=0, const unsigned int c=0) {
11736       const ulongT off = (ulongT)offset(x,y,z,c);
11737       if (!_data || off>=size()) {
11738         cimg::warn(_cimg_instance
11739                    "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
11740                    cimg_instance,
11741                    (int)x,(int)y,(int)z,(int)c,off);
11742         return *_data;
11743       }
11744       else return _data[off];
11745     }
11746 
11747     //! Access to a pixel value \const.
11748     const T& operator()(const unsigned int x, const unsigned int y=0,
11749                         const unsigned int z=0, const unsigned int c=0) const {
11750       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
11751     }
11752 
11753     //! Access to a pixel value.
11754     /**
11755        \param x X-coordinate of the pixel value.
11756        \param y Y-coordinate of the pixel value.
11757        \param z Z-coordinate of the pixel value.
11758        \param c C-coordinate of the pixel value.
11759        \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
11760        \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
11761        \note
11762        - Similar to (but faster than) operator()().
11763          It uses precomputed offsets to optimize memory access. You may use it to optimize
11764          the reading/writing of several pixel values in the same image (e.g. in a loop).
11765      **/
11766     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11767                   const ulongT wh, const ulongT whd=0) {
11768       cimg::unused(wh,whd);
11769       return (*this)(x,y,z,c);
11770     }
11771 
11772     //! Access to a pixel value \const.
11773     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11774                         const ulongT wh, const ulongT whd=0) const {
11775       cimg::unused(wh,whd);
11776       return (*this)(x,y,z,c);
11777     }
11778 #else
11779     T& operator()(const unsigned int x) {
11780       return _data[x];
11781     }
11782 
11783     const T& operator()(const unsigned int x) const {
11784       return _data[x];
11785     }
11786 
11787     T& operator()(const unsigned int x, const unsigned int y) {
11788       return _data[x + y*_width];
11789     }
11790 
11791     const T& operator()(const unsigned int x, const unsigned int y) const {
11792       return _data[x + y*_width];
11793     }
11794 
11795     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
11796       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
11797    }
11798 
11799     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
11800       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
11801     }
11802 
11803     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
11804       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
11805     }
11806 
11807     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
11808       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
11809     }
11810 
11811     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
11812                   const ulongT wh) {
11813       return _data[x + y*_width + z*wh];
11814     }
11815 
11816     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
11817                         const ulongT wh) const {
11818       return _data[x + y*_width + z*wh];
11819     }
11820 
11821     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11822                   const ulongT wh, const ulongT whd) {
11823       return _data[x + y*_width + z*wh + c*whd];
11824     }
11825 
11826     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11827                         const ulongT wh, const ulongT whd) const {
11828       return _data[x + y*_width + z*wh + c*whd];
11829     }
11830 #endif
11831 
11832     //! Implicitely cast an image into a \c T*.
11833     /**
11834        Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
11835        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
11836        \note
11837        - It simply returns the pointer data() to the pixel buffer.
11838        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
11839        \code
11840        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image.
11841        if (img1) {                      // Test succeeds, 'img1' is not an empty image.
11842          if (!img2) {                   // Test succeeds, 'img2' is an empty image.
11843            std::printf("'img1' is not empty, 'img2' is empty.");
11844          }
11845        }
11846        \endcode
11847        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
11848        \code
11849        CImg<float> img(100,100);
11850        const float value = img[99]; // Access to value of the last pixel on the first row.
11851        img[510] = 255;              // Set pixel value at (10,5).
11852        \endcode
11853     **/
11854     operator T*() {
11855       return _data;
11856     }
11857 
11858     //! Implicitely cast an image into a \c T* \const.
11859     operator const T*() const {
11860       return _data;
11861     }
11862 
11863     //! Assign a value to all image pixels.
11864     /**
11865        Assign specified \c value to each pixel value of the image instance.
11866        \param value Value that will be assigned to image pixels.
11867        \note
11868        - The image size is never modified.
11869        - The \c value may be casted to pixel type \c T if necessary.
11870        \par Example
11871        \code
11872        CImg<char> img(100,100); // Declare image (with garbage values).
11873        img = 0;                 // Set all pixel values to '0'.
11874        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char').
11875        \endcode
11876     **/
11877     CImg<T>& operator=(const T& value) {
11878       return fill(value);
11879     }
11880 
11881     //! Assign pixels values from a specified expression.
11882     /**
11883        Initialize all pixel values from the specified string \c expression.
11884        \param expression Value string describing the way pixel values are set.
11885        \note
11886        - String parameter \c expression may describe different things:
11887          - 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"),
11888            the pixel values are set from specified \c expression and the image size is not modified.
11889          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
11890            replace the image instance. The image size is modified if necessary.
11891        \par Example
11892        \code
11893        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with unitialized values.
11894        img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence.
11895        img2 = "10*((x*y)%25)";                       // Set pixel values of 'img2' from a formula.
11896        img3 = "reference.jpg";                       // Set pixel values of 'img3' from a file (image size is modified).
11897        (img1,img2,img3).display();
11898        \endcode
11899        \image html ref_operator_eq.jpg
11900     **/
11901     CImg<T>& operator=(const char *const expression) {
11902       const unsigned int omode = cimg::exception_mode();
11903       cimg::exception_mode(0);
11904       try {
11905         _fill(expression,true,true,0,0,"operator=",0);
11906       } catch (CImgException&) {
11907         cimg::exception_mode(omode);
11908         load(expression);
11909       }
11910       cimg::exception_mode(omode);
11911       return *this;
11912     }
11913 
11914     //! Copy an image into the current image instance.
11915     /**
11916        Similar to the in-place copy constructor assign(const CImg<t>&).
11917     **/
11918     template<typename t>
11919     CImg<T>& operator=(const CImg<t>& img) {
11920       return assign(img);
11921     }
11922 
11923     //! Copy an image into the current image instance \specialization.
11924     CImg<T>& operator=(const CImg<T>& img) {
11925       return assign(img);
11926     }
11927 
11928     //! Copy the content of a display window to the current image instance.
11929     /**
11930        Similar to assign(const CImgDisplay&).
11931     **/
11932     CImg<T>& operator=(const CImgDisplay& disp) {
11933       disp.snapshot(*this);
11934       return *this;
11935     }
11936 
11937     //! In-place addition operator.
11938     /**
11939        Add specified \c value to all pixels of an image instance.
11940        \param value Value to add.
11941        \note
11942        - Resulting pixel values are casted to fit the pixel type \c T.
11943          For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
11944        - Overflow values are treated as with standard C++ numeric types. For instance,
11945        \code
11946        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'.
11947        img+=1;                                   // Add '1' to each pixels -> Overflow.
11948        // here all pixels of image 'img' are equal to '0'.
11949        \endcode
11950        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
11951          and use cut() after addition.
11952        \par Example
11953        \code
11954        CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]).
11955        CImg<float> img2(img1); // Construct a float-valued copy of 'img1'.
11956        img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats.
11957        img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint.
11958        img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'.
11959        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way.
11960        (img1,img2,img3).display();
11961        \endcode
11962        \image html ref_operator_plus.jpg
11963      **/
11964     template<typename t>
11965     CImg<T>& operator+=(const t value) {
11966       if (is_empty()) return *this;
11967       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
11968       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
11969       return *this;
11970     }
11971 
11972     //! In-place addition operator.
11973     /**
11974        Add values to image pixels, according to the specified string \c expression.
11975        \param expression Value string describing the way pixel values are added.
11976        \note
11977        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
11978          instead of assigning them.
11979     **/
11980     CImg<T>& operator+=(const char *const expression) {
11981       return *this+=(+*this)._fill(expression,true,true,0,0,"operator+=",this);
11982     }
11983 
11984     //! In-place addition operator.
11985     /**
11986        Add values to image pixels, according to the values of the input image \c img.
11987        \param img Input image to add.
11988        \note
11989        - The size of the image instance is never modified.
11990        - It is not mandatory that input image \c img has the same size as the image instance.
11991          If less values are available in \c img, then the values are added periodically. For instance, adding one
11992          WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
11993          means each color channel will be incremented with the same values at the same locations.
11994        \par Example
11995        \code
11996        CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
11997        // Construct a scalar shading (img2.spectrum()==1).
11998        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
11999        img1+=img2; // Add shading to each channel of 'img1'.
12000        img1.cut(0,255); // Prevent [0,255] overflow.
12001        (img2,img1).display();
12002        \endcode
12003        \image html ref_operator_plus1.jpg
12004     **/
12005     template<typename t>
12006     CImg<T>& operator+=(const CImg<t>& img) {
12007       const ulongT siz = size(), isiz = img.size();
12008       if (siz && isiz) {
12009         if (is_overlapped(img)) return *this+=+img;
12010         T *ptrd = _data, *const ptre = _data + siz;
12011         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12012           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12013             *ptrd = (T)(*ptrd + *(ptrs++));
12014         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
12015       }
12016       return *this;
12017     }
12018 
12019     //! In-place increment operator (prefix).
12020     /**
12021        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
12022        \note
12023        - Writing \c ++img is equivalent to \c img+=1.
12024      **/
12025     CImg<T>& operator++() {
12026       if (is_empty()) return *this;
12027       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12028       cimg_rof(*this,ptrd,T) ++*ptrd;
12029       return *this;
12030     }
12031 
12032     //! In-place increment operator (postfix).
12033     /**
12034        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
12035        \note
12036        - Use the prefixed version operator++() if you don't need a copy of the initial
12037          (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
12038      **/
12039     CImg<T> operator++(int) {
12040       const CImg<T> copy(*this,false);
12041       ++*this;
12042       return copy;
12043     }
12044 
12045     //! Return a non-shared copy of the image instance.
12046     /**
12047        \note
12048        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
12049          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
12050          and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
12051          information about the shared state of the input image.
12052        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
12053     **/
12054     CImg<T> operator+() const {
12055       return CImg<T>(*this,false);
12056     }
12057 
12058     //! Addition operator.
12059     /**
12060        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
12061        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12062      **/
12063     template<typename t>
12064     CImg<_cimg_Tt> operator+(const t value) const {
12065       return CImg<_cimg_Tt>(*this,false)+=value;
12066     }
12067 
12068     //! Addition operator.
12069     /**
12070        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
12071        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12072      **/
12073     CImg<Tfloat> operator+(const char *const expression) const {
12074       return CImg<Tfloat>(*this,false)+=expression;
12075     }
12076 
12077     //! Addition operator.
12078     /**
12079        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12080        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12081      **/
12082     template<typename t>
12083     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
12084       return CImg<_cimg_Tt>(*this,false)+=img;
12085     }
12086 
12087     //! In-place substraction operator.
12088     /**
12089        Similar to operator+=(const t), except that it performs a substraction instead of an addition.
12090      **/
12091     template<typename t>
12092     CImg<T>& operator-=(const t value) {
12093       if (is_empty()) return *this;
12094       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12095       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
12096       return *this;
12097     }
12098 
12099     //! In-place substraction operator.
12100     /**
12101        Similar to operator+=(const char*), except that it performs a substraction instead of an addition.
12102      **/
12103     CImg<T>& operator-=(const char *const expression) {
12104       return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",this);
12105     }
12106 
12107     //! In-place substraction operator.
12108     /**
12109        Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition.
12110      **/
12111     template<typename t>
12112     CImg<T>& operator-=(const CImg<t>& img) {
12113       const ulongT siz = size(), isiz = img.size();
12114       if (siz && isiz) {
12115         if (is_overlapped(img)) return *this-=+img;
12116         T *ptrd = _data, *const ptre = _data + siz;
12117         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12118           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12119             *ptrd = (T)(*ptrd - *(ptrs++));
12120         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
12121       }
12122       return *this;
12123     }
12124 
12125     //! In-place decrement operator (prefix).
12126     /**
12127        Similar to operator++(), except that it performs a decrement instead of an increment.
12128     **/
12129     CImg<T>& operator--() {
12130       if (is_empty()) return *this;
12131       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12132       cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1;
12133       return *this;
12134     }
12135 
12136     //! In-place decrement operator (postfix).
12137     /**
12138        Similar to operator++(int), except that it performs a decrement instead of an increment.
12139     **/
12140     CImg<T> operator--(int) {
12141       const CImg<T> copy(*this,false);
12142       --*this;
12143       return copy;
12144     }
12145 
12146     //! Replace each pixel by its opposite value.
12147     /**
12148        \note
12149        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
12150          For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
12151        \par Example
12152        \code
12153        const CImg<unsigned char>
12154          img1("reference.jpg"),   // Load a RGB color image.
12155          img2 = -img1;            // Compute its opposite (in 'unsigned char').
12156        (img1,img2).display();
12157        \endcode
12158        \image html ref_operator_minus.jpg
12159      **/
12160     CImg<T> operator-() const {
12161       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
12162     }
12163 
12164     //! Substraction operator.
12165     /**
12166        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
12167        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12168     **/
12169     template<typename t>
12170     CImg<_cimg_Tt> operator-(const t value) const {
12171       return CImg<_cimg_Tt>(*this,false)-=value;
12172     }
12173 
12174     //! Substraction operator.
12175     /**
12176        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
12177        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12178     **/
12179     CImg<Tfloat> operator-(const char *const expression) const {
12180       return CImg<Tfloat>(*this,false)-=expression;
12181     }
12182 
12183     //! Substraction operator.
12184     /**
12185        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12186        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12187     **/
12188     template<typename t>
12189     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
12190       return CImg<_cimg_Tt>(*this,false)-=img;
12191     }
12192 
12193     //! In-place multiplication operator.
12194     /**
12195        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
12196      **/
12197     template<typename t>
12198     CImg<T>& operator*=(const t value) {
12199       if (is_empty()) return *this;
12200       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
12201       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
12202       return *this;
12203     }
12204 
12205     //! In-place multiplication operator.
12206     /**
12207        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
12208      **/
12209     CImg<T>& operator*=(const char *const expression) {
12210       return mul((+*this)._fill(expression,true,true,0,0,"operator*=",this));
12211     }
12212 
12213     //! In-place multiplication operator.
12214     /**
12215        Replace the image instance by the matrix multiplication between the image instance and the specified matrix
12216        \c img.
12217        \param img Second operand of the matrix multiplication.
12218        \note
12219        - It does \e not compute a pointwise multiplication between two images. For this purpose, use
12220          mul(const CImg<t>&) instead.
12221        - The size of the image instance can be modified by this operator.
12222        \par Example
12223        \code
12224        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4].
12225        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2].
12226        A*=X;                              // Assign matrix multiplication A*X to 'A'.
12227        // 'A' is now a 1x2 vector whose values are [5;11].
12228        \endcode
12229     **/
12230     template<typename t>
12231     CImg<T>& operator*=(const CImg<t>& img) {
12232       return ((*this)*img).move_to(*this);
12233     }
12234 
12235     //! Multiplication operator.
12236     /**
12237        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
12238        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12239     **/
12240     template<typename t>
12241     CImg<_cimg_Tt> operator*(const t value) const {
12242       return CImg<_cimg_Tt>(*this,false)*=value;
12243     }
12244 
12245     //! Multiplication operator.
12246     /**
12247        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
12248        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12249     **/
12250     CImg<Tfloat> operator*(const char *const expression) const {
12251       return CImg<Tfloat>(*this,false)*=expression;
12252     }
12253 
12254     //! Multiplication operator.
12255     /**
12256        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12257        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12258     **/
12259     template<typename t>
12260     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
12261       if (_width!=img._height || _depth!=1 || _spectrum!=1)
12262         throw CImgArgumentException(_cimg_instance
12263                                     "operator*(): Invalid multiplication of instance by specified "
12264                                     "matrix (%u,%u,%u,%u,%p)",
12265                                     cimg_instance,
12266                                     img._width,img._height,img._depth,img._spectrum,img._data);
12267       CImg<_cimg_Tt> res(img._width,_height);
12268 #ifdef cimg_use_openmp
12269       cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024))
12270       cimg_forXY(res,i,j) {
12271         _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value;
12272       }
12273 #else
12274       _cimg_Tt *ptrd = res._data;
12275       cimg_forXY(res,i,j) {
12276         _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value;
12277       }
12278 #endif
12279       return res;
12280     }
12281 
12282     //! In-place division operator.
12283     /**
12284        Similar to operator+=(const t), except that it performs a division instead of an addition.
12285      **/
12286     template<typename t>
12287     CImg<T>& operator/=(const t value) {
12288       if (is_empty()) return *this;
12289       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12290       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
12291       return *this;
12292     }
12293 
12294     //! In-place division operator.
12295     /**
12296        Similar to operator+=(const char*), except that it performs a division instead of an addition.
12297      **/
12298     CImg<T>& operator/=(const char *const expression) {
12299       return div((+*this)._fill(expression,true,true,0,0,"operator/=",this));
12300     }
12301 
12302     //! In-place division operator.
12303     /**
12304        Replace the image instance by the (right) matrix division between the image instance and the specified
12305        matrix \c img.
12306        \param img Second operand of the matrix division.
12307        \note
12308        - It does \e not compute a pointwise division between two images. For this purpose, use
12309          div(const CImg<t>&) instead.
12310        - It returns the matrix operation \c A*inverse(img).
12311        - The size of the image instance can be modified by this operator.
12312      **/
12313     template<typename t>
12314     CImg<T>& operator/=(const CImg<t>& img) {
12315       return (*this*img.get_invert()).move_to(*this);
12316     }
12317 
12318     //! Division operator.
12319     /**
12320        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
12321        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12322     **/
12323     template<typename t>
12324     CImg<_cimg_Tt> operator/(const t value) const {
12325       return CImg<_cimg_Tt>(*this,false)/=value;
12326     }
12327 
12328     //! Division operator.
12329     /**
12330        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
12331        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12332     **/
12333     CImg<Tfloat> operator/(const char *const expression) const {
12334       return CImg<Tfloat>(*this,false)/=expression;
12335     }
12336 
12337     //! Division operator.
12338     /**
12339        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12340        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12341     **/
12342     template<typename t>
12343     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
12344       return (*this)*img.get_invert();
12345     }
12346 
12347     //! In-place modulo operator.
12348     /**
12349        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
12350     **/
12351     template<typename t>
12352     CImg<T>& operator%=(const t value) {
12353       if (is_empty()) return *this;
12354       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=16384))
12355       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
12356       return *this;
12357     }
12358 
12359     //! In-place modulo operator.
12360     /**
12361        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
12362     **/
12363     CImg<T>& operator%=(const char *const expression) {
12364       return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",this);
12365     }
12366 
12367     //! In-place modulo operator.
12368     /**
12369        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
12370     **/
12371     template<typename t>
12372     CImg<T>& operator%=(const CImg<t>& img) {
12373       const ulongT siz = size(), isiz = img.size();
12374       if (siz && isiz) {
12375         if (is_overlapped(img)) return *this%=+img;
12376         T *ptrd = _data, *const ptre = _data + siz;
12377         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12378           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12379             *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
12380         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
12381       }
12382       return *this;
12383     }
12384 
12385     //! Modulo operator.
12386     /**
12387        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
12388        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12389     **/
12390     template<typename t>
12391     CImg<_cimg_Tt> operator%(const t value) const {
12392       return CImg<_cimg_Tt>(*this,false)%=value;
12393     }
12394 
12395     //! Modulo operator.
12396     /**
12397        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
12398        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12399     **/
12400     CImg<Tfloat> operator%(const char *const expression) const {
12401       return CImg<Tfloat>(*this,false)%=expression;
12402     }
12403 
12404     //! Modulo operator.
12405     /**
12406        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12407        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12408     **/
12409     template<typename t>
12410     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
12411       return CImg<_cimg_Tt>(*this,false)%=img;
12412     }
12413 
12414     //! In-place bitwise AND operator.
12415     /**
12416        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
12417     **/
12418     template<typename t>
12419     CImg<T>& operator&=(const t value) {
12420       if (is_empty()) return *this;
12421       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12422       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd & (ulongT)value);
12423       return *this;
12424     }
12425 
12426     //! In-place bitwise AND operator.
12427     /**
12428        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
12429     **/
12430     CImg<T>& operator&=(const char *const expression) {
12431       return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",this);
12432     }
12433 
12434     //! In-place bitwise AND operator.
12435     /**
12436        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
12437     **/
12438     template<typename t>
12439     CImg<T>& operator&=(const CImg<t>& img) {
12440       const ulongT siz = size(), isiz = img.size();
12441       if (siz && isiz) {
12442         if (is_overlapped(img)) return *this&=+img;
12443         T *ptrd = _data, *const ptre = _data + siz;
12444         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12445           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12446             *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
12447         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
12448       }
12449       return *this;
12450     }
12451 
12452     //! Bitwise AND operator.
12453     /**
12454        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
12455        The pixel type of the returned image is \c T.
12456     **/
12457     template<typename t>
12458     CImg<T> operator&(const t value) const {
12459       return (+*this)&=value;
12460     }
12461 
12462     //! Bitwise AND operator.
12463     /**
12464        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
12465        The pixel type of the returned image is \c T.
12466     **/
12467     CImg<T> operator&(const char *const expression) const {
12468       return (+*this)&=expression;
12469     }
12470 
12471     //! Bitwise AND operator.
12472     /**
12473        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12474        The pixel type of the returned image is \c T.
12475     **/
12476     template<typename t>
12477     CImg<T> operator&(const CImg<t>& img) const {
12478       return (+*this)&=img;
12479     }
12480 
12481     //! In-place bitwise OR operator.
12482     /**
12483        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
12484     **/
12485     template<typename t>
12486     CImg<T>& operator|=(const t value) {
12487       if (is_empty()) return *this;
12488       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12489       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd | (ulongT)value);
12490       return *this;
12491     }
12492 
12493     //! In-place bitwise OR operator.
12494     /**
12495        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
12496     **/
12497     CImg<T>& operator|=(const char *const expression) {
12498       return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",this);
12499     }
12500 
12501     //! In-place bitwise OR operator.
12502     /**
12503        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
12504     **/
12505     template<typename t>
12506     CImg<T>& operator|=(const CImg<t>& img) {
12507       const ulongT siz = size(), isiz = img.size();
12508       if (siz && isiz) {
12509         if (is_overlapped(img)) return *this|=+img;
12510         T *ptrd = _data, *const ptre = _data + siz;
12511         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12512           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12513             *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
12514         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
12515       }
12516       return *this;
12517     }
12518 
12519     //! Bitwise OR operator.
12520     /**
12521        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
12522        The pixel type of the returned image is \c T.
12523     **/
12524     template<typename t>
12525     CImg<T> operator|(const t value) const {
12526       return (+*this)|=value;
12527     }
12528 
12529     //! Bitwise OR operator.
12530     /**
12531        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
12532        The pixel type of the returned image is \c T.
12533     **/
12534     CImg<T> operator|(const char *const expression) const {
12535       return (+*this)|=expression;
12536     }
12537 
12538     //! Bitwise OR operator.
12539     /**
12540        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12541        The pixel type of the returned image is \c T.
12542     **/
12543     template<typename t>
12544     CImg<T> operator|(const CImg<t>& img) const {
12545       return (+*this)|=img;
12546     }
12547 
12548     //! In-place bitwise XOR operator.
12549     /**
12550        Similar to operator+=(const 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 t) instead.
12553     **/
12554     template<typename t>
12555     CImg<T>& operator^=(const t value) {
12556       if (is_empty()) return *this;
12557       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12558       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)value);
12559       return *this;
12560     }
12561 
12562     //! In-place bitwise XOR operator.
12563     /**
12564        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
12565        \warning
12566        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
12567     **/
12568     CImg<T>& operator^=(const char *const expression) {
12569       return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",this);
12570     }
12571 
12572     //! In-place bitwise XOR operator.
12573     /**
12574        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
12575        \warning
12576        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
12577     **/
12578     template<typename t>
12579     CImg<T>& operator^=(const CImg<t>& img) {
12580       const ulongT siz = size(), isiz = img.size();
12581       if (siz && isiz) {
12582         if (is_overlapped(img)) return *this^=+img;
12583         T *ptrd = _data, *const ptre = _data + siz;
12584         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12585           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12586             *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
12587         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
12588       }
12589       return *this;
12590     }
12591 
12592     //! Bitwise XOR operator.
12593     /**
12594        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
12595        The pixel type of the returned image is \c T.
12596     **/
12597     template<typename t>
12598     CImg<T> operator^(const t value) const {
12599       return (+*this)^=value;
12600     }
12601 
12602     //! Bitwise XOR operator.
12603     /**
12604        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
12605        The pixel type of the returned image is \c T.
12606     **/
12607     CImg<T> operator^(const char *const expression) const {
12608       return (+*this)^=expression;
12609     }
12610 
12611     //! Bitwise XOR operator.
12612     /**
12613        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12614        The pixel type of the returned image is \c T.
12615     **/
12616     template<typename t>
12617     CImg<T> operator^(const CImg<t>& img) const {
12618       return (+*this)^=img;
12619     }
12620 
12621     //! In-place bitwise left shift operator.
12622     /**
12623        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
12624     **/
12625     template<typename t>
12626     CImg<T>& operator<<=(const t value) {
12627       if (is_empty()) return *this;
12628       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
12629       cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) << (int)value);
12630       return *this;
12631     }
12632 
12633     //! In-place bitwise left shift operator.
12634     /**
12635        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
12636     **/
12637     CImg<T>& operator<<=(const char *const expression) {
12638       return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",this);
12639     }
12640 
12641     //! In-place bitwise left shift operator.
12642     /**
12643        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
12644     **/
12645     template<typename t>
12646     CImg<T>& operator<<=(const CImg<t>& img) {
12647       const ulongT siz = size(), isiz = img.size();
12648       if (siz && isiz) {
12649         if (is_overlapped(img)) return *this^=+img;
12650         T *ptrd = _data, *const ptre = _data + siz;
12651         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12652           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12653             *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
12654         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
12655       }
12656       return *this;
12657     }
12658 
12659     //! Bitwise left shift operator.
12660     /**
12661        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
12662        The pixel type of the returned image is \c T.
12663     **/
12664     template<typename t>
12665     CImg<T> operator<<(const t value) const {
12666       return (+*this)<<=value;
12667     }
12668 
12669     //! Bitwise left shift operator.
12670     /**
12671        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
12672        The pixel type of the returned image is \c T.
12673     **/
12674     CImg<T> operator<<(const char *const expression) const {
12675       return (+*this)<<=expression;
12676     }
12677 
12678     //! Bitwise left shift operator.
12679     /**
12680        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
12681        operating in-place.
12682        The pixel type of the returned image is \c T.
12683     **/
12684     template<typename t>
12685     CImg<T> operator<<(const CImg<t>& img) const {
12686       return (+*this)<<=img;
12687     }
12688 
12689     //! In-place bitwise right shift operator.
12690     /**
12691        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
12692     **/
12693     template<typename t>
12694     CImg<T>& operator>>=(const t value) {
12695       if (is_empty()) return *this;
12696       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
12697       cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) >> (int)value);
12698       return *this;
12699     }
12700 
12701     //! In-place bitwise right shift operator.
12702     /**
12703        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
12704     **/
12705     CImg<T>& operator>>=(const char *const expression) {
12706       return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",this);
12707     }
12708 
12709     //! In-place bitwise right shift operator.
12710     /**
12711        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
12712     **/
12713     template<typename t>
12714     CImg<T>& operator>>=(const CImg<t>& img) {
12715       const ulongT siz = size(), isiz = img.size();
12716       if (siz && isiz) {
12717         if (is_overlapped(img)) return *this^=+img;
12718         T *ptrd = _data, *const ptre = _data + siz;
12719         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12720           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12721             *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
12722         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
12723       }
12724       return *this;
12725     }
12726 
12727     //! Bitwise right shift operator.
12728     /**
12729        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
12730        The pixel type of the returned image is \c T.
12731     **/
12732     template<typename t>
12733     CImg<T> operator>>(const t value) const {
12734       return (+*this)>>=value;
12735     }
12736 
12737     //! Bitwise right shift operator.
12738     /**
12739        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
12740        The pixel type of the returned image is \c T.
12741     **/
12742     CImg<T> operator>>(const char *const expression) const {
12743       return (+*this)>>=expression;
12744     }
12745 
12746     //! Bitwise right shift operator.
12747     /**
12748        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
12749        operating in-place.
12750        The pixel type of the returned image is \c T.
12751     **/
12752     template<typename t>
12753     CImg<T> operator>>(const CImg<t>& img) const {
12754       return (+*this)>>=img;
12755     }
12756 
12757     //! Bitwise inversion operator.
12758     /**
12759        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
12760     **/
12761     CImg<T> operator~() const {
12762       CImg<T> res(_width,_height,_depth,_spectrum);
12763       const T *ptrs = _data;
12764       cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
12765       return res;
12766     }
12767 
12768     //! Test if all pixels of an image have the same value.
12769     /**
12770        Return \c true is all pixels of the image instance are equal to the specified \c value.
12771        \param value Reference value to compare with.
12772     **/
12773     template<typename t>
12774     bool operator==(const t value) const {
12775       if (is_empty()) return false;
12776       typedef _cimg_Tt Tt;
12777       bool is_equal = true;
12778       for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
12779       return is_equal;
12780     }
12781 
12782     //! Test if all pixel values of an image follow a specified expression.
12783     /**
12784        Return \c true is all pixels of the image instance are equal to the specified \c expression.
12785        \param expression Value string describing the way pixel values are compared.
12786     **/
12787     bool operator==(const char *const expression) const {
12788       return *this==(+*this)._fill(expression,true,true,0,0,"operator==",this);
12789     }
12790 
12791     //! Test if two images have the same size and values.
12792     /**
12793        Return \c true if the image instance and the input image \c img have the same dimensions and pixel values,
12794        and \c false otherwise.
12795        \param img Input image to compare with.
12796        \note
12797        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
12798          to return \c true.
12799          Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
12800          pixel types \c T and \c t.
12801        \par Example
12802        \code
12803        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values).
12804        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values).
12805        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same.
12806          std::printf("'img1' and 'img2' have same dimensions and values.");
12807        }
12808        \endcode
12809     **/
12810     template<typename t>
12811     bool operator==(const CImg<t>& img) const {
12812       typedef _cimg_Tt Tt;
12813       const ulongT siz = size();
12814       bool is_equal = true;
12815       if (siz!=img.size()) return false;
12816       t *ptrs = img._data + siz;
12817       for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
12818       return is_equal;
12819     }
12820 
12821     //! Test if pixels of an image are all different from a value.
12822     /**
12823        Return \c true is all pixels of the image instance are different than the specified \c value.
12824        \param value Reference value to compare with.
12825     **/
12826     template<typename t>
12827     bool operator!=(const t value) const {
12828       return !((*this)==value);
12829     }
12830 
12831     //! Test if all pixel values of an image are different from a specified expression.
12832     /**
12833        Return \c true is all pixels of the image instance are different to the specified \c expression.
12834        \param expression Value string describing the way pixel values are compared.
12835     **/
12836     bool operator!=(const char *const expression) const {
12837       return !((*this)==expression);
12838     }
12839 
12840     //! Test if two images have different sizes or values.
12841     /**
12842        Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
12843        and \c false otherwise.
12844        \param img Input image to compare with.
12845        \note
12846        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
12847     **/
12848     template<typename t>
12849     bool operator!=(const CImg<t>& img) const {
12850       return !((*this)==img);
12851     }
12852 
12853     //! Construct an image list from two images.
12854     /**
12855        Return a new list of image (\c CImgList instance) containing exactly two elements:
12856          - A copy of the image instance, at position [\c 0].
12857          - A copy of the specified image \c img, at position [\c 1].
12858 
12859        \param img Input image that will be the second image of the resulting list.
12860        \note
12861        - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
12862          in practice (see warning below).
12863        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
12864          inserted as new non-shared copies in the resulting list.
12865        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
12866        \warning
12867        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
12868          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
12869          build a new CImgList instance from several images, if you are seeking for performance.
12870          Fast insertions of images in an image list are possible with
12871          CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
12872        \par Example
12873        \code
12874        const CImg<float>
12875           img1("reference.jpg"),
12876           img2 = img1.get_mirror('x'),
12877           img3 = img2.get_blur(5);
12878        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'.
12879        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'.
12880        \endcode
12881        \image html ref_operator_comma.jpg
12882     **/
12883     template<typename t>
12884     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
12885       return CImgList<_cimg_Tt>(*this,img);
12886     }
12887 
12888     //! Construct an image list from image instance and an input image list.
12889     /**
12890        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
12891          - A copy of the image instance, at position [\c 0].
12892          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
12893 
12894        \param list Input image list that will be appended to the image instance.
12895        \note
12896        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
12897     **/
12898     template<typename t>
12899     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
12900       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
12901     }
12902 
12903     //! Split image along specified axis.
12904     /**
12905        Return a new list of images (\c CImgList instance) containing the splitted components
12906        of the instance image along the specified axis.
12907        \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
12908        \note
12909        - Similar to get_split(char,int) const, with default second argument.
12910        \par Example
12911        \code
12912        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image.
12913        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels.
12914        (img,list).display();
12915        \endcode
12916        \image html ref_operator_less.jpg
12917     **/
12918     CImgList<T> operator<(const char axis) const {
12919       return get_split(axis);
12920     }
12921 
12922     //@}
12923     //-------------------------------------
12924     //
12925     //! \name Instance Characteristics
12926     //@{
12927     //-------------------------------------
12928 
12929     //! Return the type of image pixel values as a C string.
12930     /**
12931        Return a \c char* string containing the usual type name of the image pixel values
12932        (i.e. a stringified version of the template parameter \c T).
12933        \note
12934        - The returned string may contain spaces (as in \c "unsigned char").
12935        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
12936     **/
12937     static const char* pixel_type() {
12938       return cimg::type<T>::string();
12939     }
12940 
12941     //! Return the number of image columns.
12942     /**
12943        Return the image width, i.e. the image dimension along the X-axis.
12944        \note
12945        - The width() of an empty image is equal to \c 0.
12946        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
12947        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
12948          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12949          \c unsigned \c int variables.
12950          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12951          <tt>(*this)._width</tt>.
12952     **/
12953     int width() const {
12954       return (int)_width;
12955     }
12956 
12957     //! Return the number of image rows.
12958     /**
12959        Return the image height, i.e. the image dimension along the Y-axis.
12960        \note
12961        - The height() of an empty image is equal to \c 0.
12962        - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
12963          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12964          \c unsigned \c int variables.
12965          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12966          <tt>(*this)._height</tt>.
12967     **/
12968     int height() const {
12969       return (int)_height;
12970     }
12971 
12972     //! Return the number of image slices.
12973     /**
12974        Return the image depth, i.e. the image dimension along the Z-axis.
12975        \note
12976        - The depth() of an empty image is equal to \c 0.
12977        - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image
12978          is said to be \e volumetric.
12979        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
12980          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12981          \c unsigned \c int variables.
12982          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12983          <tt>(*this)._depth</tt>.
12984     **/
12985     int depth() const {
12986       return (int)_depth;
12987     }
12988 
12989     //! Return the number of image channels.
12990     /**
12991        Return the number of image channels, i.e. the image dimension along the C-axis.
12992        \note
12993        - The spectrum() of an empty image is equal to \c 0.
12994        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
12995          for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
12996          The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
12997          up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
12998        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
12999          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
13000          \c unsigned \c int variables.
13001          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
13002          <tt>(*this)._spectrum</tt>.
13003     **/
13004     int spectrum() const {
13005       return (int)_spectrum;
13006     }
13007 
13008     //! Return the total number of pixel values.
13009     /**
13010        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
13011        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
13012        \note
13013        - The size() of an empty image is equal to \c 0.
13014        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
13015          <tt>size()*sizeof(T)</tt>.
13016        \par Example
13017        \code
13018        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image.
13019        if (img.size()==30000)                            // Test succeeds.
13020          std::printf("Pixel buffer uses %lu bytes",
13021                      img.size()*sizeof(float));
13022        \endcode
13023     **/
13024     ulongT size() const {
13025       return (ulongT)_width*_height*_depth*_spectrum;
13026     }
13027 
13028     //! Return a pointer to the first pixel value.
13029     /**
13030        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
13031        whether the instance is \c const or not.
13032        \note
13033        - The data() of an empty image is equal to \c 0 (null pointer).
13034        - The allocated pixel buffer for the image instance starts from \c data()
13035          and goes to <tt>data()+\ref size() - 1</tt> (included).
13036        - To get the pointer to one particular location of the pixel buffer, use
13037          data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
13038     **/
13039     T* data() {
13040       return _data;
13041     }
13042 
13043     //! Return a pointer to the first pixel value \const.
13044     const T* data() const {
13045       return _data;
13046     }
13047 
13048     //! Return a pointer to a located pixel value.
13049     /**
13050        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
13051        of the image instance,
13052        whether the instance is \c const or not.
13053        \param x X-coordinate of the pixel value.
13054        \param y Y-coordinate of the pixel value.
13055        \param z Z-coordinate of the pixel value.
13056        \param c C-coordinate of the pixel value.
13057        \note
13058        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
13059          properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
13060      **/
13061 #if cimg_verbosity>=3
13062     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
13063       const ulongT off = (ulongT)offset(x,y,z,c);
13064       if (off>=size())
13065         cimg::warn(_cimg_instance
13066                    "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
13067                    cimg_instance,
13068                    x,y,z,c,off);
13069       return _data + off;
13070     }
13071 
13072     //! Return a pointer to a located pixel value \const.
13073     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
13074       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
13075     }
13076 #else
13077     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
13078       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
13079     }
13080 
13081     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
13082       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
13083     }
13084 #endif
13085 
13086     //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
13087     /**
13088        \param x X-coordinate of the pixel value.
13089        \param y Y-coordinate of the pixel value.
13090        \param z Z-coordinate of the pixel value.
13091        \param c C-coordinate of the pixel value.
13092        \note
13093        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
13094          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
13095        \par Example
13096        \code
13097        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image.
13098        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10).
13099        const float val = img[off];              // Get the blue value of this pixel.
13100        \endcode
13101     **/
13102     longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
13103       return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
13104     }
13105 
13106     //! Return a CImg<T>::iterator pointing to the first pixel value.
13107     /**
13108        \note
13109        - Equivalent to data().
13110        - It has been mainly defined for compatibility with STL naming conventions.
13111      **/
13112     iterator begin() {
13113       return _data;
13114     }
13115 
13116     //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
13117     const_iterator begin() const {
13118       return _data;
13119     }
13120 
13121     //! Return a CImg<T>::iterator pointing next to the last pixel value.
13122     /**
13123        \note
13124        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
13125        - It has been mainly defined for compatibility with STL naming conventions.
13126        \warning
13127        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
13128          Trying to read or write the content of the returned iterator will probably result in a crash.
13129          Use it mainly as a strict upper bound for a CImg<T>::iterator.
13130        \par Example
13131        \code
13132        CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image.
13133        // 'img.end()' used below as an upper bound for the iterator.
13134        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
13135          *it = 0;
13136        \endcode
13137     **/
13138     iterator end() {
13139       return _data + size();
13140     }
13141 
13142     //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
13143     const_iterator end() const {
13144       return _data + size();
13145     }
13146 
13147     //! Return a reference to the first pixel value.
13148     /**
13149        \note
13150        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
13151        - It has been mainly defined for compatibility with STL naming conventions.
13152     **/
13153     T& front() {
13154       return *_data;
13155     }
13156 
13157     //! Return a reference to the first pixel value \const.
13158     const T& front() const {
13159       return *_data;
13160     }
13161 
13162     //! Return a reference to the last pixel value.
13163     /**
13164        \note
13165        - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
13166          <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
13167        - It has been mainly defined for compatibility with STL naming conventions.
13168     **/
13169     T& back() {
13170       return *(_data + size() - 1);
13171     }
13172 
13173     //! Return a reference to the last pixel value \const.
13174     const T& back() const {
13175       return *(_data + size() - 1);
13176     }
13177 
13178     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
13179     /**
13180        Return a reference to the pixel value of the image instance located at a specified \c offset,
13181        or to a specified default value in case of out-of-bounds access.
13182        \param offset Offset to the desired pixel value.
13183        \param out_value Default value returned if \c offset is outside image bounds.
13184        \note
13185        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
13186          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
13187          is safely returned instead.
13188        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13189          you are \e not sure about the validity of the specified pixel offset.
13190     **/
13191     T& at(const int offset, const T& out_value) {
13192       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
13193     }
13194 
13195     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
13196     T at(const int offset, const T& out_value) const {
13197       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
13198     }
13199 
13200     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
13201     /**
13202        Return a reference to the pixel value of the image instance located at a specified \c offset,
13203        or to the nearest pixel location in the image instance in case of out-of-bounds access.
13204        \param offset Offset to the desired pixel value.
13205        \note
13206        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
13207          nearest pixel in the image instance, regarding the specified offset, i.e.
13208          - If \c offset<0, then \c img[0] is returned.
13209          - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
13210        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13211          you are \e not sure about the validity of the specified pixel offset.
13212        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
13213      **/
13214     T& at(const int offset) {
13215       if (is_empty())
13216         throw CImgInstanceException(_cimg_instance
13217                                     "at(): Empty instance.",
13218                                     cimg_instance);
13219       return _at(offset);
13220     }
13221 
13222     T& _at(const int offset) {
13223       const unsigned int siz = (unsigned int)size();
13224       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
13225     }
13226 
13227     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
13228     const T& at(const int offset) const {
13229       if (is_empty())
13230         throw CImgInstanceException(_cimg_instance
13231                                     "at(): Empty instance.",
13232                                     cimg_instance);
13233       return _at(offset);
13234     }
13235 
13236     const T& _at(const int offset) const {
13237       const unsigned int siz = (unsigned int)size();
13238       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
13239     }
13240 
13241     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
13242     /**
13243        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
13244        or to a specified default value in case of out-of-bounds access along the X-axis.
13245        \param x X-coordinate of the pixel value.
13246        \param y Y-coordinate of the pixel value.
13247        \param z Z-coordinate of the pixel value.
13248        \param c C-coordinate of the pixel value.
13249        \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
13250        \note
13251        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
13252          \c out_value.
13253        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13254          you are \e not sure about the validity of the specified pixel coordinates.
13255        \warning
13256        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13257     **/
13258     T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
13259       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13260     }
13261 
13262     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
13263     T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
13264       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
13265     }
13266 
13267     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
13268     /**
13269        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
13270        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
13271        \param x X-coordinate of the pixel value.
13272        \param y Y-coordinate of the pixel value.
13273        \param z Z-coordinate of the pixel value.
13274        \param c C-coordinate of the pixel value.
13275        \note
13276        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
13277          nearest pixel in the image instance, regarding the specified X-coordinate.
13278        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13279          you are \e not sure about the validity of the specified pixel coordinates.
13280        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13281          \c _at(int,int,int,int).
13282        \warning
13283        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13284      **/
13285     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
13286       if (is_empty())
13287         throw CImgInstanceException(_cimg_instance
13288                                     "atX(): Empty instance.",
13289                                     cimg_instance);
13290       return _atX(x,y,z,c);
13291     }
13292 
13293     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
13294       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
13295     }
13296 
13297     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
13298     const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
13299       if (is_empty())
13300         throw CImgInstanceException(_cimg_instance
13301                                     "atX(): Empty instance.",
13302                                     cimg_instance);
13303       return _atX(x,y,z,c);
13304     }
13305 
13306     const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
13307       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
13308     }
13309 
13310     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
13311     /**
13312        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
13313     **/
13314     T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
13315       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13316     }
13317 
13318     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
13319     T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
13320       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
13321     }
13322 
13323     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
13324     /**
13325        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
13326        \note
13327        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13328          \c _atXY(int,int,int,int).
13329      **/
13330     T& atXY(const int x, const int y, const int z=0, const int c=0) {
13331       if (is_empty())
13332         throw CImgInstanceException(_cimg_instance
13333                                     "atXY(): Empty instance.",
13334                                     cimg_instance);
13335       return _atXY(x,y,z,c);
13336     }
13337 
13338     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
13339       return (*this)(cimg::cut(x,0,width() - 1),
13340                      cimg::cut(y,0,height() - 1),z,c);
13341     }
13342 
13343     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
13344     const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
13345       if (is_empty())
13346         throw CImgInstanceException(_cimg_instance
13347                                     "atXY(): Empty instance.",
13348                                     cimg_instance);
13349       return _atXY(x,y,z,c);
13350     }
13351 
13352     const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
13353       return (*this)(cimg::cut(x,0,width() - 1),
13354                      cimg::cut(y,0,height() - 1),z,c);
13355     }
13356 
13357     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
13358     /**
13359        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
13360        X,Y and Z-coordinates.
13361     **/
13362     T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
13363       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
13364         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13365     }
13366 
13367     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
13368     T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
13369       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
13370     }
13371 
13372     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
13373     /**
13374        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
13375        \note
13376        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13377          \c _atXYZ(int,int,int,int).
13378     **/
13379     T& atXYZ(const int x, const int y, const int z, const int c=0) {
13380       if (is_empty())
13381         throw CImgInstanceException(_cimg_instance
13382                                     "atXYZ(): Empty instance.",
13383                                     cimg_instance);
13384       return _atXYZ(x,y,z,c);
13385     }
13386 
13387     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
13388       return (*this)(cimg::cut(x,0,width() - 1),
13389                      cimg::cut(y,0,height() - 1),
13390                      cimg::cut(z,0,depth() - 1),c);
13391     }
13392 
13393     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
13394     const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
13395       if (is_empty())
13396         throw CImgInstanceException(_cimg_instance
13397                                     "atXYZ(): Empty instance.",
13398                                     cimg_instance);
13399       return _atXYZ(x,y,z,c);
13400     }
13401 
13402     const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
13403       return (*this)(cimg::cut(x,0,width() - 1),
13404                      cimg::cut(y,0,height() - 1),
13405                      cimg::cut(z,0,depth() - 1),c);
13406     }
13407 
13408     //! Access to a pixel value, using Dirichlet boundary conditions.
13409     /**
13410        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
13411        X,Y,Z and C-coordinates.
13412     **/
13413     T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
13414       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
13415         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13416     }
13417 
13418     //! Access to a pixel value, using Dirichlet boundary conditions \const.
13419     T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
13420       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
13421         (*this)(x,y,z,c);
13422     }
13423 
13424     //! Access to a pixel value, using Neumann boundary conditions.
13425     /**
13426        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
13427        \note
13428        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13429          \c _atXYZC(int,int,int,int).
13430     **/
13431     T& atXYZC(const int x, const int y, const int z, const int c) {
13432       if (is_empty())
13433         throw CImgInstanceException(_cimg_instance
13434                                     "atXYZC(): Empty instance.",
13435                                     cimg_instance);
13436       return _atXYZC(x,y,z,c);
13437     }
13438 
13439     T& _atXYZC(const int x, const int y, const int z, const int c) {
13440       return (*this)(cimg::cut(x,0,width() - 1),
13441                      cimg::cut(y,0,height() - 1),
13442                      cimg::cut(z,0,depth() - 1),
13443                      cimg::cut(c,0,spectrum() - 1));
13444     }
13445 
13446     //! Access to a pixel value, using Neumann boundary conditions \const.
13447     const T& atXYZC(const int x, const int y, const int z, const int c) const {
13448       if (is_empty())
13449         throw CImgInstanceException(_cimg_instance
13450                                     "atXYZC(): Empty instance.",
13451                                     cimg_instance);
13452       return _atXYZC(x,y,z,c);
13453     }
13454 
13455     const T& _atXYZC(const int x, const int y, const int z, const int c) const {
13456       return (*this)(cimg::cut(x,0,width() - 1),
13457                      cimg::cut(y,0,height() - 1),
13458                      cimg::cut(z,0,depth() - 1),
13459                      cimg::cut(c,0,spectrum() - 1));
13460     }
13461 
13462     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
13463     /**
13464        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13465        or a specified default value in case of out-of-bounds access along the X-axis.
13466        \param fx X-coordinate of the pixel value (float-valued).
13467        \param y Y-coordinate of the pixel value.
13468        \param z Z-coordinate of the pixel value.
13469        \param c C-coordinate of the pixel value.
13470        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
13471        \note
13472        - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
13473          a linear interpolation along the X-axis, if corresponding coordinates are not integers.
13474        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
13475        \warning
13476        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13477     **/
13478     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13479       const int
13480 	x = (int)fx - (fx>=0?0:1), nx = x + 1;
13481       const float
13482         dx = fx - x;
13483       const Tfloat
13484         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
13485       return Ic + dx*(In - Ic);
13486     }
13487 
13488     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
13489     /**
13490        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13491        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
13492        the X-axis.
13493        \param fx X-coordinate of the pixel value (float-valued).
13494        \param y Y-coordinate of the pixel value.
13495        \param z Z-coordinate of the pixel value.
13496        \param c C-coordinate of the pixel value.
13497        \note
13498        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
13499          the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
13500        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13501          \c _linear_atX(float,int,int,int).
13502        \warning
13503        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13504     **/
13505     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13506       if (is_empty())
13507         throw CImgInstanceException(_cimg_instance
13508                                     "linear_atX(): Empty instance.",
13509                                     cimg_instance);
13510 
13511       return _linear_atX(fx,y,z,c);
13512     }
13513 
13514     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13515       const float
13516         nfx = cimg::cut(fx,0,width() - 1);
13517       const unsigned int
13518         x = (unsigned int)nfx;
13519       const float
13520         dx = nfx - x;
13521       const unsigned int
13522         nx = dx>0?x + 1:x;
13523       const Tfloat
13524         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
13525       return Ic + dx*(In - Ic);
13526     }
13527 
13528     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
13529     /**
13530        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13531        boundary checking are achieved both for X and Y-coordinates.
13532     **/
13533     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13534       const int
13535         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13536         y = (int)fy - (fy>=0?0:1), ny = y + 1;
13537       const float
13538         dx = fx - x,
13539         dy = fy - y;
13540       const Tfloat
13541         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
13542         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
13543       return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
13544     }
13545 
13546     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
13547     /**
13548        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13549        are achieved both for X and Y-coordinates.
13550        \note
13551        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13552          \c _linear_atXY(float,float,int,int).
13553     **/
13554     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13555       if (is_empty())
13556         throw CImgInstanceException(_cimg_instance
13557                                     "linear_atXY(): Empty instance.",
13558                                     cimg_instance);
13559 
13560       return _linear_atXY(fx,fy,z,c);
13561     }
13562 
13563     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13564       const float
13565         nfx = cimg::cut(fx,0,width() - 1),
13566         nfy = cimg::cut(fy,0,height() - 1);
13567       const unsigned int
13568         x = (unsigned int)nfx,
13569         y = (unsigned int)nfy;
13570       const float
13571         dx = nfx - x,
13572         dy = nfy - y;
13573       const unsigned int
13574         nx = dx>0?x + 1:x,
13575         ny = dy>0?y + 1:y;
13576       const Tfloat
13577         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
13578         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
13579       return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
13580     }
13581 
13582     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
13583     /**
13584        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13585        boundary checking are achieved both for X,Y and Z-coordinates.
13586     **/
13587     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
13588       const int
13589         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13590         y = (int)fy - (fy>=0?0:1), ny = y + 1,
13591         z = (int)fz - (fz>=0?0:1), nz = z + 1;
13592       const float
13593         dx = fx - x,
13594         dy = fy - y,
13595         dz = fz - z;
13596       const Tfloat
13597         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
13598         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
13599         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
13600         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
13601       return Iccc +
13602         dx*(Incc - Iccc +
13603             dy*(Iccc + Innc - Icnc - Incc +
13604                 dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
13605             dz*(Iccc + Incn - Iccn - Incc)) +
13606         dy*(Icnc - Iccc +
13607             dz*(Iccc + Icnn - Iccn - Icnc)) +
13608         dz*(Iccn - Iccc);
13609     }
13610 
13611     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
13612     /**
13613        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13614        are achieved both for X,Y and Z-coordinates.
13615        \note
13616        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13617          \c _linear_atXYZ(float,float,float,int).
13618     **/
13619     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
13620       if (is_empty())
13621         throw CImgInstanceException(_cimg_instance
13622                                     "linear_atXYZ(): Empty instance.",
13623                                     cimg_instance);
13624 
13625       return _linear_atXYZ(fx,fy,fz,c);
13626     }
13627 
13628     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
13629       const float
13630         nfx = cimg::cut(fx,0,width() - 1),
13631         nfy = cimg::cut(fy,0,height() - 1),
13632         nfz = cimg::cut(fz,0,depth() - 1);
13633       const unsigned int
13634         x = (unsigned int)nfx,
13635         y = (unsigned int)nfy,
13636         z = (unsigned int)nfz;
13637       const float
13638         dx = nfx - x,
13639         dy = nfy - y,
13640         dz = nfz - z;
13641       const unsigned int
13642         nx = dx>0?x + 1:x,
13643         ny = dy>0?y + 1:y,
13644         nz = dz>0?z + 1:z;
13645       const Tfloat
13646         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
13647         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
13648         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
13649         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
13650       return Iccc +
13651         dx*(Incc - Iccc +
13652             dy*(Iccc + Innc - Icnc - Incc +
13653                 dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
13654             dz*(Iccc + Incn - Iccn - Incc)) +
13655         dy*(Icnc - Iccc +
13656             dz*(Iccc + Icnn - Iccn - Icnc)) +
13657         dz*(Iccn - Iccc);
13658     }
13659 
13660     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
13661     /**
13662        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13663        boundary checking are achieved for all X,Y,Z and C-coordinates.
13664     **/
13665     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
13666       const int
13667         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13668         y = (int)fy - (fy>=0?0:1), ny = y + 1,
13669         z = (int)fz - (fz>=0?0:1), nz = z + 1,
13670         c = (int)fc - (fc>=0?0:1), nc = c + 1;
13671       const float
13672         dx = fx - x,
13673         dy = fy - y,
13674         dz = fz - z,
13675         dc = fc - c;
13676       const Tfloat
13677         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
13678         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
13679         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
13680         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
13681         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
13682         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
13683         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
13684         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
13685       return Icccc +
13686         dx*(Inccc - Icccc +
13687             dy*(Icccc + Inncc - Icncc - Inccc +
13688                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
13689                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
13690                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
13691                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
13692             dz*(Icccc + Incnc - Iccnc - Inccc +
13693                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
13694             dc*(Icccc + Inccn - Inccc - Icccn)) +
13695         dy*(Icncc - Icccc +
13696             dz*(Icccc + Icnnc - Iccnc - Icncc +
13697                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
13698             dc*(Icccc + Icncn - Icncc - Icccn)) +
13699         dz*(Iccnc - Icccc +
13700             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
13701         dc*(Icccn  -Icccc);
13702     }
13703 
13704     //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
13705     /**
13706        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13707        are achieved for all X,Y,Z and C-coordinates.
13708        \note
13709        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13710          \c _linear_atXYZC(float,float,float,float).
13711     **/
13712     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
13713       if (is_empty())
13714         throw CImgInstanceException(_cimg_instance
13715                                     "linear_atXYZC(): Empty instance.",
13716                                     cimg_instance);
13717 
13718       return _linear_atXYZC(fx,fy,fz,fc);
13719     }
13720 
13721     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
13722       const float
13723         nfx = cimg::cut(fx,0,width() - 1),
13724         nfy = cimg::cut(fy,0,height() - 1),
13725         nfz = cimg::cut(fz,0,depth() - 1),
13726         nfc = cimg::cut(fc,0,spectrum() - 1);
13727       const unsigned int
13728         x = (unsigned int)nfx,
13729         y = (unsigned int)nfy,
13730         z = (unsigned int)nfz,
13731         c = (unsigned int)nfc;
13732       const float
13733         dx = nfx - x,
13734         dy = nfy - y,
13735         dz = nfz - z,
13736         dc = nfc - c;
13737       const unsigned int
13738         nx = dx>0?x + 1:x,
13739         ny = dy>0?y + 1:y,
13740         nz = dz>0?z + 1:z,
13741         nc = dc>0?c + 1:c;
13742       const Tfloat
13743         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
13744         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
13745         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
13746         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
13747         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
13748         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
13749         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
13750         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
13751       return Icccc +
13752         dx*(Inccc - Icccc +
13753             dy*(Icccc + Inncc - Icncc - Inccc +
13754                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
13755                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
13756                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
13757                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
13758             dz*(Icccc + Incnc - Iccnc - Inccc +
13759                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
13760             dc*(Icccc + Inccn - Inccc - Icccn)) +
13761         dy*(Icncc - Icccc +
13762             dz*(Icccc + Icnnc - Iccnc - Icncc +
13763                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
13764             dc*(Icccc + Icncn - Icncc - Icccn)) +
13765         dz*(Iccnc - Icccc +
13766             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
13767         dc*(Icccn - Icccc);
13768     }
13769 
13770     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
13771     /**
13772        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13773        or a specified default value in case of out-of-bounds access along the X-axis.
13774        The cubic interpolation uses Hermite splines.
13775        \param fx d X-coordinate of the pixel value (float-valued).
13776        \param y Y-coordinate of the pixel value.
13777        \param z Z-coordinate of the pixel value.
13778        \param c C-coordinate of the pixel value.
13779        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
13780        \note
13781        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
13782          approximated by a \e cubic interpolation along the X-axis.
13783        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
13784        \warning
13785        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13786     **/
13787     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13788       const int
13789         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
13790       const float
13791         dx = fx - x;
13792       const Tfloat
13793         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
13794         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
13795       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));
13796     }
13797 
13798     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
13799     /**
13800        Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
13801        min/max range of the datatype \c T.
13802     **/
13803     T cubic_cut_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13804       return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
13805     }
13806 
13807     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
13808     /**
13809        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13810        or the value of the nearest pixel location in the image instance in case of out-of-bounds access
13811        along the X-axis. The cubic interpolation uses Hermite splines.
13812        \param fx X-coordinate of the pixel value (float-valued).
13813        \param y Y-coordinate of the pixel value.
13814        \param z Z-coordinate of the pixel value.
13815        \param c C-coordinate of the pixel value.
13816        \note
13817        - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
13818          approximated by a cubic interpolation along the X-axis.
13819        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13820          \c _cubic_atX(float,int,int,int).
13821        \warning
13822        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13823     **/
13824     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13825       if (is_empty())
13826         throw CImgInstanceException(_cimg_instance
13827                                     "cubic_atX(): Empty instance.",
13828                                     cimg_instance);
13829       return _cubic_atX(fx,y,z,c);
13830     }
13831 
13832     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13833       const float
13834         nfx = cimg::cut(fx,0,width() - 1);
13835       const int
13836         x = (int)nfx;
13837       const float
13838         dx = nfx - x;
13839       const int
13840         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
13841       const Tfloat
13842         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
13843         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
13844       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));
13845     }
13846 
13847     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
13848     /**
13849        Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
13850        min/max range of the datatype \c T.
13851     **/
13852     T cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
13853       return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
13854     }
13855 
13856     T _cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
13857       return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
13858     }
13859 
13860     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
13861     /**
13862        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
13863        are achieved both for X and Y-coordinates.
13864     **/
13865     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13866       const int
13867         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
13868         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
13869       const float dx = fx - x, dy = fy - y;
13870       const Tfloat
13871         Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
13872         Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
13873         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)),
13874         Ipc = (Tfloat)atXY(px,y,z,c,out_value),  Icc = (Tfloat)atXY(x, y,z,c,out_value),
13875         Inc = (Tfloat)atXY(nx,y,z,c,out_value),  Iac = (Tfloat)atXY(ax,y,z,c,out_value),
13876         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)),
13877         Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
13878         Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
13879         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)),
13880         Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
13881         Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
13882         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));
13883       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));
13884     }
13885 
13886     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
13887     /**
13888        Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
13889        min/max range of the datatype \c T.
13890     **/
13891     T cubic_cut_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13892       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
13893     }
13894 
13895     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
13896     /**
13897        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
13898        are achieved for both X and Y-coordinates.
13899        \note
13900        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13901        \c _cubic_atXY(float,float,int,int).
13902     **/
13903     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13904       if (is_empty())
13905         throw CImgInstanceException(_cimg_instance
13906                                     "cubic_atXY(): Empty instance.",
13907                                     cimg_instance);
13908       return _cubic_atXY(fx,fy,z,c);
13909     }
13910 
13911     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13912       const float
13913         nfx = cimg::cut(fx,0,width() - 1),
13914         nfy = cimg::cut(fy,0,height() - 1);
13915       const int x = (int)nfx, y = (int)nfy;
13916       const float dx = nfx - x, dy = nfy - y;
13917       const int
13918         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
13919         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2;
13920       const Tfloat
13921         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
13922         Iap = (Tfloat)(*this)(ax,py,z,c),
13923         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)),
13924         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
13925         Iac = (Tfloat)(*this)(ax,y,z,c),
13926         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)),
13927         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
13928         Ian = (Tfloat)(*this)(ax,ny,z,c),
13929         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)),
13930         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
13931         Iaa = (Tfloat)(*this)(ax,ay,z,c),
13932         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));
13933       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));
13934     }
13935 
13936     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
13937     /**
13938        Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
13939        min/max range of the datatype \c T.
13940     **/
13941     T cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
13942       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
13943     }
13944 
13945     T _cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
13946       return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
13947     }
13948 
13949     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
13950     /**
13951        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
13952        are achieved both for X,Y and Z-coordinates.
13953     **/
13954     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
13955       const int
13956         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
13957         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
13958         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
13959       const float dx = fx - x, dy = fy - y, dz = fz - z;
13960       const Tfloat
13961         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
13962         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
13963         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
13964                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
13965         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
13966         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
13967         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
13968                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
13969         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
13970         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
13971         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
13972                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
13973         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
13974         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
13975         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
13976                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
13977         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
13978                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
13979         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
13980         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
13981         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
13982                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
13983         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
13984         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
13985         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
13986                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
13987         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
13988         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
13989         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
13990                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
13991         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
13992         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
13993         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
13994                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
13995         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
13996                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
13997         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
13998         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
13999         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
14000                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
14001         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
14002         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
14003         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
14004                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
14005         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
14006         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
14007         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
14008                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
14009         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
14010         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
14011         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
14012                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
14013         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
14014                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
14015         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
14016         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
14017         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
14018                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
14019         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
14020         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
14021         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
14022                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
14023         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
14024         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
14025         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
14026                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
14027         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
14028         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
14029         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
14030                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
14031         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
14032                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
14033       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));
14034     }
14035 
14036     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
14037     /**
14038        Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
14039        in the min/max range of the datatype \c T.
14040     **/
14041     T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
14042       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
14043     }
14044 
14045     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
14046     /**
14047        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
14048        are achieved both for X,Y and Z-coordinates.
14049        \note
14050        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14051          \c _cubic_atXYZ(float,float,float,int).
14052     **/
14053     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
14054       if (is_empty())
14055         throw CImgInstanceException(_cimg_instance
14056                                     "cubic_atXYZ(): Empty instance.",
14057                                     cimg_instance);
14058       return _cubic_atXYZ(fx,fy,fz,c);
14059     }
14060 
14061     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
14062       const float
14063         nfx = cimg::cut(fx,0,width() - 1),
14064         nfy = cimg::cut(fy,0,height() - 1),
14065         nfz = cimg::cut(fz,0,depth() - 1);
14066       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
14067       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
14068       const int
14069         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
14070         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
14071         pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
14072       const Tfloat
14073         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
14074         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
14075         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
14076                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
14077         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
14078         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
14079         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
14080                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
14081         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
14082         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
14083         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
14084                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
14085         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
14086         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
14087         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
14088                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
14089         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
14090                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
14091         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
14092         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
14093         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
14094                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
14095         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
14096         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
14097         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
14098                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
14099         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
14100         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
14101         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
14102                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
14103         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
14104         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
14105         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
14106                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
14107         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
14108                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
14109         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
14110         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
14111         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
14112                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
14113         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
14114         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
14115         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
14116                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
14117         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
14118         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
14119         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
14120                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
14121         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
14122         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
14123         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
14124                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
14125         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
14126                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
14127         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
14128         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
14129         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
14130                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
14131         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
14132         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
14133         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
14134                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
14135         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
14136         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
14137         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
14138                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
14139         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
14140         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
14141         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
14142                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
14143         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
14144                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
14145       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));
14146     }
14147 
14148     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
14149     /**
14150        Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
14151        min/max range of the datatype \c T.
14152     **/
14153     T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
14154       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
14155     }
14156 
14157     T _cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
14158       return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
14159     }
14160 
14161     //! Set pixel value, using linear interpolation for the X-coordinates.
14162     /**
14163        Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
14164        the value is spread amongst several neighbors if the pixel coordinates are float-valued.
14165        \param value Pixel value to set.
14166        \param fx X-coordinate of the pixel value (float-valued).
14167        \param y Y-coordinate of the pixel value.
14168        \param z Z-coordinate of the pixel value.
14169        \param c C-coordinate of the pixel value.
14170        \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
14171          pixel(s).
14172        \return A reference to the current image instance.
14173        \note
14174        - Calling this method with out-of-bounds coordinates does nothing.
14175     **/
14176     CImg<T>& set_linear_atX(const T& value, const float fx, const int y=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       const float
14181         dx = fx - x;
14182       if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
14183         if (x>=0 && x<width()) {
14184           const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
14185           (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14186         }
14187         if (nx>=0 && nx<width()) {
14188           const float w1 = dx, w2 = is_added?1:(1 - w1);
14189           (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14190         }
14191       }
14192       return *this;
14193     }
14194 
14195     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
14196     /**
14197        Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
14198        is achieved both for X and Y-coordinates.
14199     **/
14200     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
14201                              const bool is_added=false) {
14202       const int
14203         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14204         y = (int)fy - (fy>=0?0:1), ny = y + 1;
14205       const float
14206         dx = fx - x,
14207         dy = fy - y;
14208       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
14209         if (y>=0 && y<height()) {
14210           if (x>=0 && x<width()) {
14211             const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
14212             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14213           }
14214           if (nx>=0 && nx<width()) {
14215             const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
14216             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14217           }
14218         }
14219         if (ny>=0 && ny<height()) {
14220           if (x>=0 && x<width()) {
14221             const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
14222             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
14223           }
14224           if (nx>=0 && nx<width()) {
14225             const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
14226             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
14227           }
14228         }
14229       }
14230       return *this;
14231     }
14232 
14233     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
14234     /**
14235        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
14236        is achieved both for X,Y and Z-coordinates.
14237     **/
14238     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
14239                               const bool is_added=false) {
14240       const int
14241         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14242         y = (int)fy - (fy>=0?0:1), ny = y + 1,
14243         z = (int)fz - (fz>=0?0:1), nz = z + 1;
14244       const float
14245         dx = fx - x,
14246         dy = fy - y,
14247         dz = fz - z;
14248       if (c>=0 && c<spectrum()) {
14249         if (z>=0 && z<depth()) {
14250           if (y>=0 && y<height()) {
14251             if (x>=0 && x<width()) {
14252               const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
14253               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14254             }
14255             if (nx>=0 && nx<width()) {
14256               const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
14257               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14258             }
14259           }
14260           if (ny>=0 && ny<height()) {
14261             if (x>=0 && x<width()) {
14262               const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
14263               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
14264             }
14265             if (nx>=0 && nx<width()) {
14266               const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
14267               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
14268             }
14269           }
14270         }
14271         if (nz>=0 && nz<depth()) {
14272           if (y>=0 && y<height()) {
14273             if (x>=0 && x<width()) {
14274               const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
14275               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
14276             }
14277             if (nx>=0 && nx<width()) {
14278               const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
14279               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
14280             }
14281           }
14282           if (ny>=0 && ny<height()) {
14283             if (x>=0 && x<width()) {
14284               const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
14285               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
14286             }
14287             if (nx>=0 && nx<width()) {
14288               const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
14289               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
14290             }
14291           }
14292         }
14293       }
14294       return *this;
14295     }
14296 
14297     //! Return a C-string containing a list of all values of the image instance.
14298     /**
14299        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
14300        of the image instance (written in base 10), separated by specified \c separator character.
14301        \param separator A \c char character which specifies the separator between values in the returned C-string.
14302        \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
14303        \param format For float/double-values, tell the printf format used to generate the ascii representation
14304          of the numbers (or \c 0 for default representation).
14305        \note
14306        - The returned image is never empty.
14307        - For an empty image instance, the returned string is <tt>""</tt>.
14308        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
14309        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
14310          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
14311     **/
14312     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
14313                              const char *const format=0) const {
14314       if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
14315       CImgList<charT> items;
14316       CImg<charT> s_item(256); *s_item = 0;
14317       const T *ptrs = _data;
14318       unsigned int string_size = 0;
14319       const char *const _format = format?format:cimg::type<T>::format();
14320       for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
14321         const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
14322                                                              cimg::type<T>::format(*(ptrs++)));
14323         CImg<charT> item(s_item._data,printed_size);
14324         item[printed_size - 1] = separator;
14325         item.move_to(items);
14326         if (max_size) string_size+=printed_size;
14327       }
14328       CImg<charT> res;
14329       (items>'x').move_to(res);
14330       if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
14331       res.back() = 0;
14332       return res;
14333     }
14334 
14335     //@}
14336     //-------------------------------------
14337     //
14338     //! \name Instance Checking
14339     //@{
14340     //-------------------------------------
14341 
14342     //! Test shared state of the pixel buffer.
14343     /**
14344        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
14345        \note
14346        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
14347        - Most of the time, a \c CImg<T> image instance will \e not be shared.
14348        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
14349     **/
14350     bool is_shared() const {
14351       return _is_shared;
14352     }
14353 
14354     //! Test if image instance is empty.
14355     /**
14356        Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
14357        \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.
14358     **/
14359     bool is_empty() const {
14360       return !(_data && _width && _height && _depth && _spectrum);
14361     }
14362 
14363     //! Test if image instance contains a 'inf' value.
14364     /**
14365        Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
14366     **/
14367     bool is_inf() const {
14368       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
14369       return false;
14370     }
14371 
14372     //! Test if image instance contains a NaN value.
14373     /**
14374        Return \c true, if image instance contains a NaN value, and \c false otherwise.
14375     **/
14376     bool is_nan() const {
14377       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
14378       return false;
14379     }
14380 
14381     //! Test if image width is equal to specified value.
14382     bool is_sameX(const unsigned int size_x) const {
14383       return _width==size_x;
14384     }
14385 
14386     //! Test if image width is equal to specified value.
14387     template<typename t>
14388     bool is_sameX(const CImg<t>& img) const {
14389       return is_sameX(img._width);
14390     }
14391 
14392     //! Test if image width is equal to specified value.
14393     bool is_sameX(const CImgDisplay& disp) const {
14394       return is_sameX(disp._width);
14395     }
14396 
14397     //! Test if image height is equal to specified value.
14398     bool is_sameY(const unsigned int size_y) const {
14399       return _height==size_y;
14400     }
14401 
14402     //! Test if image height is equal to specified value.
14403     template<typename t>
14404     bool is_sameY(const CImg<t>& img) const {
14405       return is_sameY(img._height);
14406     }
14407 
14408     //! Test if image height is equal to specified value.
14409     bool is_sameY(const CImgDisplay& disp) const {
14410       return is_sameY(disp._height);
14411     }
14412 
14413     //! Test if image depth is equal to specified value.
14414     bool is_sameZ(const unsigned int size_z) const {
14415       return _depth==size_z;
14416     }
14417 
14418     //! Test if image depth is equal to specified value.
14419     template<typename t>
14420     bool is_sameZ(const CImg<t>& img) const {
14421       return is_sameZ(img._depth);
14422     }
14423 
14424     //! Test if image spectrum is equal to specified value.
14425     bool is_sameC(const unsigned int size_c) const {
14426       return _spectrum==size_c;
14427     }
14428 
14429     //! Test if image spectrum is equal to specified value.
14430     template<typename t>
14431     bool is_sameC(const CImg<t>& img) const {
14432       return is_sameC(img._spectrum);
14433     }
14434 
14435     //! Test if image width and height are equal to specified values.
14436     /**
14437        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
14438     **/
14439     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
14440       return _width==size_x && _height==size_y;
14441     }
14442 
14443     //! Test if image width and height are the same as that of another image.
14444     /**
14445        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
14446     **/
14447     template<typename t>
14448     bool is_sameXY(const CImg<t>& img) const {
14449       return is_sameXY(img._width,img._height);
14450     }
14451 
14452     //! Test if image width and height are the same as that of an existing display window.
14453     /**
14454        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
14455     **/
14456     bool is_sameXY(const CImgDisplay& disp) const {
14457       return is_sameXY(disp._width,disp._height);
14458     }
14459 
14460     //! Test if image width and depth are equal to specified values.
14461     /**
14462        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
14463     **/
14464     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
14465       return _width==size_x && _depth==size_z;
14466     }
14467 
14468     //! Test if image width and depth are the same as that of another image.
14469     /**
14470        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14471     **/
14472     template<typename t>
14473     bool is_sameXZ(const CImg<t>& img) const {
14474       return is_sameXZ(img._width,img._depth);
14475     }
14476 
14477     //! Test if image width and spectrum are equal to specified values.
14478     /**
14479        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
14480     **/
14481     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
14482       return _width==size_x && _spectrum==size_c;
14483     }
14484 
14485     //! Test if image width and spectrum are the same as that of another image.
14486     /**
14487        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14488     **/
14489     template<typename t>
14490     bool is_sameXC(const CImg<t>& img) const {
14491       return is_sameXC(img._width,img._spectrum);
14492     }
14493 
14494     //! Test if image height and depth are equal to specified values.
14495     /**
14496        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
14497     **/
14498     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
14499       return _height==size_y && _depth==size_z;
14500     }
14501 
14502     //! Test if image height and depth are the same as that of another image.
14503     /**
14504        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14505     **/
14506     template<typename t>
14507     bool is_sameYZ(const CImg<t>& img) const {
14508       return is_sameYZ(img._height,img._depth);
14509     }
14510 
14511     //! Test if image height and spectrum are equal to specified values.
14512     /**
14513        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
14514     **/
14515     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
14516       return _height==size_y && _spectrum==size_c;
14517     }
14518 
14519     //! Test if image height and spectrum are the same as that of another image.
14520     /**
14521        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14522     **/
14523     template<typename t>
14524     bool is_sameYC(const CImg<t>& img) const {
14525       return is_sameYC(img._height,img._spectrum);
14526     }
14527 
14528     //! Test if image depth and spectrum are equal to specified values.
14529     /**
14530        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
14531     **/
14532     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
14533       return _depth==size_z && _spectrum==size_c;
14534     }
14535 
14536     //! Test if image depth and spectrum are the same as that of another image.
14537     /**
14538        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14539     **/
14540     template<typename t>
14541     bool is_sameZC(const CImg<t>& img) const {
14542       return is_sameZC(img._depth,img._spectrum);
14543     }
14544 
14545     //! Test if image width, height and depth are equal to specified values.
14546     /**
14547        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
14548     **/
14549     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
14550       return is_sameXY(size_x,size_y) && _depth==size_z;
14551     }
14552 
14553     //! Test if image width, height and depth are the same as that of another image.
14554     /**
14555        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14556     **/
14557     template<typename t>
14558     bool is_sameXYZ(const CImg<t>& img) const {
14559       return is_sameXYZ(img._width,img._height,img._depth);
14560     }
14561 
14562     //! Test if image width, height and spectrum are equal to specified values.
14563     /**
14564        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14565     **/
14566     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
14567       return is_sameXY(size_x,size_y) && _spectrum==size_c;
14568     }
14569 
14570     //! Test if image width, height and spectrum are the same as that of another image.
14571     /**
14572        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14573     **/
14574     template<typename t>
14575     bool is_sameXYC(const CImg<t>& img) const {
14576       return is_sameXYC(img._width,img._height,img._spectrum);
14577     }
14578 
14579     //! Test if image width, depth and spectrum are equal to specified values.
14580     /**
14581        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14582     **/
14583     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
14584       return is_sameXZ(size_x,size_z) && _spectrum==size_c;
14585     }
14586 
14587     //! Test if image width, depth and spectrum are the same as that of another image.
14588     /**
14589        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14590     **/
14591     template<typename t>
14592     bool is_sameXZC(const CImg<t>& img) const {
14593       return is_sameXZC(img._width,img._depth,img._spectrum);
14594     }
14595 
14596     //! Test if image height, depth and spectrum are equal to specified values.
14597     /**
14598        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14599     **/
14600     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
14601       return is_sameYZ(size_y,size_z) && _spectrum==size_c;
14602     }
14603 
14604     //! Test if image height, depth and spectrum are the same as that of another image.
14605     /**
14606        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14607     **/
14608     template<typename t>
14609     bool is_sameYZC(const CImg<t>& img) const {
14610       return is_sameYZC(img._height,img._depth,img._spectrum);
14611     }
14612 
14613     //! Test if image width, height, depth and spectrum are equal to specified values.
14614     /**
14615        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
14616        verified.
14617     **/
14618     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
14619                      const unsigned int size_z, const unsigned int size_c) const {
14620       return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
14621     }
14622 
14623     //! Test if image width, height, depth and spectrum are the same as that of another image.
14624     /**
14625        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14626     **/
14627     template<typename t>
14628     bool is_sameXYZC(const CImg<t>& img) const {
14629       return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
14630     }
14631 
14632     //! Test if specified coordinates are inside image bounds.
14633     /**
14634        Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
14635        and \c false otherwise.
14636        \param x X-coordinate of the pixel value.
14637        \param y Y-coordinate of the pixel value.
14638        \param z Z-coordinate of the pixel value.
14639        \param c C-coordinate of the pixel value.
14640        \note
14641        - Return \c true only if all these conditions are verified:
14642          - The image instance is \e not empty.
14643          - <tt>0<=x<=\ref width() - 1</tt>.
14644          - <tt>0<=y<=\ref height() - 1</tt>.
14645          - <tt>0<=z<=\ref depth() - 1</tt>.
14646          - <tt>0<=c<=\ref spectrum() - 1</tt>.
14647     **/
14648     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
14649       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
14650     }
14651 
14652     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
14653     /**
14654        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
14655        and \c false otherwise.
14656        \param pixel Reference to pixel value to test.
14657        \param[out] x X-coordinate of the pixel value, if test succeeds.
14658        \param[out] y Y-coordinate of the pixel value, if test succeeds.
14659        \param[out] z Z-coordinate of the pixel value, if test succeeds.
14660        \param[out] c C-coordinate of the pixel value, if test succeeds.
14661        \note
14662        - Useful to convert an offset to a buffer value into pixel value coordinates:
14663        \code
14664        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image.
14665        const unsigned long offset = 1249;       // Offset to the pixel (49,12,0,0).
14666        unsigned int x,y,z,c;
14667        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates.
14668          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
14669                      offset,x,y,z,c);
14670        }
14671        \endcode
14672     **/
14673     template<typename t>
14674     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
14675       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
14676       const T *const ppixel = &pixel;
14677       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14678       ulongT off = (ulongT)(ppixel - _data);
14679       const ulongT nc = off/whd;
14680       off%=whd;
14681       const ulongT nz = off/wh;
14682       off%=wh;
14683       const ulongT ny = off/_width, nx = off%_width;
14684       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
14685       return true;
14686     }
14687 
14688     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
14689     /**
14690        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
14691     **/
14692     template<typename t>
14693     bool contains(const T& pixel, t& x, t& y, t& z) const {
14694       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
14695       const T *const ppixel = &pixel;
14696       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14697       ulongT off = ((ulongT)(ppixel - _data))%whd;
14698       const ulongT nz = off/wh;
14699       off%=wh;
14700       const ulongT ny = off/_width, nx = off%_width;
14701       x = (t)nx; y = (t)ny; z = (t)nz;
14702       return true;
14703     }
14704 
14705     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
14706     /**
14707        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
14708     **/
14709     template<typename t>
14710     bool contains(const T& pixel, t& x, t& y) const {
14711       const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
14712       const T *const ppixel = &pixel;
14713       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14714       ulongT off = ((unsigned int)(ppixel - _data))%wh;
14715       const ulongT ny = off/_width, nx = off%_width;
14716       x = (t)nx; y = (t)ny;
14717       return true;
14718     }
14719 
14720     //! Test if pixel value is inside image bounds and get its X-coordinate.
14721     /**
14722        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
14723     **/
14724     template<typename t>
14725     bool contains(const T& pixel, t& x) const {
14726       const T *const ppixel = &pixel;
14727       if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
14728       x = (t)(((ulongT)(ppixel - _data))%_width);
14729       return true;
14730     }
14731 
14732     //! Test if pixel value is inside image bounds.
14733     /**
14734        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
14735     **/
14736     bool contains(const T& pixel) const {
14737       const T *const ppixel = &pixel;
14738       return !is_empty() && ppixel>=_data && ppixel<_data + size();
14739     }
14740 
14741     //! Test if pixel buffers of instance and input images overlap.
14742     /**
14743        Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
14744        and \c false otherwise.
14745        \param img Input image to compare with.
14746        \note
14747        - Buffer overlapping may happen when manipulating \e shared images.
14748        - If two image buffers overlap, operating on one of the image will probably modify the other one.
14749        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
14750        \par Example
14751        \code
14752        const CImg<float>
14753          img1("reference.jpg"),             // Load RGB-color image.
14754          img2 = img1.get_shared_channel(1); // Get shared version of the green channel.
14755        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps.
14756          std::printf("Buffers overlap!\n");
14757        }
14758        \endcode
14759     **/
14760     template<typename t>
14761     bool is_overlapped(const CImg<t>& img) const {
14762       const ulongT csiz = size(), isiz = img.size();
14763       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
14764     }
14765 
14766     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object.
14767     /**
14768        Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
14769        valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image.
14770        \param primitives List of primitives of the 3d object.
14771        \param colors List of colors of the 3d object.
14772        \param opacities List (or image) of opacities of the 3d object.
14773        \param full_check Tells if full checking of the 3d object must be performed.
14774        \param[out] error_message C-string to contain the error message, if the test does not succeed.
14775        \note
14776        - Set \c full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
14777          each 3d object component is checked.
14778        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
14779     **/
14780     template<typename tp, typename tc, typename to>
14781     bool is_object3d(const CImgList<tp>& primitives,
14782                      const CImgList<tc>& colors,
14783                      const to& opacities,
14784                      const bool full_check=true,
14785                      char *const error_message=0) const {
14786       if (error_message) *error_message = 0;
14787 
14788       // Check consistency for the particular case of an empty 3d object.
14789       if (is_empty()) {
14790         if (primitives || colors || opacities) {
14791           if (error_message) cimg_sprintf(error_message,
14792                                           "3d object (%u,%u) defines no vertices but %u primitives, "
14793                                           "%u colors and %lu opacities",
14794                                           _width,primitives._width,primitives._width,
14795                                           colors._width,(unsigned long)opacities.size());
14796           return false;
14797         }
14798         return true;
14799       }
14800 
14801       // Check consistency of vertices.
14802       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
14803         if (error_message) cimg_sprintf(error_message,
14804                                         "3d object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
14805                                         _width,primitives._width,_width,_height,_depth,_spectrum);
14806         return false;
14807       }
14808       if (colors._width>primitives._width + 1) {
14809         if (error_message) cimg_sprintf(error_message,
14810                                         "3d object (%u,%u) defines %u colors",
14811                                         _width,primitives._width,colors._width);
14812         return false;
14813       }
14814       if (opacities.size()>primitives._width) {
14815         if (error_message) cimg_sprintf(error_message,
14816                                         "3d object (%u,%u) defines %lu opacities",
14817                                         _width,primitives._width,(unsigned long)opacities.size());
14818         return false;
14819       }
14820       if (!full_check) return true;
14821 
14822       // Check consistency of primitives.
14823       cimglist_for(primitives,l) {
14824         const CImg<tp>& primitive = primitives[l];
14825         const unsigned int psiz = (unsigned int)primitive.size();
14826         switch (psiz) {
14827         case 1 : { // Point.
14828           const unsigned int i0 = (unsigned int)primitive(0);
14829           if (i0>=_width) {
14830             if (error_message) cimg_sprintf(error_message,
14831                                             "3d object (%u,%u) refers to invalid vertex indice %u in "
14832                                             "point primitive [%u]",
14833                                             _width,primitives._width,i0,l);
14834             return false;
14835           }
14836         } break;
14837         case 5 : { // Sphere.
14838           const unsigned int
14839             i0 = (unsigned int)primitive(0),
14840             i1 = (unsigned int)primitive(1);
14841           if (i0>=_width || i1>=_width) {
14842             if (error_message) cimg_sprintf(error_message,
14843                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
14844                                             "sphere primitive [%u]",
14845                                             _width,primitives._width,i0,i1,l);
14846             return false;
14847           }
14848         } break;
14849         case 2 : // Segment.
14850         case 6 : {
14851           const unsigned int
14852             i0 = (unsigned int)primitive(0),
14853             i1 = (unsigned int)primitive(1);
14854           if (i0>=_width || i1>=_width) {
14855             if (error_message) cimg_sprintf(error_message,
14856                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
14857                                             "segment primitive [%u]",
14858                                             _width,primitives._width,i0,i1,l);
14859             return false;
14860           }
14861         } break;
14862         case 3 : // Triangle.
14863         case 9 : {
14864           const unsigned int
14865             i0 = (unsigned int)primitive(0),
14866             i1 = (unsigned int)primitive(1),
14867             i2 = (unsigned int)primitive(2);
14868           if (i0>=_width || i1>=_width || i2>=_width) {
14869             if (error_message) cimg_sprintf(error_message,
14870                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
14871                                             "triangle primitive [%u]",
14872                                             _width,primitives._width,i0,i1,i2,l);
14873             return false;
14874           }
14875         } break;
14876         case 4 : // Quadrangle.
14877         case 12 : {
14878           const unsigned int
14879             i0 = (unsigned int)primitive(0),
14880             i1 = (unsigned int)primitive(1),
14881             i2 = (unsigned int)primitive(2),
14882             i3 = (unsigned int)primitive(3);
14883           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
14884             if (error_message) cimg_sprintf(error_message,
14885                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
14886                                             "quadrangle primitive [%u]",
14887                                             _width,primitives._width,i0,i1,i2,i3,l);
14888             return false;
14889           }
14890         } break;
14891         default :
14892           if (error_message) cimg_sprintf(error_message,
14893                                           "3d object (%u,%u) defines an invalid primitive [%u] of size %u",
14894                                           _width,primitives._width,l,(unsigned int)psiz);
14895           return false;
14896         }
14897       }
14898 
14899       // Check consistency of colors.
14900       cimglist_for(colors,c) {
14901         const CImg<tc>& color = colors[c];
14902         if (!color) {
14903           if (error_message) cimg_sprintf(error_message,
14904                                           "3d object (%u,%u) defines no color for primitive [%u]",
14905                                           _width,primitives._width,c);
14906           return false;
14907         }
14908       }
14909 
14910       // Check consistency of light texture.
14911       if (colors._width>primitives._width) {
14912         const CImg<tc> &light = colors.back();
14913         if (!light || light._depth>1) {
14914           if (error_message) cimg_sprintf(error_message,
14915                                           "3d object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
14916                                           _width,primitives._width,light._width,
14917                                           light._height,light._depth,light._spectrum);
14918           return false;
14919         }
14920       }
14921 
14922       return true;
14923     }
14924 
14925     //! Test if image instance represents a valid serialization of a 3d object.
14926     /**
14927        Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise.
14928        \param full_check Tells if full checking of the instance must be performed.
14929        \param[out] error_message C-string to contain the error message, if the test does not succeed.
14930        \note
14931        - Set \c full_check to \c false to speed-up the 3d object checking. In this case, only the size of
14932          each 3d object component is checked.
14933        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
14934     **/
14935     bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
14936       if (error_message) *error_message = 0;
14937 
14938       // Check instance dimension and header.
14939       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
14940         if (error_message) cimg_sprintf(error_message,
14941                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
14942                                         _width,_height,_depth,_spectrum);
14943         return false;
14944       }
14945       const T *ptrs = _data, *const ptre = end();
14946       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
14947           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
14948         if (error_message) cimg_sprintf(error_message,
14949                                         "CImg3d header not found");
14950         return false;
14951       }
14952       const unsigned int
14953         nb_points = cimg::float2uint((float)*(ptrs++)),
14954         nb_primitives = cimg::float2uint((float)*(ptrs++));
14955 
14956       // Check consistency of number of vertices / primitives.
14957       if (!full_check) {
14958         const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
14959         if (_data + minimal_size>ptre) {
14960           if (error_message) cimg_sprintf(error_message,
14961                                           "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
14962                                           nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
14963           return false;
14964         }
14965       }
14966 
14967       // Check consistency of vertex data.
14968       if (!nb_points) {
14969         if (nb_primitives) {
14970           if (error_message) cimg_sprintf(error_message,
14971                                           "CImg3d (%u,%u) defines no vertices but %u primitives",
14972                                           nb_points,nb_primitives,nb_primitives);
14973           return false;
14974         }
14975         if (ptrs!=ptre) {
14976           if (error_message) cimg_sprintf(error_message,
14977                                           "CImg3d (%u,%u) is an empty object but contains %u value%s "
14978                                           "more than expected",
14979                                           nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
14980           return false;
14981         }
14982         return true;
14983       }
14984       if (ptrs + 3*nb_points>ptre) {
14985         if (error_message) cimg_sprintf(error_message,
14986                                         "CImg3d (%u,%u) defines only %u vertices data",
14987                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
14988         return false;
14989       }
14990       ptrs+=3*nb_points;
14991 
14992       // Check consistency of primitive data.
14993       if (ptrs==ptre) {
14994         if (error_message) cimg_sprintf(error_message,
14995                                         "CImg3d (%u,%u) defines %u vertices but no primitive",
14996                                         nb_points,nb_primitives,nb_points);
14997         return false;
14998       }
14999 
15000       if (!full_check) return true;
15001 
15002       for (unsigned int p = 0; p<nb_primitives; ++p) {
15003         const unsigned int nb_inds = (unsigned int)*(ptrs++);
15004         switch (nb_inds) {
15005         case 1 : { // Point.
15006           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
15007           if (i0>=nb_points) {
15008             if (error_message) cimg_sprintf(error_message,
15009                                             "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]",
15010                                             nb_points,nb_primitives,i0,p);
15011             return false;
15012           }
15013         } break;
15014         case 5 : { // Sphere.
15015           const unsigned int
15016             i0 = cimg::float2uint((float)*(ptrs++)),
15017             i1 = cimg::float2uint((float)*(ptrs++));
15018           ptrs+=3;
15019           if (i0>=nb_points || i1>=nb_points) {
15020             if (error_message) cimg_sprintf(error_message,
15021                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
15022                                             "sphere primitive [%u]",
15023                                             nb_points,nb_primitives,i0,i1,p);
15024             return false;
15025           }
15026         } break;
15027         case 2 : case 6 : { // Segment.
15028           const unsigned int
15029             i0 = cimg::float2uint((float)*(ptrs++)),
15030             i1 = cimg::float2uint((float)*(ptrs++));
15031           if (nb_inds==6) ptrs+=4;
15032           if (i0>=nb_points || i1>=nb_points) {
15033             if (error_message) cimg_sprintf(error_message,
15034                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
15035                                             "segment primitive [%u]",
15036                                             nb_points,nb_primitives,i0,i1,p);
15037             return false;
15038           }
15039         } break;
15040         case 3 : case 9 : { // Triangle.
15041           const unsigned int
15042             i0 = cimg::float2uint((float)*(ptrs++)),
15043             i1 = cimg::float2uint((float)*(ptrs++)),
15044             i2 = cimg::float2uint((float)*(ptrs++));
15045           if (nb_inds==9) ptrs+=6;
15046           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
15047             if (error_message) cimg_sprintf(error_message,
15048                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
15049                                             "triangle primitive [%u]",
15050                                             nb_points,nb_primitives,i0,i1,i2,p);
15051             return false;
15052           }
15053         } break;
15054         case 4 : case 12 : { // Quadrangle.
15055           const unsigned int
15056             i0 = cimg::float2uint((float)*(ptrs++)),
15057             i1 = cimg::float2uint((float)*(ptrs++)),
15058             i2 = cimg::float2uint((float)*(ptrs++)),
15059             i3 = cimg::float2uint((float)*(ptrs++));
15060           if (nb_inds==12) ptrs+=8;
15061           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
15062             if (error_message) cimg_sprintf(error_message,
15063                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
15064                                             "quadrangle primitive [%u]",
15065                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
15066             return false;
15067           }
15068         } break;
15069         default :
15070           if (error_message) cimg_sprintf(error_message,
15071                                           "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
15072                                           nb_points,nb_primitives,p,nb_inds);
15073           return false;
15074         }
15075         if (ptrs>ptre) {
15076           if (error_message) cimg_sprintf(error_message,
15077                                           "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
15078                                           "%u values missing",
15079                                           nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
15080           return false;
15081         }
15082       }
15083 
15084       // Check consistency of color data.
15085       if (ptrs==ptre) {
15086         if (error_message) cimg_sprintf(error_message,
15087                                         "CImg3d (%u,%u) defines no color/texture data",
15088                                         nb_points,nb_primitives);
15089         return false;
15090       }
15091       for (unsigned int c = 0; c<nb_primitives; ++c) {
15092         if (*(ptrs++)!=(T)-128) ptrs+=2;
15093         else if ((ptrs+=3)<ptre) {
15094           const unsigned int
15095             w = (unsigned int)*(ptrs - 3),
15096             h = (unsigned int)*(ptrs - 2),
15097             s = (unsigned int)*(ptrs - 1);
15098           if (!h && !s) {
15099             if (w>=c) {
15100               if (error_message) cimg_sprintf(error_message,
15101                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u "
15102                                               "for primitive [%u]",
15103                                               nb_points,nb_primitives,w,c);
15104               return false;
15105             }
15106           } else ptrs+=w*h*s;
15107         }
15108         if (ptrs>ptre) {
15109           if (error_message) cimg_sprintf(error_message,
15110                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
15111                                           "%u values missing",
15112                                           nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
15113           return false;
15114         }
15115       }
15116 
15117       // Check consistency of opacity data.
15118       if (ptrs==ptre) {
15119         if (error_message) cimg_sprintf(error_message,
15120                                         "CImg3d (%u,%u) defines no opacity data",
15121                                         nb_points,nb_primitives);
15122         return false;
15123       }
15124       for (unsigned int o = 0; o<nb_primitives; ++o) {
15125         if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
15126           const unsigned int
15127             w = (unsigned int)*(ptrs - 3),
15128             h = (unsigned int)*(ptrs - 2),
15129             s = (unsigned int)*(ptrs - 1);
15130           if (!h && !s) {
15131             if (w>=o) {
15132               if (error_message) cimg_sprintf(error_message,
15133                                               "CImg3d (%u,%u) refers to invalid shared opacity indice %u "
15134                                               "for primitive [%u]",
15135                                               nb_points,nb_primitives,w,o);
15136               return false;
15137             }
15138           } else ptrs+=w*h*s;
15139         }
15140         if (ptrs>ptre) {
15141           if (error_message) cimg_sprintf(error_message,
15142                                           "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
15143                                           nb_points,nb_primitives,o);
15144           return false;
15145         }
15146       }
15147 
15148       // Check end of data.
15149       if (ptrs<ptre) {
15150         if (error_message) cimg_sprintf(error_message,
15151                                         "CImg3d (%u,%u) contains %u value%s more than expected",
15152                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
15153         return false;
15154       }
15155       return true;
15156     }
15157 
15158     static bool _is_CImg3d(const T val, const char c) {
15159       return val>=(T)c && val<(T)(c + 1);
15160     }
15161 
15162     //@}
15163     //-------------------------------------
15164     //
15165     //! \name Mathematical Functions
15166     //@{
15167     //-------------------------------------
15168 
15169     // Define the math formula parser/compiler and expression evaluator.
15170     struct _cimg_math_parser {
15171       CImg<doubleT> mem;
15172       CImg<intT> memtype;
15173       CImgList<ulongT> _code, &code, code_init, code_end;
15174       CImg<ulongT> opcode;
15175       const CImg<ulongT> *p_code_end, *p_code;
15176       const CImg<ulongT> *const p_break;
15177 
15178       CImg<charT> expr, pexpr;
15179       const CImg<T>& imgin;
15180       const CImgList<T>& listin;
15181       CImg<T> &imgout;
15182       CImgList<T>& listout;
15183 
15184       CImg<doubleT> _img_stats, &img_stats, constcache_vals;
15185       CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median;
15186       CImg<uintT> mem_img_stats, constcache_inds;
15187 
15188       CImg<uintT> level, variable_pos, reserved_label;
15189       CImgList<charT> variable_def, macro_def, macro_body;
15190       CImgList<boolT> macro_body_is_string;
15191       char *user_macro;
15192 
15193       unsigned int mempos, mem_img_median, debug_indent, result_dim, break_type, constcache_size;
15194       bool is_parallelizable, is_fill, need_input_copy;
15195       double *result;
15196       const char *const calling_function, *s_op, *ss_op;
15197       typedef double (*mp_func)(_cimg_math_parser&);
15198 
15199 #define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value?
15200 #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
15201 #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
15202 #define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable?
15203 #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
15204 #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
15205 #define _cimg_mp_calling_function calling_function_s()._data
15206 #define _cimg_mp_op(s) s_op = s; ss_op = ss
15207 #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
15208 #define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char)
15209 #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
15210 #define _cimg_mp_check_vector0(dim) check_vector0(dim,ss,se,saved_char)
15211 #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char)
15212 #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
15213 #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
15214 #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
15215 #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val)))
15216 #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
15217 #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
15218 #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
15219 #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
15220 #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
15221 #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
15222 #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
15223 #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
15224 #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
15225 #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
15226 #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
15227 #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
15228 #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
15229 
15230       // Constructors.
15231       _cimg_math_parser(const char *const expression, const char *const funcname=0,
15232                         const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
15233                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0,
15234                         const bool _is_fill=false):
15235         code(_code),p_break((CImg<ulongT>*)0 - 2),
15236         imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()),
15237         imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()),
15238         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_macro(0),
15239         mem_img_median(~0U),debug_indent(0),result_dim(0),break_type(0),constcache_size(0),
15240         is_parallelizable(true),is_fill(_is_fill),need_input_copy(false),
15241         calling_function(funcname?funcname:"cimg_math_parser") {
15242         if (!expression || !*expression)
15243           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15244                                       "CImg<%s>::%s: Empty expression.",
15245                                       pixel_type(),_cimg_mp_calling_function);
15246         const char *_expression = expression;
15247         while (*_expression && ((signed char)*_expression<=' ' || *_expression==';')) ++_expression;
15248         CImg<charT>::string(_expression).move_to(expr);
15249         char *ps = &expr.back() - 1;
15250         while (ps>expr._data && ((signed char)*ps<=' ' || *ps==';')) --ps;
15251         *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
15252 
15253         // Ease the retrieval of previous non-space characters afterwards.
15254         pexpr.assign(expr._width);
15255         char c, *pe = pexpr._data;
15256         for (ps = expr._data, c = ' '; *ps; ++ps) {
15257           if ((signed char)*ps>' ') c = *ps; else *ps = ' ';
15258           *(pe++) = c;
15259         }
15260         *pe = 0;
15261         level = get_level(expr);
15262 
15263         // Init constant values.
15264 #define _cimg_mp_interpolation (reserved_label[29]!=~0U?reserved_label[29]:0)
15265 #define _cimg_mp_boundary (reserved_label[30]!=~0U?reserved_label[30]:0)
15266 #define _cimg_mp_slot_nan 29
15267 #define _cimg_mp_slot_x 30
15268 #define _cimg_mp_slot_y 31
15269 #define _cimg_mp_slot_z 32
15270 #define _cimg_mp_slot_c 33
15271 
15272         mem.assign(96);
15273         for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
15274         for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
15275         mem[16] = 0.5;
15276         mem[17] = 0; // thread_id
15277         mem[18] = (double)imgin._width; // w
15278         mem[19] = (double)imgin._height; // h
15279         mem[20] = (double)imgin._depth; // d
15280         mem[21] = (double)imgin._spectrum; // s
15281         mem[22] = (double)imgin._is_shared; // r
15282         mem[23] = (double)imgin._width*imgin._height; // wh
15283         mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
15284         mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
15285         mem[26] = (double)listin._width; // l
15286         mem[27] = std::exp(1.0); // e
15287         mem[28] = cimg::PI; // pi
15288         mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
15289 
15290         // Set value property :
15291         // { -2 = other | -1 = variable | 0 = computation value |
15292         //    1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
15293         memtype.assign(mem._width,1,1,1,0);
15294         for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
15295         memtype[17] = 0;
15296         memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2;
15297         mempos = _cimg_mp_slot_c + 1;
15298         variable_pos.assign(8);
15299 
15300         reserved_label.assign(128,1,1,1,~0U);
15301         // reserved_label[4-28] are used to store these two-char variables:
15302         // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv,
15303         // [8] = is, [9] = ip, [10] = ic, [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM,
15304         // [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, [29] = interpolation, [30] = boundary
15305 
15306         // Compile expression into a serie of opcodes.
15307         s_op = ""; ss_op = expr._data;
15308         const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false);
15309         if (!_cimg_mp_is_constant(ind_result)) {
15310           if (_cimg_mp_is_vector(ind_result))
15311             CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
15312               fill(cimg::type<double>::nan());
15313           else mem[ind_result] = cimg::type<double>::nan();
15314         }
15315 
15316         // Free resources used for compiling expression and prepare evaluation.
15317         result_dim = _cimg_mp_size(ind_result);
15318         if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
15319         result = mem._data + ind_result;
15320         memtype.assign();
15321         constcache_vals.assign();
15322         constcache_inds.assign();
15323         level.assign();
15324         variable_pos.assign();
15325         reserved_label.assign();
15326         expr.assign();
15327         pexpr.assign();
15328         opcode.assign();
15329         opcode._is_shared = true;
15330 
15331         // Execute init() bloc if any specified.
15332         if (code_init) {
15333           mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
15334           p_code_end = code_init.end();
15335           for (p_code = code_init; p_code<p_code_end; ++p_code) {
15336             opcode._data = p_code->_data;
15337             const ulongT target = opcode[1];
15338             mem[target] = _cimg_mp_defunc(*this);
15339           }
15340         }
15341         p_code_end = code.end();
15342       }
15343 
15344       _cimg_math_parser():
15345         code(_code),p_code_end(0),p_break((CImg<ulongT>*)0 - 2),
15346         imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
15347         imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
15348         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0),
15349         result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),need_input_copy(false),
15350         calling_function(0) {
15351         mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
15352         result = mem._data;
15353       }
15354 
15355       _cimg_math_parser(const _cimg_math_parser& mp):
15356         mem(mp.mem),code(mp.code),p_code_end(mp.p_code_end),p_break(mp.p_break),
15357         imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats),
15358         list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),result_dim(mp.result_dim),
15359         break_type(0),constcache_size(0),is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),
15360         need_input_copy(mp.need_input_copy), result(mem._data + (mp.result - mp.mem._data)),calling_function(0) {
15361 #ifdef cimg_use_openmp
15362         mem[17] = omp_get_thread_num();
15363 #endif
15364         opcode.assign();
15365         opcode._is_shared = true;
15366       }
15367 
15368       // Count parentheses/brackets level of each character of the expression.
15369       CImg<uintT> get_level(CImg<charT>& expr) const {
15370         bool is_escaped = false, next_is_escaped = false;
15371         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
15372         CImg<uintT> res(expr._width - 1);
15373         unsigned int *pd = res._data;
15374         int level = 0;
15375         for (const char *ps = expr._data; *ps && level>=0; ++ps) {
15376           if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
15377           if (!is_escaped && *ps=='\'') { // Non-escaped character
15378             if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
15379             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
15380             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
15381           }
15382           *(pd++) = (unsigned int)(mode>=1 || is_escaped?level + (mode==1):
15383                                    *ps=='(' || *ps=='['?level++:
15384                                    *ps==')' || *ps==']'?--level:
15385                                    level);
15386           mode = next_mode;
15387           is_escaped = next_is_escaped;
15388           next_is_escaped = false;
15389         }
15390         if (mode) {
15391           cimg::strellipsize(expr,64);
15392           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15393                                       "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
15394                                       pixel_type(),_cimg_mp_calling_function,
15395                                       expr._data);
15396         }
15397         if (level) {
15398           cimg::strellipsize(expr,64);
15399           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15400                                       "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
15401                                       pixel_type(),_cimg_mp_calling_function,
15402                                       expr._data);
15403         }
15404         return res;
15405       }
15406 
15407       // Tell for each character of an expression if it is inside a string or not.
15408       CImg<boolT> is_inside_string(CImg<charT>& expr) const {
15409         bool is_escaped = false, next_is_escaped = false;
15410         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
15411         CImg<boolT> res = CImg<charT>::string(expr);
15412         bool *pd = res._data;
15413         for (const char *ps = expr._data; *ps; ++ps) {
15414           if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
15415           if (!is_escaped && *ps=='\'') { // Non-escaped character
15416             if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
15417             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
15418             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
15419           }
15420           *(pd++) = mode>=1 || is_escaped;
15421           mode = next_mode;
15422           is_escaped = next_is_escaped;
15423           next_is_escaped = false;
15424         }
15425         return res;
15426       }
15427 
15428       // Compilation procedure.
15429       unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
15430                            const bool is_single) {
15431         if (depth>256) {
15432           cimg::strellipsize(expr,64);
15433           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15434                                       "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
15435                                       "in expression '%s%s%s'.",
15436                                       pixel_type(),_cimg_mp_calling_function,
15437                                       (ss - 4)>expr._data?"...":"",
15438                                       (ss - 4)>expr._data?ss - 4:expr._data,
15439                                       se<&expr.back()?"...":"");
15440         }
15441         char c1, c2, c3, c4;
15442 
15443         // Simplify expression when possible.
15444         do {
15445           c2 = 0;
15446           if (ss<se) {
15447             while (*ss && ((signed char)*ss<=' ' || *ss==';')) ++ss;
15448             while (se>ss && ((signed char)(c1 = *(se - 1))<=' ' || c1==';')) --se;
15449           }
15450           while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) {
15451             ++ss; --se; c2 = 1;
15452           }
15453         } while (c2 && ss<se);
15454 
15455         if (se<=ss || !*ss) {
15456           cimg::strellipsize(expr,64);
15457           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15458                                       "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.",
15459                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
15460                                       *s_op=='F'?"argument":"item",
15461                                       (ss_op - 4)>expr._data?"...":"",
15462                                       (ss_op - 4)>expr._data?ss_op - 4:expr._data,
15463                                       ss_op + std::strlen(ss_op)<&expr.back()?"...":"");
15464         }
15465 
15466         const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
15467         const unsigned int depth1 = depth + 1;
15468         unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
15469         char
15470           *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
15471           *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
15472           *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
15473           *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
15474         double val, val1, val2;
15475         mp_func op;
15476 
15477         // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
15478         // linked to the returned memory slot (reference that cannot be determined at compile time).
15479         // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
15480         //                   3 = image value (coordinates) | 4 = image value as a vector (offsets) |
15481         //                   5 = image value as a vector (coordinates) }.
15482         // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
15483         // When p_ref[0]==0, p_ref is actually unlinked.
15484         // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
15485         // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
15486         // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
15487         // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
15488         // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
15489         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; }
15490 
15491         const char saved_char = *se; *se = 0;
15492         const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
15493         bool is_sth, is_relative;
15494         CImg<uintT> ref;
15495         CImgList<ulongT> _opcode;
15496         CImg<charT> variable_name;
15497 
15498         // Look for a single value or a pre-defined variable.
15499         int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
15500 
15501 #if cimg_OS==2
15502         // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
15503         // to read those particular values.
15504         if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
15505           is_sth = true;
15506           s = ss;
15507           if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; }
15508           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
15509           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
15510           if (nb==1 && !is_sth) val = -val;
15511         }
15512 #endif
15513         if (nb==1) _cimg_mp_constant(val);
15514         if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
15515 
15516         if (ss1==se) switch (*ss) { // One-char reserved variable
15517           case 'c' : _cimg_mp_return(reserved_label['c']!=~0U?reserved_label['c']:_cimg_mp_slot_c);
15518           case 'd' : _cimg_mp_return(reserved_label['d']!=~0U?reserved_label['d']:20);
15519           case 'e' : _cimg_mp_return(reserved_label['e']!=~0U?reserved_label['e']:27);
15520           case 'h' : _cimg_mp_return(reserved_label['h']!=~0U?reserved_label['h']:19);
15521           case 'l' : _cimg_mp_return(reserved_label['l']!=~0U?reserved_label['l']:26);
15522           case 'r' : _cimg_mp_return(reserved_label['r']!=~0U?reserved_label['r']:22);
15523           case 's' : _cimg_mp_return(reserved_label['s']!=~0U?reserved_label['s']:21);
15524           case 't' : _cimg_mp_return(reserved_label['t']!=~0U?reserved_label['t']:17);
15525           case 'w' : _cimg_mp_return(reserved_label['w']!=~0U?reserved_label['w']:18);
15526           case 'x' : _cimg_mp_return(reserved_label['x']!=~0U?reserved_label['x']:_cimg_mp_slot_x);
15527           case 'y' : _cimg_mp_return(reserved_label['y']!=~0U?reserved_label['y']:_cimg_mp_slot_y);
15528           case 'z' : _cimg_mp_return(reserved_label['z']!=~0U?reserved_label['z']:_cimg_mp_slot_z);
15529           case 'u' :
15530             if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']);
15531             _cimg_mp_scalar2(mp_u,0,1);
15532           case 'g' :
15533             if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']);
15534             _cimg_mp_scalar0(mp_g);
15535           case 'i' :
15536             if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']);
15537             _cimg_mp_scalar0(mp_i);
15538           case 'I' :
15539             _cimg_mp_op("Variable 'I'");
15540             if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']);
15541             _cimg_mp_check_vector0(imgin._spectrum);
15542             need_input_copy = true;
15543             pos = vector(imgin._spectrum);
15544             CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
15545             _cimg_mp_return(pos);
15546           case 'R' :
15547             if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']);
15548             need_input_copy = true;
15549             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
15550           case 'G' :
15551             if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']);
15552             need_input_copy = true;
15553             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
15554           case 'B' :
15555             if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']);
15556             need_input_copy = true;
15557             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
15558           case 'A' :
15559             if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']);
15560             need_input_copy = true;
15561             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
15562           }
15563         else if (ss2==se) { // Two-chars reserved variable
15564           arg1 = arg2 = ~0U;
15565           if (*ss=='w' && *ss1=='h') // wh
15566             _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
15567           if (*ss=='p' && *ss1=='i') // pi
15568             _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
15569           if (*ss=='i') {
15570             if (*ss1>='0' && *ss1<='9') { // i0...i9
15571               pos = 19 + *ss1 - '0';
15572               if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
15573               need_input_copy = true;
15574               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 19,0,0);
15575             }
15576             switch (*ss1) {
15577             case 'm' : arg1 = 4; arg2 = 0; break; // im
15578             case 'M' : arg1 = 5; arg2 = 1; break; // iM
15579             case 'a' : arg1 = 6; arg2 = 2; break; // ia
15580             case 'v' : arg1 = 7; arg2 = 3; break; // iv
15581             case 's' : arg1 = 8; arg2 = 12; break; // is
15582             case 'p' : arg1 = 9; arg2 = 13; break; // ip
15583             case 'c' : // ic
15584               if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
15585               if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
15586               _cimg_mp_return(mem_img_median);
15587               break;
15588             }
15589           }
15590           else if (*ss1=='m') switch (*ss) {
15591             case 'x' : arg1 = 11; arg2 = 4; break; // xm
15592             case 'y' : arg1 = 12; arg2 = 5; break; // ym
15593             case 'z' : arg1 = 13; arg2 = 6; break; // zm
15594             case 'c' : arg1 = 14; arg2 = 7; break; // cm
15595             }
15596           else if (*ss1=='M') switch (*ss) {
15597             case 'x' : arg1 = 15; arg2 = 8; break; // xM
15598             case 'y' : arg1 = 16; arg2 = 9; break; // yM
15599             case 'z' : arg1 = 17; arg2 = 10; break; // zM
15600             case 'c' : arg1 = 18; arg2 = 11; break; // cM
15601             }
15602           if (arg1!=~0U) {
15603             if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
15604             if (!img_stats) {
15605               img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
15606               mem_img_stats.assign(1,14,1,1,~0U);
15607             }
15608             if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
15609             _cimg_mp_return(mem_img_stats[arg2]);
15610           }
15611         } else if (ss3==se) { // Three-chars reserved variable
15612           if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
15613             _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
15614         } else if (ss4==se) { // Four-chars reserved variable
15615           if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
15616             _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
15617         }
15618 
15619         pos = ~0U;
15620         is_sth = false;
15621         for (s0 = ss, s = ss1; s<se1; ++s)
15622           if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
15623             arg1 = code_end._width;
15624             arg2 = compile(s0,s++,depth,0,is_single);
15625             if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
15626             is_sth = true;
15627             while (*s && ((signed char)*s<=' ' || *s==';')) ++s;
15628             s0 = s;
15629           }
15630         if (is_sth) {
15631           arg1 = code_end._width;
15632           arg2 = compile(s0,se,depth,p_ref,is_single);
15633           if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
15634           _cimg_mp_return(pos);
15635         }
15636 
15637         // Declare / assign variable, vector value or image value.
15638         for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
15639           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
15640               *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
15641               *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
15642               level[s - expr._data]==clevel) {
15643             variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
15644             cimg::strpare(variable_name,false,true);
15645             const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
15646             char *const ve1 = ss + l_variable_name - 1;
15647             _cimg_mp_op("Operator '='");
15648 
15649             // Assign image value (direct).
15650             if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
15651                 (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) {
15652               is_relative = *ss=='j' || *ss=='J';
15653 
15654               if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
15655                 if (!is_single) is_parallelizable = false;
15656                 if (*ss2=='#') { // Index specified
15657                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
15658                   p1 = compile(ss3,s0++,depth1,0,is_single);
15659                   _cimg_mp_check_list(true);
15660                 } else { p1 = ~0U; s0 = ss2; }
15661                 arg1 = compile(s0,ve1,depth1,0,is_single); // Offset
15662                 _cimg_mp_check_type(arg1,0,1,0);
15663                 arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15664                 if (_cimg_mp_is_vector(arg2)) {
15665                   p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
15666                   if (p1==~0U) p2 = imgin._spectrum;
15667                   else if (_cimg_mp_is_constant(p1)) {
15668                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
15669                     p2 = listin[p3]._spectrum;
15670                   }
15671                   _cimg_mp_check_vector0(p2);
15672                 } else p2 = 0;
15673                 _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,p2);
15674 
15675                 if (p_ref) {
15676                   *p_ref = _cimg_mp_is_vector(arg2)?4:2;
15677                   p_ref[1] = p1;
15678                   p_ref[2] = (unsigned int)is_relative;
15679                   p_ref[3] = arg1;
15680                   if (_cimg_mp_is_vector(arg2))
15681                     set_variable_vector(arg2); // Prevent from being used in further optimization
15682                   else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15683                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
15684                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
15685                 }
15686 
15687 
15688                 if (p1!=~0U) {
15689                   if (!listout) _cimg_mp_return(arg2);
15690                   if (*ss>='i')
15691                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
15692                                         arg2,p1,arg1).move_to(code);
15693                   else if (_cimg_mp_is_scalar(arg2))
15694                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
15695                                         arg2,p1,arg1).move_to(code);
15696                   else
15697                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
15698                                         arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
15699                 } else {
15700                   if (!imgout) _cimg_mp_return(arg2);
15701                   if (*ss>='i')
15702                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
15703                                         arg2,arg1).move_to(code);
15704                   else if (_cimg_mp_is_scalar(arg2))
15705                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
15706                                         arg2,arg1).move_to(code);
15707                   else
15708                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
15709                                         arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
15710                 }
15711                 _cimg_mp_return(arg2);
15712               }
15713 
15714               if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
15715                 if (!is_single) is_parallelizable = false;
15716                 if (*ss2=='#') { // Index specified
15717                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
15718                   p1 = compile(ss3,s0++,depth1,0,is_single);
15719                   _cimg_mp_check_list(true);
15720                 } else { p1 = ~0U; s0 = ss2; }
15721                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
15722                 arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
15723                 arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
15724                 arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
15725                 arg5 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15726                 if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
15727                   s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
15728                   arg1 = compile(s0,s1,depth1,0,is_single);
15729                   if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
15730                     p2 = _cimg_mp_size(arg1); // Vector size
15731                     ++arg1;
15732                     if (p2>1) {
15733                       arg2 = arg1 + 1;
15734                       if (p2>2) {
15735                         arg3 = arg2 + 1;
15736                         if (p2>3) arg4 = arg3 + 1;
15737                       }
15738                     }
15739                   } else if (s1<ve1) { // Y
15740                     s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
15741                     arg2 = compile(s1,s2,depth1,0,is_single);
15742                     if (s2<ve1) { // Z
15743                       s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
15744                       arg3 = compile(s2,s3,depth1,0,is_single);
15745                       if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,is_single); // C
15746                     }
15747                   }
15748                 }
15749 
15750                 if (_cimg_mp_is_vector(arg5)) {
15751                   p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
15752                   if (p1==~0U) p2 = imgin._spectrum;
15753                   else if (_cimg_mp_is_constant(p1)) {
15754                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
15755                     p2 = listin[p3]._spectrum;
15756                   }
15757                   _cimg_mp_check_vector0(p2);
15758                 } else p2 = 0;
15759                 _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,p2);
15760 
15761                 if (p_ref) {
15762                   *p_ref = _cimg_mp_is_vector(arg5)?5:3;
15763                   p_ref[1] = p1;
15764                   p_ref[2] = (unsigned int)is_relative;
15765                   p_ref[3] = arg1;
15766                   p_ref[4] = arg2;
15767                   p_ref[5] = arg3;
15768                   p_ref[6] = arg4;
15769                   if (_cimg_mp_is_vector(arg5))
15770                     set_variable_vector(arg5); // Prevent from being used in further optimization
15771                   else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -2;
15772                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
15773                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
15774                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15775                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
15776                   if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
15777                 }
15778                 if (p1!=~0U) {
15779                   if (!listout) _cimg_mp_return(arg5);
15780                   if (*ss>='i')
15781                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
15782                                         arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
15783                   else if (_cimg_mp_is_scalar(arg5))
15784                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
15785                                         arg5,p1,arg1,arg2,arg3).move_to(code);
15786                   else
15787                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
15788                                         arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
15789                 } else {
15790                   if (!imgout) _cimg_mp_return(arg5);
15791                   if (*ss>='i')
15792                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
15793                                         arg5,arg1,arg2,arg3,arg4).move_to(code);
15794                   else if (_cimg_mp_is_scalar(arg5))
15795                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
15796                                         arg5,arg1,arg2,arg3).move_to(code);
15797                   else
15798                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
15799                                         arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
15800                 }
15801                 _cimg_mp_return(arg5);
15802               }
15803             }
15804 
15805             // Assign vector value (direct).
15806             if (l_variable_name>3 && *ve1==']' && *ss!='[') {
15807               s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
15808               is_sth = true; // is_valid_variable_name?
15809               if (*ss>='0' && *ss<='9') is_sth = false;
15810               else for (ns = ss; ns<s0; ++ns)
15811                      if (!is_varchar(*ns)) { is_sth = false; break; }
15812               if (is_sth && s0>ss) {
15813                 variable_name[s0 - ss] = 0; // Remove brackets in variable name
15814                 arg1 = ~0U; // Vector slot
15815                 arg2 = compile(++s0,ve1,depth1,0,is_single); // Index
15816                 arg3 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15817                 _cimg_mp_check_type(arg3,2,1,0);
15818 
15819                 if (variable_name[1]) { // Multi-char variable
15820                   cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) {
15821                     arg1 = variable_pos[i]; break;
15822                   }
15823                 } else arg1 = reserved_label[*variable_name]; // Single-char variable
15824                 if (arg1==~0U) compile(ss,s0 - 1,depth1,0,is_single); // Variable does not exist -> error
15825                 else { // Variable already exists
15826                   if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0,is_single); // Variable is not a vector -> error
15827                   if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly
15828                     nb = (int)mem[arg2];
15829                     if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
15830                       arg1+=nb + 1;
15831                       CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
15832                       _cimg_mp_return(arg1);
15833                     }
15834                     compile(ss,s,depth1,0,is_single); // Out-of-bounds reference -> error
15835                   }
15836 
15837                   // Case of non-constant index -> return assigned value + linked reference
15838                   if (p_ref) {
15839                     *p_ref = 1;
15840                     p_ref[1] = arg1;
15841                     p_ref[2] = arg2;
15842                     if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization
15843                     if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15844                   }
15845                   CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),
15846                                        arg2,arg3).
15847                     move_to(code);
15848                   _cimg_mp_return(arg3);
15849                 }
15850               }
15851             }
15852 
15853             // Assign user-defined macro.
15854             if (l_variable_name>2 && *ve1==')' && *ss!='(') {
15855               s0 = ve1; while (s0>ss && *s0!='(') --s0;
15856               is_sth = std::strncmp(variable_name,"debug(",6) &&
15857                 std::strncmp(variable_name,"print(",6); // is_valid_function_name?
15858               if (*ss>='0' && *ss<='9') is_sth = false;
15859               else for (ns = ss; ns<s0; ++ns)
15860                      if (!is_varchar(*ns)) { is_sth = false; break; }
15861 
15862               if (is_sth && s0>ss) { // Looks like a valid function declaration
15863                 s0 = variable_name._data + (s0 - ss);
15864                 *s0 = 0;
15865                 s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
15866                 CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
15867                 ++s; while (*s && (signed char)*s<=' ') ++s;
15868                 CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
15869 
15870                 p1 = 1; // Indice of current parsed argument
15871                 for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
15872                   if (p1>24) {
15873                     *se = saved_char;
15874                     cimg::strellipsize(variable_name,64);
15875                     s0 = ss - 4>expr._data?ss - 4:expr._data;
15876                     cimg::strellipsize(s0,64);
15877                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
15878                                                 "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
15879                                                 "definition '%s()', in expression '%s%s%s'.",
15880                                                 pixel_type(),_cimg_mp_calling_function,s_op,
15881                                                 variable_name._data,
15882                                                 s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
15883                   }
15884                   while (*s && (signed char)*s<=' ') ++s;
15885                   if (*s==')' && p1==1) break; // Function has no arguments
15886 
15887                   s2 = s; // Start of the argument name
15888                   is_sth = true; // is_valid_argument_name?
15889                   if (*s>='0' && *s<='9') is_sth = false;
15890                   else for (ns = s; ns<s1 && *ns!=',' && (signed char)*ns>' '; ++ns)
15891                          if (!is_varchar(*ns)) { is_sth = false; break; }
15892                   s3 = ns; // End of the argument name
15893                   while (*ns && (signed char)*ns<=' ') ++ns;
15894                   if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
15895                     *se = saved_char;
15896                     cimg::strellipsize(variable_name,64);
15897                     s0 = ss - 4>expr._data?ss - 4:expr._data;
15898                     cimg::strellipsize(s0,64);
15899                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
15900                                                 "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
15901                                                 "macro '%s()', in expression '%s%s%s'.",
15902                                                 pixel_type(),_cimg_mp_calling_function,s_op,
15903                                                 is_sth?"Empty":"Invalid",p1,
15904                                                 variable_name._data,
15905                                                 s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
15906                   }
15907                   if (ns==s1 || *ns==',') { // New argument found
15908                     *s3 = 0;
15909                     p2 = (unsigned int)(s3 - s2); // Argument length
15910                     for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
15911                       if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
15912                             (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
15913                         if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
15914                           *(ps - 1) = (char)p1;
15915                           if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
15916                             std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
15917                             macro_body[0]._width-=p2 + 1;
15918                           } else { // Has pre number sign only
15919                             std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
15920                             macro_body[0]._width-=p2;
15921                           }
15922                         } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
15923                           *(ps++) = (char)p1;
15924                           std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
15925                           macro_body[0]._width-=p2;
15926                         } else { // Not near a number sign
15927                           if (p2<3) {
15928                             ps-=(ulongT)macro_body[0]._data;
15929                             macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
15930                             ps+=(ulongT)macro_body[0]._data;
15931                           } else macro_body[0]._width-=p2 - 3;
15932                           std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
15933                           *(ps++) = '(';
15934                           *(ps++) = (char)p1;
15935                           *(ps++) = ')';
15936                         }
15937                       } else ++ps;
15938                     }
15939                   }
15940                 }
15941 
15942                 // Store number of arguments.
15943                 macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
15944 
15945                 // Detect parts of function body inside a string.
15946                 is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
15947                 _cimg_mp_return_nan();
15948               }
15949             }
15950 
15951             // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
15952             is_sth = true; // is_valid_variable_name?
15953             const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
15954 
15955             s0 = variable_name._data;
15956             if (is_const) {
15957               s0+=6; while ((signed char)*s0<=' ') ++s0;
15958               variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
15959             }
15960 
15961             if (*variable_name>='0' && *variable_name<='9') is_sth = false;
15962             else for (ns = variable_name._data; *ns; ++ns)
15963                    if (!is_varchar(*ns)) { is_sth = false; break; }
15964 
15965             // Assign variable (direct).
15966             if (is_sth) {
15967               arg3 = variable_name[1]?~0U:*variable_name; // One-char variable
15968               if (variable_name[1] && !variable_name[2]) { // Two-chars variable
15969                 c1 = variable_name[0];
15970                 c2 = variable_name[1];
15971                 if (c1=='w' && c2=='h') arg3 = 0; // wh
15972                 else if (c1=='p' && c2=='i') arg3 = 3; // pi
15973                 else if (c1=='i') {
15974                   if (c2>='0' && c2<='9') arg3 = 19 + c2 - '0'; // i0...i9
15975                   else if (c2=='m') arg3 = 4; // im
15976                   else if (c2=='M') arg3 = 5; // iM
15977                   else if (c2=='a') arg3 = 6; // ia
15978                   else if (c2=='v') arg3 = 7; // iv
15979                   else if (c2=='s') arg3 = 8; // is
15980                   else if (c2=='p') arg3 = 9; // ip
15981                   else if (c2=='c') arg3 = 10; // ic
15982                 } else if (c2=='m') {
15983                   if (c1=='x') arg3 = 11; // xm
15984                   else if (c1=='y') arg3 = 12; // ym
15985                   else if (c1=='z') arg3 = 13; // zm
15986                   else if (c1=='c') arg3 = 14; // cm
15987                 } else if (c2=='M') {
15988                   if (c1=='x') arg3 = 15; // xM
15989                   else if (c1=='y') arg3 = 16; // yM
15990                   else if (c1=='z') arg3 = 17; // zM
15991                   else if (c1=='c') arg3 = 18; // cM
15992                 }
15993               } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
15994                 c1 = variable_name[0];
15995                 c2 = variable_name[1];
15996                 c3 = variable_name[2];
15997                 if (c1=='w' && c2=='h' && c3=='d') arg3 = 1; // whd
15998               } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
15999                          !variable_name[4]) { // Four-chars variable
16000                 c1 = variable_name[0];
16001                 c2 = variable_name[1];
16002                 c3 = variable_name[2];
16003                 c4 = variable_name[3];
16004                 if (c1=='w' && c2=='h' && c3=='d' && c4=='s') arg3 = 2; // whds
16005               } else if (!std::strcmp(variable_name,"interpolation")) arg3 = 29; // interpolation
16006               else if (!std::strcmp(variable_name,"boundary")) arg3 = 30; // boundary
16007 
16008               arg1 = ~0U;
16009               arg2 = compile(s + 1,se,depth1,0,is_single);
16010               if (is_const) _cimg_mp_check_constant(arg2,2,0);
16011 
16012               if (arg3!=~0U) // One-char variable, or variable in reserved_labels
16013                 arg1 = reserved_label[arg3];
16014               else // Multi-char variable name : check for existing variable with same name
16015                 cimglist_for(variable_def,i)
16016                   if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; }
16017 
16018               if (arg1==~0U) { // Create new variable
16019                 if (_cimg_mp_is_vector(arg2)) { // Vector variable
16020                   arg1 = is_comp_vector(arg2)?arg2:vector_copy(arg2);
16021                   set_variable_vector(arg1);
16022                 } else { // Scalar variable
16023                   if (is_const) arg1 = arg2;
16024                   else {
16025                     arg1 = _cimg_mp_is_comp(arg2)?arg2:scalar1(mp_copy,arg2);
16026                     memtype[arg1] = -1;
16027                   }
16028                 }
16029 
16030                 if (arg3!=~0U) reserved_label[arg3] = arg1;
16031                 else {
16032                   if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
16033                   variable_pos[variable_def._width] = arg1;
16034                   variable_name.move_to(variable_def);
16035                 }
16036 
16037               } else { // Variable already exists -> assign a new value
16038                 if (is_const || _cimg_mp_is_constant(arg1)) {
16039                   *se = saved_char;
16040                   cimg::strellipsize(variable_name,64);
16041                   s0 = ss - 4>expr._data?ss - 4:expr._data;
16042                   cimg::strellipsize(s0,64);
16043                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
16044                                               "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
16045                                               "in expression '%s%s%s'.",
16046                                               pixel_type(),_cimg_mp_calling_function,s_op,
16047                                               _cimg_mp_is_constant(arg1)?"already-defined ":"non-",
16048                                               variable_name._data,
16049                                               !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"",
16050                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16051                 }
16052                 _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
16053                 if (_cimg_mp_is_vector(arg1)) { // Vector
16054                   if (_cimg_mp_is_vector(arg2)) // From vector
16055                     CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
16056                       move_to(code);
16057                   else // From scalar
16058                     CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
16059                       move_to(code);
16060                 } else // Scalar
16061                   CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
16062               }
16063               _cimg_mp_return(arg1);
16064             }
16065 
16066             // Assign lvalue (variable name was not valid for a direct assignment).
16067             arg1 = ~0U;
16068             is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
16069             if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
16070 
16071             if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
16072               ref.assign(7);
16073               arg1 = compile(ss,s,depth1,ref,is_single); // Lvalue slot
16074               arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
16075 
16076               if (*ref==1) { // Vector value (scalar): V[k] = scalar
16077                 _cimg_mp_check_type(arg2,2,1,0);
16078                 arg3 = ref[1]; // Vector slot
16079                 arg4 = ref[2]; // Index
16080                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16081                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg2).
16082                   move_to(code);
16083                 _cimg_mp_return(arg2);
16084               }
16085 
16086               if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
16087                 if (!is_single) is_parallelizable = false;
16088                 _cimg_mp_check_type(arg2,2,1,0);
16089                 p1 = ref[1]; // Index
16090                 is_relative = (bool)ref[2];
16091                 arg3 = ref[3]; // Offset
16092                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16093                 if (p1!=~0U) {
16094                   if (!listout) _cimg_mp_return(arg2);
16095                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16096                                       arg2,p1,arg3).move_to(code);
16097                 } else {
16098                   if (!imgout) _cimg_mp_return(arg2);
16099                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16100                                       arg2,arg3).move_to(code);
16101                 }
16102                 _cimg_mp_return(arg2);
16103               }
16104 
16105               if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
16106                 if (!is_single) is_parallelizable = false;
16107                 _cimg_mp_check_type(arg2,2,1,0);
16108                 p1 = ref[1]; // Index
16109                 is_relative = (bool)ref[2];
16110                 arg3 = ref[3]; // X
16111                 arg4 = ref[4]; // Y
16112                 arg5 = ref[5]; // Z
16113                 arg6 = ref[6]; // C
16114                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16115                 if (p1!=~0U) {
16116                   if (!listout) _cimg_mp_return(arg2);
16117                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
16118                                       arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
16119                 } else {
16120                   if (!imgout) _cimg_mp_return(arg2);
16121                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
16122                                       arg2,arg3,arg4,arg5,arg6).move_to(code);
16123                 }
16124                 _cimg_mp_return(arg2);
16125               }
16126 
16127               if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
16128                 if (!is_single) is_parallelizable = false;
16129                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16130                 p1 = ref[1]; // Index
16131                 is_relative = (bool)ref[2];
16132                 arg3 = ref[3]; // Offset
16133                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16134                 if (p1!=~0U) {
16135                   if (!listout) _cimg_mp_return(arg2);
16136                   if (_cimg_mp_is_scalar(arg2))
16137                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
16138                                         arg2,p1,arg3).move_to(code);
16139                   else
16140                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16141                                         arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
16142                 } else {
16143                   if (!imgout) _cimg_mp_return(arg2);
16144                   if (_cimg_mp_is_scalar(arg2))
16145                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
16146                                         arg2,arg3).move_to(code);
16147                   else
16148                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16149                                         arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
16150                 }
16151                 _cimg_mp_return(arg2);
16152               }
16153 
16154               if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
16155                 if (!is_single) is_parallelizable = false;
16156                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16157                 p1 = ref[1]; // Index
16158                 is_relative = (bool)ref[2];
16159                 arg3 = ref[3]; // X
16160                 arg4 = ref[4]; // Y
16161                 arg5 = ref[5]; // Z
16162                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16163                 if (p1!=~0U) {
16164                   if (!listout) _cimg_mp_return(arg2);
16165                   if (_cimg_mp_is_scalar(arg2))
16166                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
16167                                         arg2,p1,arg3,arg4,arg5).move_to(code);
16168                   else
16169                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16170                                         arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
16171                 } else {
16172                   if (!imgout) _cimg_mp_return(arg2);
16173                   if (_cimg_mp_is_scalar(arg2))
16174                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
16175                                         arg2,arg3,arg4,arg5).move_to(code);
16176                   else
16177                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16178                                         arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
16179                 }
16180                 _cimg_mp_return(arg2);
16181               }
16182 
16183               if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
16184                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16185                 if (_cimg_mp_is_vector(arg2)) // From vector
16186                   CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
16187                     move_to(code);
16188                 else // From scalar
16189                   CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
16190                     move_to(code);
16191                 _cimg_mp_return(arg1);
16192               }
16193 
16194               if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar
16195                 _cimg_mp_check_type(arg2,2,1,0);
16196                 CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
16197                 _cimg_mp_return(arg1);
16198               }
16199             }
16200 
16201             // No assignment expressions match -> error
16202             *se = saved_char;
16203             cimg::strellipsize(variable_name,64);
16204             s0 = ss - 4>expr._data?ss - 4:expr._data;
16205             cimg::strellipsize(s0,64);
16206             throw CImgArgumentException("[" cimg_appname "_math_parser] "
16207                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
16208                                         "in expression '%s%s%s'.",
16209                                         pixel_type(),_cimg_mp_calling_function,s_op,
16210                                         arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"",
16211                                         variable_name._data,
16212                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16213           }
16214 
16215         // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
16216         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
16217           if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
16218               level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
16219             _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
16220 
16221             ref.assign(7);
16222             arg1 = compile(ss,ns,depth1,ref,is_single); // Vector slot
16223             arg2 = compile(s + 1,se,depth1,0,is_single); // Right operand
16224             _cimg_mp_check_type(arg1,1,2,2);
16225             _cimg_mp_check_type(arg2,2,3,2);
16226             if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
16227               if (*ps=='*')
16228                 CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
16229               else if (*ps=='/')
16230                 CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
16231               else
16232                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
16233             } else { // Complex **= scalar
16234               if (*ps=='*') {
16235                 if (arg2==1) _cimg_mp_return(arg1);
16236                 self_vector_s(arg1,mp_self_mul,arg2);
16237               } else if (*ps=='/') {
16238                 if (arg2==1) _cimg_mp_return(arg1);
16239                 self_vector_s(arg1,mp_self_div,arg2);
16240               } else {
16241                 if (arg2==1) _cimg_mp_return(arg1);
16242                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
16243               }
16244             }
16245 
16246             // Write computed value back in image if necessary.
16247             if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
16248               if (!is_single) is_parallelizable = false;
16249               p1 = ref[1]; // Index
16250               is_relative = (bool)ref[2];
16251               arg3 = ref[3]; // Offset
16252               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16253               if (p1!=~0U) {
16254                 if (!listout) _cimg_mp_return(arg1);
16255                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16256                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
16257               } else {
16258                 if (!imgout) _cimg_mp_return(arg1);
16259                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16260                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
16261               }
16262 
16263             } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
16264               if (!is_single) is_parallelizable = false;
16265               p1 = ref[1]; // Index
16266               is_relative = (bool)ref[2];
16267               arg3 = ref[3]; // X
16268               arg4 = ref[4]; // Y
16269               arg5 = ref[5]; // Z
16270               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16271               if (p1!=~0U) {
16272                 if (!listout) _cimg_mp_return(arg1);
16273                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16274                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16275               } else {
16276                 if (!imgout) _cimg_mp_return(arg1);
16277                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16278                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16279               }
16280             }
16281 
16282             _cimg_mp_return(arg1);
16283           }
16284 
16285         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
16286           if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
16287                           *ps=='&' || *ps=='^' || *ps=='|' ||
16288                           (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
16289               level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
16290             switch (*ps) {
16291             case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
16292             case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
16293             case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
16294             case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
16295             case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
16296             case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
16297             case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
16298             case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
16299             case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
16300             default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
16301             }
16302             s1 = *ps=='>' || *ps=='<'?ns:ps;
16303 
16304             ref.assign(7);
16305             arg1 = compile(ss,s1,depth1,ref,is_single); // Variable slot
16306             arg2 = compile(s + 1,se,depth1,0,is_single); // Value to apply
16307 
16308             // Check for particular case to be simplified.
16309             if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
16310             if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
16311 
16312             // Apply operator on a copy to prevent modifying a constant or a variable.
16313             if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
16314               if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
16315               else arg1 = scalar1(mp_copy,arg1);
16316             }
16317 
16318             if (*ref==1) { // Vector value (scalar): V[k] += scalar
16319               _cimg_mp_check_type(arg2,2,1,0);
16320               arg3 = ref[1]; // Vector slot
16321               arg4 = ref[2]; // Index
16322               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16323               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16324               CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
16325                 move_to(code);
16326               _cimg_mp_return(arg1);
16327             }
16328 
16329             if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
16330               if (!is_single) is_parallelizable = false;
16331               _cimg_mp_check_type(arg2,2,1,0);
16332               p1 = ref[1]; // Index
16333               is_relative = (bool)ref[2];
16334               arg3 = ref[3]; // Offset
16335               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16336               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16337               if (p1!=~0U) {
16338                 if (!listout) _cimg_mp_return(arg1);
16339                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16340                                     arg1,p1,arg3).move_to(code);
16341               } else {
16342                 if (!imgout) _cimg_mp_return(arg1);
16343                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16344                                     arg1,arg3).move_to(code);
16345               }
16346               _cimg_mp_return(arg1);
16347             }
16348 
16349             if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
16350               if (!is_single) is_parallelizable = false;
16351               _cimg_mp_check_type(arg2,2,1,0);
16352               p1 = ref[1]; // Index
16353               is_relative = (bool)ref[2];
16354               arg3 = ref[3]; // X
16355               arg4 = ref[4]; // Y
16356               arg5 = ref[5]; // Z
16357               arg6 = ref[6]; // C
16358               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16359               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16360               if (p1!=~0U) {
16361                 if (!listout) _cimg_mp_return(arg1);
16362                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
16363                                     arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
16364               } else {
16365                 if (!imgout) _cimg_mp_return(arg1);
16366                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
16367                                     arg1,arg3,arg4,arg5,arg6).move_to(code);
16368               }
16369               _cimg_mp_return(arg1);
16370             }
16371 
16372             if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
16373               if (!is_single) is_parallelizable = false;
16374               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16375               p1 = ref[1]; // Index
16376               is_relative = (bool)ref[2];
16377               arg3 = ref[3]; // Offset
16378               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16379               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
16380               if (p1!=~0U) {
16381                 if (!listout) _cimg_mp_return(arg1);
16382                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16383                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
16384               } else {
16385                 if (!imgout) _cimg_mp_return(arg1);
16386                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16387                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
16388               }
16389               _cimg_mp_return(arg1);
16390             }
16391 
16392             if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
16393               if (!is_single) is_parallelizable = false;
16394               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16395               p1 = ref[1]; // Index
16396               is_relative = (bool)ref[2];
16397               arg3 = ref[3]; // X
16398               arg4 = ref[4]; // Y
16399               arg5 = ref[5]; // Z
16400               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16401               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
16402               if (p1!=~0U) {
16403                 if (!listout) _cimg_mp_return(arg1);
16404                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16405                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16406               } else {
16407                 if (!imgout) _cimg_mp_return(arg1);
16408                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16409                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16410               }
16411               _cimg_mp_return(arg1);
16412             }
16413 
16414             if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
16415               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16416               if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
16417               else self_vector_s(arg1,op,arg2); // Vector += scalar
16418               _cimg_mp_return(arg1);
16419             }
16420 
16421             if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar
16422               _cimg_mp_check_type(arg2,2,1,0);
16423               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16424               _cimg_mp_return(arg1);
16425             }
16426 
16427             variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
16428             cimg::strpare(variable_name,false,true);
16429             *se = saved_char;
16430             s0 = ss - 4>expr._data?ss - 4:expr._data;
16431             cimg::strellipsize(s0,64);
16432             throw CImgArgumentException("[" cimg_appname "_math_parser] "
16433                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
16434                                         "in expression '%s%s%s'.",
16435                                         pixel_type(),_cimg_mp_calling_function,s_op,
16436                                         _cimg_mp_is_constant(arg1)?"const ":"",
16437                                         variable_name._data,
16438                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16439           }
16440 
16441         for (s = ss1; s<se1; ++s)
16442           if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
16443             _cimg_mp_op("Operator '?:'");
16444             s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
16445             arg1 = compile(ss,s,depth1,0,is_single);
16446             _cimg_mp_check_type(arg1,1,1,0);
16447             if (_cimg_mp_is_constant(arg1)) {
16448               if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
16449               else return *s1!=':'?0:compile(++s1,se,depth1,0,is_single);
16450             }
16451             p2 = code._width;
16452             arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
16453             p3 = code._width;
16454             arg3 = *s1==':'?compile(++s1,se,depth1,0,is_single):
16455               _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
16456             _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
16457             arg4 = _cimg_mp_size(arg2);
16458             if (arg4) pos = vector(arg4); else pos = scalar();
16459             CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
16460                                 p3 - p2,code._width - p3,arg4).move_to(code,p2);
16461             _cimg_mp_return(pos);
16462           }
16463 
16464         for (s = se3, ns = se2; s>ss; --s, --ns)
16465           if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
16466             _cimg_mp_op("Operator '||'");
16467             arg1 = compile(ss,s,depth1,0,is_single);
16468             _cimg_mp_check_type(arg1,1,1,0);
16469             if (arg1>0 && arg1<=16) _cimg_mp_return(1);
16470             p2 = code._width;
16471             arg2 = compile(s + 2,se,depth1,0,is_single);
16472             _cimg_mp_check_type(arg2,2,1,0);
16473             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16474               _cimg_mp_constant(mem[arg1] || mem[arg2]);
16475             if (!arg1) _cimg_mp_return(arg2);
16476             pos = scalar();
16477             CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
16478               move_to(code,p2);
16479             _cimg_mp_return(pos);
16480           }
16481 
16482         for (s = se3, ns = se2; s>ss; --s, --ns)
16483           if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
16484             _cimg_mp_op("Operator '&&'");
16485             arg1 = compile(ss,s,depth1,0,is_single);
16486             _cimg_mp_check_type(arg1,1,1,0);
16487             if (!arg1) _cimg_mp_return(0);
16488             p2 = code._width;
16489             arg2 = compile(s + 2,se,depth1,0,is_single);
16490             _cimg_mp_check_type(arg2,2,1,0);
16491             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16492               _cimg_mp_constant(mem[arg1] && mem[arg2]);
16493             if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
16494             pos = scalar();
16495             CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
16496               move_to(code,p2);
16497             _cimg_mp_return(pos);
16498           }
16499 
16500         for (s = se2; s>ss; --s)
16501           if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
16502             _cimg_mp_op("Operator '|'");
16503             arg1 = compile(ss,s,depth1,0,is_single);
16504             arg2 = compile(s + 1,se,depth1,0,is_single);
16505             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16506             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
16507             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16508               if (!arg2) _cimg_mp_return(arg1);
16509               _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
16510             }
16511             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16512               if (!arg1) _cimg_mp_return(arg2);
16513               _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
16514             }
16515             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16516               _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]);
16517             if (!arg2) _cimg_mp_return(arg1);
16518             if (!arg1) _cimg_mp_return(arg2);
16519             _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
16520           }
16521 
16522         for (s = se2; s>ss; --s)
16523           if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
16524             _cimg_mp_op("Operator '&'");
16525             arg1 = compile(ss,s,depth1,0,is_single);
16526             arg2 = compile(s + 1,se,depth1,0,is_single);
16527             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16528             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
16529             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
16530             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
16531             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16532               _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]);
16533             if (!arg1 || !arg2) _cimg_mp_return(0);
16534             _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
16535           }
16536 
16537         for (s = se3, ns = se2; s>ss; --s, --ns)
16538           if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
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             if (arg1==arg2) _cimg_mp_return(0);
16543             p1 = _cimg_mp_size(arg1);
16544             p2 = _cimg_mp_size(arg2);
16545             if (p1 || p2) {
16546               if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
16547               _cimg_mp_scalar6(mp_vector_neq,arg1,p1,arg2,p2,11,1);
16548             }
16549             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
16550             _cimg_mp_scalar2(mp_neq,arg1,arg2);
16551           }
16552 
16553         for (s = se3, ns = se2; s>ss; --s, --ns)
16554           if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
16555             _cimg_mp_op("Operator '=='");
16556             arg1 = compile(ss,s,depth1,0,is_single);
16557             arg2 = compile(s + 2,se,depth1,0,is_single);
16558             if (arg1==arg2) _cimg_mp_return(1);
16559             p1 = _cimg_mp_size(arg1);
16560             p2 = _cimg_mp_size(arg2);
16561             if (p1 || p2) {
16562               if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
16563               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,11,1);
16564             }
16565             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]);
16566             _cimg_mp_scalar2(mp_eq,arg1,arg2);
16567           }
16568 
16569         for (s = se3, ns = se2; s>ss; --s, --ns)
16570           if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
16571             _cimg_mp_op("Operator '<='");
16572             arg1 = compile(ss,s,depth1,0,is_single);
16573             arg2 = compile(s + 2,se,depth1,0,is_single);
16574             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16575             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
16576             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
16577             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
16578             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
16579             if (arg1==arg2) _cimg_mp_return(1);
16580             _cimg_mp_scalar2(mp_lte,arg1,arg2);
16581           }
16582 
16583         for (s = se3, ns = se2; s>ss; --s, --ns)
16584           if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
16585             _cimg_mp_op("Operator '>='");
16586             arg1 = compile(ss,s,depth1,0,is_single);
16587             arg2 = compile(s + 2,se,depth1,0,is_single);
16588             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16589             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
16590             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
16591             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
16592             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
16593             if (arg1==arg2) _cimg_mp_return(1);
16594             _cimg_mp_scalar2(mp_gte,arg1,arg2);
16595           }
16596 
16597         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
16598           if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
16599             _cimg_mp_op("Operator '<'");
16600             arg1 = compile(ss,s,depth1,0,is_single);
16601             arg2 = compile(s + 1,se,depth1,0,is_single);
16602             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16603             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
16604             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
16605             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
16606             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]);
16607             if (arg1==arg2) _cimg_mp_return(0);
16608             _cimg_mp_scalar2(mp_lt,arg1,arg2);
16609           }
16610 
16611         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
16612           if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>')
16613             _cimg_mp_op("Operator '>'");
16614             arg1 = compile(ss,s,depth1,0,is_single);
16615             arg2 = compile(s + 1,se,depth1,0,is_single);
16616             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16617             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
16618             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
16619             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
16620             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]);
16621             if (arg1==arg2) _cimg_mp_return(0);
16622             _cimg_mp_scalar2(mp_gt,arg1,arg2);
16623           }
16624 
16625         for (s = se3, ns = se2; s>ss; --s, --ns)
16626           if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
16627             _cimg_mp_op("Operator '<<'");
16628             arg1 = compile(ss,s,depth1,0,is_single);
16629             arg2 = compile(s + 2,se,depth1,0,is_single);
16630             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16631             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
16632               _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
16633             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16634               if (!arg2) _cimg_mp_return(arg1);
16635               _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
16636             }
16637             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
16638               _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
16639             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16640               _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]);
16641             if (!arg1) _cimg_mp_return(0);
16642             if (!arg2) _cimg_mp_return(arg1);
16643             _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
16644           }
16645 
16646         for (s = se3, ns = se2; s>ss; --s, --ns)
16647           if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
16648             _cimg_mp_op("Operator '>>'");
16649             arg1 = compile(ss,s,depth1,0,is_single);
16650             arg2 = compile(s + 2,se,depth1,0,is_single);
16651             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16652             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
16653               _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
16654             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16655               if (!arg2) _cimg_mp_return(arg1);
16656               _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
16657             }
16658             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
16659               _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
16660             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16661               _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]);
16662             if (!arg1) _cimg_mp_return(0);
16663             if (!arg2) _cimg_mp_return(arg1);
16664             _cimg_mp_scalar2(mp_bitwise_right_shift,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) { // Addition ('+')
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 (!arg1) _cimg_mp_return(arg2);
16679             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
16680             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
16681             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
16682             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]);
16683             if (code) { // Try to spot linear case 'a*b + c'.
16684               CImg<ulongT> &pop = code.back();
16685               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16686                 arg3 = (unsigned int)pop[1];
16687                 arg4 = (unsigned int)pop[2];
16688                 arg5 = (unsigned int)pop[3];
16689                 code.remove();
16690                 CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
16691                 _cimg_mp_return(arg3);
16692               }
16693             }
16694             if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
16695             if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
16696             _cimg_mp_scalar2(mp_add,arg1,arg2);
16697           }
16698 
16699         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
16700           if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
16701               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
16702               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
16703                                                                                      *(ps - 1)<='9')))) &&
16704               level[s - expr._data]==clevel) { // Subtraction ('-')
16705             _cimg_mp_op("Operator '-'");
16706             arg1 = compile(ss,s,depth1,0,is_single);
16707             arg2 = compile(s + 1,se,depth1,0,is_single);
16708             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16709             if (!arg2) _cimg_mp_return(arg1);
16710             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
16711             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
16712             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16713               if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
16714               _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
16715             }
16716             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]);
16717             if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
16718             if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'.
16719               CImg<ulongT> &pop = code.back();
16720               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16721                 arg3 = (unsigned int)pop[1];
16722                 arg4 = (unsigned int)pop[2];
16723                 arg5 = (unsigned int)pop[3];
16724                 code.remove();
16725                 CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
16726                                      arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
16727                 _cimg_mp_return(arg3);
16728               }
16729             }
16730             if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
16731             _cimg_mp_scalar2(mp_sub,arg1,arg2);
16732           }
16733 
16734         for (s = se3, ns = se2; s>ss; --s, --ns)
16735           if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
16736             _cimg_mp_op("Operator '**'");
16737             arg1 = compile(ss,s,depth1,0,is_single);
16738             arg2 = compile(s + 2,se,depth1,0,is_single);
16739             _cimg_mp_check_type(arg1,1,3,2);
16740             _cimg_mp_check_type(arg2,2,3,2);
16741             if (arg2==1) _cimg_mp_return(arg1);
16742             if (arg1==1) _cimg_mp_return(arg2);
16743             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16744               pos = vector(2);
16745               CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
16746               _cimg_mp_return(pos);
16747             }
16748             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
16749             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
16750             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
16751             if (!arg1 || !arg2) _cimg_mp_return(0);
16752             _cimg_mp_scalar2(mp_mul,arg1,arg2);
16753           }
16754 
16755         for (s = se3, ns = se2; s>ss; --s, --ns)
16756           if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
16757             _cimg_mp_op("Operator '//'");
16758             arg1 = compile(ss,s,depth1,0,is_single);
16759             arg2 = compile(s + 2,se,depth1,0,is_single);
16760             _cimg_mp_check_type(arg1,1,3,2);
16761             _cimg_mp_check_type(arg2,2,3,2);
16762             if (arg2==1) _cimg_mp_return(arg1);
16763             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16764               pos = vector(2);
16765               CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
16766               _cimg_mp_return(pos);
16767             }
16768             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16769             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16770               pos = vector(2);
16771               CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
16772               _cimg_mp_return(pos);
16773             }
16774             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
16775             if (!arg1) _cimg_mp_return(0);
16776             _cimg_mp_scalar2(mp_div,arg1,arg2);
16777           }
16778 
16779         for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
16780             _cimg_mp_op("Operator '*'");
16781             arg1 = compile(ss,s,depth1,0,is_single);
16782             arg2 = compile(s + 1,se,depth1,0,is_single);
16783             p2 = _cimg_mp_size(arg2);
16784             if (p2>0 && _cimg_mp_size(arg1)==p2*p2) { // Particular case of matrix multiplication
16785               pos = vector(p2);
16786               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
16787               _cimg_mp_return(pos);
16788             }
16789             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16790             if (arg2==1) _cimg_mp_return(arg1);
16791             if (arg1==1) _cimg_mp_return(arg2);
16792             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
16793             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
16794             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
16795             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
16796 
16797             if (code) { // Try to spot double multiplication 'a*b*c'.
16798               CImg<ulongT> &pop = code.back();
16799               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16800                 arg3 = (unsigned int)pop[1];
16801                 arg4 = (unsigned int)pop[2];
16802                 arg5 = (unsigned int)pop[3];
16803                 code.remove();
16804                 CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
16805                 _cimg_mp_return(arg3);
16806               }
16807             }
16808             if (!arg1 || !arg2) _cimg_mp_return(0);
16809             _cimg_mp_scalar2(mp_mul,arg1,arg2);
16810           }
16811 
16812         for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
16813             _cimg_mp_op("Operator '/'");
16814             arg1 = compile(ss,s,depth1,0,is_single);
16815             arg2 = compile(s + 1,se,depth1,0,is_single);
16816             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16817             if (arg2==1) _cimg_mp_return(arg1);
16818             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
16819             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16820             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
16821             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
16822             if (!arg1) _cimg_mp_return(0);
16823             _cimg_mp_scalar2(mp_div,arg1,arg2);
16824           }
16825 
16826         for (s = se2, ns = se1; s>ss; --s, --ns)
16827           if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
16828             _cimg_mp_op("Operator '%'");
16829             arg1 = compile(ss,s,depth1,0,is_single);
16830             arg2 = compile(s + 1,se,depth1,0,is_single);
16831             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16832             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
16833             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
16834             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
16835             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16836               _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
16837             _cimg_mp_scalar2(mp_modulo,arg1,arg2);
16838           }
16839 
16840         if (se1>ss) {
16841           if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
16842             _cimg_mp_op("Operator '+'");
16843             _cimg_mp_return(compile(ss1,se,depth1,0,is_single));
16844           }
16845 
16846           if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
16847             _cimg_mp_op("Operator '-'");
16848             arg1 = compile(ss1,se,depth1,0,is_single);
16849             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
16850             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]);
16851             _cimg_mp_scalar1(mp_minus,arg1);
16852           }
16853 
16854           if (*ss=='!') { // Logical not ('!')
16855             _cimg_mp_op("Operator '!'");
16856             if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
16857               arg1 = compile(ss2,se,depth1,0,is_single);
16858               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
16859               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
16860               _cimg_mp_scalar1(mp_bool,arg1);
16861             }
16862             arg1 = compile(ss1,se,depth1,0,is_single);
16863             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
16864             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]);
16865             _cimg_mp_scalar1(mp_logical_not,arg1);
16866           }
16867 
16868           if (*ss=='~') { // Bitwise not ('~')
16869             _cimg_mp_op("Operator '~'");
16870             arg1 = compile(ss1,se,depth1,0,is_single);
16871             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
16872             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]);
16873             _cimg_mp_scalar1(mp_bitwise_not,arg1);
16874           }
16875         }
16876 
16877         for (s = se3, ns = se2; s>ss; --s, --ns)
16878           if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
16879             _cimg_mp_op("Operator '^^'");
16880             arg1 = compile(ss,s,depth1,0,is_single);
16881             arg2 = compile(s + 2,se,depth1,0,is_single);
16882             _cimg_mp_check_type(arg1,1,3,2);
16883             _cimg_mp_check_type(arg2,2,3,2);
16884             if (arg2==1) _cimg_mp_return(arg1);
16885             pos = vector(2);
16886             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16887               CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
16888               _cimg_mp_return(pos);
16889             }
16890             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16891               CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
16892               _cimg_mp_return(pos);
16893             }
16894             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16895               CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
16896               _cimg_mp_return(pos);
16897             }
16898             CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
16899             _cimg_mp_return(pos);
16900           }
16901 
16902         for (s = se2; s>ss; --s)
16903           if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
16904             _cimg_mp_op("Operator '^'");
16905             arg1 = compile(ss,s,depth1,0,is_single);
16906             arg2 = compile(s + 1,se,depth1,0,is_single);
16907             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16908             if (arg2==1) _cimg_mp_return(arg1);
16909             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
16910             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
16911             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
16912             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16913               _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
16914             switch (arg2) {
16915             case 0 : _cimg_mp_return(1);
16916             case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
16917             case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
16918             case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
16919             default :
16920               if (_cimg_mp_is_constant(arg2)) {
16921                 if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
16922                 else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
16923               }
16924               _cimg_mp_scalar2(mp_pow,arg1,arg2);
16925             }
16926           }
16927 
16928         // Percentage computation.
16929         if (*se1=='%') {
16930           arg1 = compile(ss,se1,depth1,0,is_single);
16931           arg2 = _cimg_mp_is_constant(arg1)?0:constant(100);
16932           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16933           if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100);
16934           _cimg_mp_scalar2(mp_div,arg1,arg2);
16935         }
16936 
16937         is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
16938         if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment
16939           if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
16940             _cimg_mp_op("Operator '++'");
16941             op = mp_self_increment;
16942           } else {
16943             _cimg_mp_op("Operator '--'");
16944             op = mp_self_decrement;
16945           }
16946           ref.assign(7);
16947           arg1 = is_sth?compile(ss2,se,depth1,ref,is_single):
16948             compile(ss,se2,depth1,ref,is_single); // Variable slot
16949 
16950           // Apply operator on a copy to prevent modifying a constant or a variable.
16951           if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
16952             if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
16953             else arg1 = scalar1(mp_copy,arg1);
16954           }
16955 
16956           if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action
16957           else {
16958             if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
16959             else pos = scalar1(mp_copy,arg1);
16960           }
16961 
16962           if (*ref==1) { // Vector value (scalar): V[k]++
16963             arg3 = ref[1]; // Vector slot
16964             arg4 = ref[2]; // Index
16965             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16966             CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
16967             CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
16968               move_to(code);
16969             _cimg_mp_return(pos);
16970           }
16971 
16972           if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
16973             if (!is_single) is_parallelizable = false;
16974             p1 = ref[1]; // Index
16975             is_relative = (bool)ref[2];
16976             arg3 = ref[3]; // Offset
16977             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16978             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
16979             if (p1!=~0U) {
16980               if (!listout) _cimg_mp_return(pos);
16981               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16982                                   arg1,p1,arg3).move_to(code);
16983             } else {
16984               if (!imgout) _cimg_mp_return(pos);
16985               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16986                                   arg1,arg3).move_to(code);
16987             }
16988             _cimg_mp_return(pos);
16989           }
16990 
16991           if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
16992             if (!is_single) is_parallelizable = false;
16993             p1 = ref[1]; // Index
16994             is_relative = (bool)ref[2];
16995             arg3 = ref[3]; // X
16996             arg4 = ref[4]; // Y
16997             arg5 = ref[5]; // Z
16998             arg6 = ref[6]; // C
16999             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
17000             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
17001             if (p1!=~0U) {
17002               if (!listout) _cimg_mp_return(pos);
17003               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17004                                   arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
17005             } else {
17006               if (!imgout) _cimg_mp_return(pos);
17007               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17008                                   arg1,arg3,arg4,arg5,arg6).move_to(code);
17009             }
17010             _cimg_mp_return(pos);
17011           }
17012 
17013           if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
17014             if (!is_single) is_parallelizable = false;
17015             p1 = ref[1]; // Index
17016             is_relative = (bool)ref[2];
17017             arg3 = ref[3]; // Offset
17018             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
17019             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
17020             if (p1!=~0U) {
17021               if (!listout) _cimg_mp_return(pos);
17022               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17023                                   arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17024             } else {
17025               if (!imgout) _cimg_mp_return(pos);
17026               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17027                                   arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17028             }
17029             _cimg_mp_return(pos);
17030           }
17031 
17032           if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
17033             if (!is_single) is_parallelizable = false;
17034             p1 = ref[1]; // Index
17035             is_relative = (bool)ref[2];
17036             arg3 = ref[3]; // X
17037             arg4 = ref[4]; // Y
17038             arg5 = ref[5]; // Z
17039             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
17040             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
17041             if (p1!=~0U) {
17042               if (!listout) _cimg_mp_return(pos);
17043               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17044                                   arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17045             } else {
17046               if (!imgout) _cimg_mp_return(pos);
17047               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17048                                   arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17049             }
17050             _cimg_mp_return(pos);
17051           }
17052 
17053           if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
17054             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
17055             _cimg_mp_return(pos);
17056           }
17057 
17058           if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++
17059             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
17060             _cimg_mp_return(pos);
17061           }
17062 
17063           if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
17064           else variable_name.assign(ss,(unsigned int)(se1 - ss));
17065           variable_name.back() = 0;
17066           cimg::strpare(variable_name,false,true);
17067           *se = saved_char;
17068           cimg::strellipsize(variable_name,64);
17069           s0 = ss - 4>expr._data?ss - 4:expr._data;
17070           cimg::strellipsize(s0,64);
17071           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17072                                       "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17073                                       "in expression '%s%s%s'.",
17074                                       pixel_type(),_cimg_mp_calling_function,s_op,
17075                                       _cimg_mp_is_constant(arg1)?"const ":"",
17076                                       variable_name._data,
17077                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17078         }
17079 
17080         // Array-like access to vectors and  image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
17081         if (*se1==']' && *ss!='[') {
17082           _cimg_mp_op("Value accessor '[]'");
17083           is_relative = *ss=='j' || *ss=='J';
17084           s0 = s1 = std::strchr(ss,'['); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
17085 
17086           if ((*ss=='I' || *ss=='J') && *ss1=='[' &&
17087               (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector
17088             if (*ss2=='#') { // Index specified
17089               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17090               p1 = compile(ss3,s0++,depth1,0,is_single);
17091               _cimg_mp_check_list(false);
17092             } else { p1 = ~0U; s0 = ss2; }
17093             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17094             p2 = 1 + (p1!=~0U);
17095             arg1 = compile(s0,s1,depth1,0,is_single); // Offset
17096             _cimg_mp_check_type(arg1,p2,1,0);
17097             arg2 = ~0U;
17098             if (s1<se1) {
17099               arg2 = compile(++s1,se1,depth1,0,is_single); // Boundary
17100               _cimg_mp_check_type(arg2,p2 + 1,1,0);
17101             }
17102             if (p_ref && arg2==~0U) {
17103               *p_ref = 4;
17104               p_ref[1] = p1;
17105               p_ref[2] = (unsigned int)is_relative;
17106               p_ref[3] = arg1;
17107               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17108               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17109             }
17110             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
17111             if (p1==~0U) p2 = imgin._spectrum;
17112             else if (_cimg_mp_is_constant(p1)) {
17113               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17114               p2 = listin[p3]._spectrum;
17115             }
17116             _cimg_mp_check_vector0(p2);
17117             pos = vector(p2);
17118             if (p1!=~0U) {
17119               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
17120                                   pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
17121             } else {
17122               need_input_copy = true;
17123               CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
17124                                   pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
17125             }
17126             _cimg_mp_return(pos);
17127           }
17128 
17129           if ((*ss=='i' || *ss=='j') && *ss1=='[' &&
17130               (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar
17131             if (*ss2=='#') { // Index specified
17132               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17133               p1 = compile(ss3,s0++,depth1,0,is_single);
17134             } else { p1 = ~0U; s0 = ss2; }
17135             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17136             arg1 = compile(s0,s1,depth1,0,is_single); // Offset
17137             arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):~0U; // Boundary
17138             if (p_ref && arg2==~0U) {
17139               *p_ref = 2;
17140               p_ref[1] = p1;
17141               p_ref[2] = (unsigned int)is_relative;
17142               p_ref[3] = arg1;
17143               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17144               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17145             }
17146             if (p1!=~0U) {
17147               if (!listin) _cimg_mp_return(0);
17148               pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
17149             } else {
17150               if (!imgin) _cimg_mp_return(0);
17151               need_input_copy = true;
17152               pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
17153             }
17154             memtype[pos] = -2; // Prevent from being used in further optimization
17155             _cimg_mp_return(pos);
17156           }
17157 
17158           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
17159           if (s0>ss) { // Vector value
17160             arg1 = compile(ss,s0,depth1,0,is_single);
17161             if (_cimg_mp_is_scalar(arg1)) {
17162               variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
17163               *se = saved_char;
17164               cimg::strellipsize(variable_name,64);
17165               s0 = ss - 4>expr._data?ss - 4:expr._data;
17166               cimg::strellipsize(s0,64);
17167               throw CImgArgumentException("[" cimg_appname "_math_parser] "
17168                                           "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
17169                                           "in expression '%s%s%s'.",
17170                                           pixel_type(),_cimg_mp_calling_function,s_op,
17171                                           variable_name._data,
17172                                           s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17173 
17174             }
17175             s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17176 
17177             if (s1<se1) { // Two arguments -> sub-vector extraction
17178               p1 = _cimg_mp_size(arg1);
17179               arg2 = compile(++s0,s1,depth1,0,is_single); // Starting indice
17180               arg3 = compile(++s1,se1,depth1,0,is_single); // Length
17181               _cimg_mp_check_constant(arg3,2,3);
17182               arg3 = (unsigned int)mem[arg3];
17183               pos = vector(arg3);
17184               CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3).move_to(code);
17185               _cimg_mp_return(pos);
17186             }
17187 
17188             // One argument -> vector value reference
17189             arg2 = compile(++s0,se1,depth1,0,is_single);
17190             if (_cimg_mp_is_constant(arg2)) { // Constant index
17191               nb = (int)mem[arg2];
17192               if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
17193               variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
17194               *se = saved_char;
17195               cimg::strellipsize(variable_name,64);
17196               s0 = ss - 4>expr._data?ss - 4:expr._data;
17197               cimg::strellipsize(s0,64);
17198               throw CImgArgumentException("[" cimg_appname "_math_parser] "
17199                                           "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
17200                                           "(vector '%s' has dimension %u), "
17201                                           "in expression '%s%s%s'.",
17202                                           pixel_type(),_cimg_mp_calling_function,
17203                                           variable_name._data,nb,
17204                                           variable_name._data,_cimg_mp_size(arg1),
17205                                           s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17206             }
17207             if (p_ref) {
17208               *p_ref = 1;
17209               p_ref[1] = arg1;
17210               p_ref[2] = arg2;
17211               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization
17212             }
17213             pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
17214             memtype[pos] = -2; // Prevent from being used in further optimization
17215             _cimg_mp_return(pos);
17216           }
17217         }
17218 
17219         // Look for a function call, an access to image value, or a parenthesis.
17220         if (*se1==')') {
17221           if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_single)); // Simple parentheses
17222           _cimg_mp_op("Value accessor '()'");
17223           is_relative = *ss=='j' || *ss=='J';
17224           s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
17225 
17226           // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
17227           if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
17228             if (*ss2=='#') { // Index specified
17229               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17230               p1 = compile(ss3,s0++,depth1,0,is_single);
17231               _cimg_mp_check_list(false);
17232             } else { p1 = ~0U; s0 = ss2; }
17233             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17234             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17235             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17236             arg4 = arg5 = ~0U;
17237             if (s0<se1) {
17238               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17239               arg1 = compile(s0,s1,depth1,0,is_single);
17240               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17241                 p2 = _cimg_mp_size(arg1);
17242                 ++arg1;
17243                 if (p2>1) {
17244                   arg2 = arg1 + 1;
17245                   if (p2>2) arg3 = arg2 + 1;
17246                 }
17247                 if (s1<se1) {
17248                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17249                   arg4 = compile(s1,s2,depth1,0,is_single);
17250                   arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17251                 }
17252               } else if (s1<se1) {
17253                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17254                 arg2 = compile(s1,s2,depth1,0,is_single);
17255                 if (s2<se1) {
17256                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17257                   arg3 = compile(s2,s3,depth1,0,is_single);
17258                   if (s3<se1) {
17259                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17260                     arg4 = compile(s3,s2,depth1,0,is_single);
17261                     arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17262                   }
17263                 }
17264               }
17265             }
17266             if (p_ref && arg4==~0U && arg5==~0U) {
17267               *p_ref = 5;
17268               p_ref[1] = p1;
17269               p_ref[2] = (unsigned int)is_relative;
17270               p_ref[3] = arg1;
17271               p_ref[4] = arg2;
17272               p_ref[5] = arg3;
17273               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17274               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17275               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17276               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
17277             }
17278             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
17279             if (p1==~0U) p2 = imgin._spectrum;
17280             else if (_cimg_mp_is_constant(p1)) {
17281               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17282               p2 = listin[p3]._spectrum;
17283             }
17284             _cimg_mp_check_vector0(p2);
17285             pos = vector(p2);
17286             if (p1!=~0U)
17287               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
17288                                    pos,p1,arg1,arg2,arg3,
17289                                    arg4==~0U?_cimg_mp_interpolation:arg4,
17290                                    arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
17291             else {
17292               need_input_copy = true;
17293               CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
17294                                   pos,arg1,arg2,arg3,
17295                                   arg4==~0U?_cimg_mp_interpolation:arg4,
17296                                   arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
17297             }
17298             _cimg_mp_return(pos);
17299           }
17300 
17301           // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
17302           if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
17303             if (*ss2=='#') { // Index specified
17304               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17305               p1 = compile(ss3,s0++,depth1,0,is_single);
17306             } else { p1 = ~0U; s0 = ss2; }
17307             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17308             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17309             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17310             arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17311             arg5 = arg6 = ~0U;
17312             if (s0<se1) {
17313               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17314               arg1 = compile(s0,s1,depth1,0,is_single);
17315               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17316                 p2 = _cimg_mp_size(arg1);
17317                 ++arg1;
17318                 if (p2>1) {
17319                   arg2 = arg1 + 1;
17320                   if (p2>2) {
17321                     arg3 = arg2 + 1;
17322                     if (p2>3) arg4 = arg3 + 1;
17323                   }
17324                 }
17325                 if (s1<se1) {
17326                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17327                   arg5 = compile(s1,s2,depth1,0,is_single);
17328                   arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17329                 }
17330               } else if (s1<se1) {
17331                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17332                 arg2 = compile(s1,s2,depth1,0,is_single);
17333                 if (s2<se1) {
17334                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17335                   arg3 = compile(s2,s3,depth1,0,is_single);
17336                   if (s3<se1) {
17337                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17338                     arg4 = compile(s3,s2,depth1,0,is_single);
17339                     if (s2<se1) {
17340                       s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17341                       arg5 = compile(s2,s3,depth1,0,is_single);
17342                       arg6 = s3<se1?compile(++s3,se1,depth1,0,is_single):~0U;
17343                     }
17344                   }
17345                 }
17346               }
17347             }
17348             if (p_ref && arg5==~0U && arg6==~0U) {
17349               *p_ref = 3;
17350               p_ref[1] = p1;
17351               p_ref[2] = (unsigned int)is_relative;
17352               p_ref[3] = arg1;
17353               p_ref[4] = arg2;
17354               p_ref[5] = arg3;
17355               p_ref[6] = arg4;
17356               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17357               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17358               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17359               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
17360               if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
17361             }
17362 
17363             if (p1!=~0U) {
17364               if (!listin) _cimg_mp_return(0);
17365               pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
17366                             p1,arg1,arg2,arg3,arg4,
17367                             arg5==~0U?_cimg_mp_interpolation:arg5,
17368                             arg6==~0U?_cimg_mp_boundary:arg6);
17369             } else {
17370               if (!imgin) _cimg_mp_return(0);
17371               need_input_copy = true;
17372               pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
17373                             arg1,arg2,arg3,arg4,
17374                             arg5==~0U?_cimg_mp_interpolation:arg5,
17375                             arg6==~0U?_cimg_mp_boundary:arg6);
17376             }
17377             memtype[pos] = -2; // Prevent from being used in further optimization
17378             _cimg_mp_return(pos);
17379           }
17380 
17381           // Mathematical functions.
17382           switch (*ss) {
17383 
17384           case '_' :
17385             if (*ss1=='(') // Skip arguments
17386               _cimg_mp_return_nan();
17387             break;
17388 
17389           case 'a' :
17390             if (!std::strncmp(ss,"abs(",4)) { // Absolute value
17391               _cimg_mp_op("Function 'abs()'");
17392               arg1 = compile(ss4,se1,depth1,0,is_single);
17393               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
17394               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1]));
17395               _cimg_mp_scalar1(mp_abs,arg1);
17396             }
17397 
17398             if (!std::strncmp(ss,"acos(",5)) { // Arccos
17399               _cimg_mp_op("Function 'acos()'");
17400               arg1 = compile(ss5,se1,depth1,0,is_single);
17401               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
17402               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1]));
17403               _cimg_mp_scalar1(mp_acos,arg1);
17404             }
17405 
17406             if (!std::strncmp(ss,"arg(",4)) { // Nth argument
17407               _cimg_mp_op("Function 'arg()'");
17408               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17409               arg1 = compile(ss4,s1,depth1,0,is_single);
17410               _cimg_mp_check_type(arg1,1,1,0);
17411               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17412               arg2 = compile(s1,s2,depth1,0,is_single);
17413               p2 = _cimg_mp_size(arg2);
17414               p3 = 3;
17415               CImg<ulongT>::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(_opcode);
17416               for (s = ++s2; s<se; ++s) {
17417                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17418                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17419                 arg3 = compile(s,ns,depth1,0,is_single);
17420                 _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
17421                 CImg<ulongT>::vector(arg3).move_to(_opcode);
17422                 ++p3;
17423                 s = ns;
17424               }
17425               (_opcode>'y').move_to(opcode);
17426               opcode[2] = opcode._height;
17427               if (_cimg_mp_is_constant(arg1)) {
17428                 p3-=1; // Number of args
17429                 arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
17430                 if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
17431                 if (p2) {
17432                   pos = vector(p2);
17433                   std::memset(&mem[pos] + 1,0,p2*sizeof(double));
17434                   _cimg_mp_return(pos);
17435                 } else _cimg_mp_return(0);
17436               }
17437               pos = opcode[1] = p2?vector(p2):scalar();
17438               opcode.move_to(code);
17439               _cimg_mp_return(pos);
17440             }
17441 
17442             if (!std::strncmp(ss,"asin(",5)) { // Arcsin
17443               _cimg_mp_op("Function 'asin()'");
17444               arg1 = compile(ss5,se1,depth1,0,is_single);
17445               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
17446               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1]));
17447               _cimg_mp_scalar1(mp_asin,arg1);
17448             }
17449 
17450             if (!std::strncmp(ss,"atan(",5)) { // Arctan
17451               _cimg_mp_op("Function 'atan()'");
17452               arg1 = compile(ss5,se1,depth1,0,is_single);
17453               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
17454               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1]));
17455               _cimg_mp_scalar1(mp_atan,arg1);
17456             }
17457 
17458             if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
17459               _cimg_mp_op("Function 'atan2()'");
17460               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17461               arg1 = compile(ss6,s1,depth1,0,is_single);
17462               arg2 = compile(++s1,se1,depth1,0,is_single);
17463               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17464               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
17465               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
17466               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
17467               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
17468                 _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
17469               _cimg_mp_scalar2(mp_atan2,arg1,arg2);
17470             }
17471             break;
17472 
17473           case 'b' :
17474             if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
17475               _cimg_mp_op("Function 'bool()'");
17476               arg1 = compile(ss5,se1,depth1,0,is_single);
17477               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
17478               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
17479               _cimg_mp_scalar1(mp_bool,arg1);
17480             }
17481 
17482             if (!std::strncmp(ss,"break(",6)) { // Complex absolute value
17483               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17484                 CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
17485                 _cimg_mp_return_nan();
17486               }
17487             }
17488 
17489             if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
17490               _cimg_mp_op("Function 'breakpoint()'");
17491               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17492                 CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
17493                 _cimg_mp_return_nan();
17494               }
17495             }
17496             break;
17497 
17498           case 'c' :
17499             if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
17500               _cimg_mp_op("Function 'cabs()'");
17501               arg1 = compile(ss5,se1,depth1,0,is_single);
17502               _cimg_mp_check_type(arg1,0,2,2);
17503               _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
17504             }
17505 
17506             if (!std::strncmp(ss,"carg(",5)) { // Complex argument
17507               _cimg_mp_op("Function 'carg()'");
17508               arg1 = compile(ss5,se1,depth1,0,is_single);
17509               _cimg_mp_check_type(arg1,0,2,2);
17510               _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
17511             }
17512 
17513             if (!std::strncmp(ss,"cats(",5)) { // Concatenate strings
17514               _cimg_mp_op("Function 'cats()'");
17515               CImg<ulongT>::vector((ulongT)mp_cats,0,0,0).move_to(_opcode);
17516               arg1 = 0;
17517               for (s = ss5; s<se; ++s) {
17518                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17519                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17520                 arg1 = compile(s,ns,depth1,0,is_single);
17521                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(_opcode);
17522                 s = ns;
17523               }
17524               _cimg_mp_check_constant(arg1,1,3); // Last argument = output vector size
17525               _opcode.remove();
17526               (_opcode>'y').move_to(opcode);
17527               p1 = (unsigned int)mem[arg1];
17528               pos = vector(p1);
17529               opcode[1] = pos;
17530               opcode[2] = p1;
17531               opcode[3] = opcode._height;
17532               opcode.move_to(code);
17533               _cimg_mp_return(pos);
17534             }
17535 
17536             if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
17537               _cimg_mp_op("Function 'cbrt()'");
17538               arg1 = compile(ss5,se1,depth1,0,is_single);
17539               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
17540               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1]));
17541               _cimg_mp_scalar1(mp_cbrt,arg1);
17542             }
17543 
17544             if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
17545               _cimg_mp_op("Function 'cconj()'");
17546               arg1 = compile(ss6,se1,depth1,0,is_single);
17547               _cimg_mp_check_type(arg1,0,2,2);
17548               pos = vector(2);
17549               CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code);
17550               _cimg_mp_return(pos);
17551             }
17552 
17553             if (!std::strncmp(ss,"ceil(",5)) { // Ceil
17554               _cimg_mp_op("Function 'ceil()'");
17555               arg1 = compile(ss5,se1,depth1,0,is_single);
17556               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
17557               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1]));
17558               _cimg_mp_scalar1(mp_ceil,arg1);
17559             }
17560 
17561             if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
17562               _cimg_mp_op("Function 'cexp()'");
17563               arg1 = compile(ss5,se1,depth1,0,is_single);
17564               _cimg_mp_check_type(arg1,0,2,2);
17565               pos = vector(2);
17566               CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1).move_to(code);
17567               _cimg_mp_return(pos);
17568             }
17569 
17570             if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
17571               _cimg_mp_op("Function 'clog()'");
17572               arg1 = compile(ss5,se1,depth1,0,is_single);
17573               _cimg_mp_check_type(arg1,0,2,2);
17574               pos = vector(2);
17575               CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1).move_to(code);
17576               _cimg_mp_return(pos);
17577             }
17578 
17579             if (!std::strncmp(ss,"continue(",9)) { // Complex absolute value
17580               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17581                 CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
17582                 _cimg_mp_return_nan();
17583               }
17584             }
17585 
17586             if (!std::strncmp(ss,"copy(",5)) { // Memory copy
17587               _cimg_mp_op("Function 'copy()'");
17588               ref.assign(14);
17589               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17590               arg1 = p1 = compile(ss5,s1,depth1,ref,is_single);
17591               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17592               arg2 = compile(s1,s2,depth1,ref._data + 7,is_single);
17593               arg3 = ~0U; arg4 = arg5 = arg6 = 1;
17594               if (s2<se1) {
17595                 s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17596                 arg3 = compile(s2,s3,depth1,0,is_single);
17597                 if (s3<se1) {
17598                   s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17599                   arg4 = compile(s3,s1,depth1,0,is_single);
17600                   if (s1<se1) {
17601                     s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17602                     arg5 = compile(s1,s2,depth1,0,is_single);
17603                     arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
17604                   }
17605                 }
17606               }
17607               if (_cimg_mp_is_vector(arg1) && !ref[0]) ++arg1;
17608               if (_cimg_mp_is_vector(arg2)) {
17609                 if (arg3==~0U) arg3 = _cimg_mp_size(arg2);
17610                 if (!ref[7]) ++arg2;
17611               }
17612               if (arg3==~0U) arg3 = 1;
17613               _cimg_mp_check_type(arg3,3,1,0);
17614               _cimg_mp_check_type(arg4,4,1,0);
17615               _cimg_mp_check_type(arg5,5,1,0);
17616               _cimg_mp_check_type(arg6,5,1,0);
17617               CImg<ulongT>(1,22).move_to(code);
17618               code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
17619               code.back().get_shared_rows(8,21).fill(ref);
17620               _cimg_mp_return(p1);
17621             }
17622 
17623             if (!std::strncmp(ss,"cos(",4)) { // Cosine
17624               _cimg_mp_op("Function 'cos()'");
17625               arg1 = compile(ss4,se1,depth1,0,is_single);
17626               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
17627               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1]));
17628               _cimg_mp_scalar1(mp_cos,arg1);
17629             }
17630 
17631             if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
17632               _cimg_mp_op("Function 'cosh()'");
17633               arg1 = compile(ss5,se1,depth1,0,is_single);
17634               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
17635               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1]));
17636               _cimg_mp_scalar1(mp_cosh,arg1);
17637             }
17638 
17639             if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
17640               _cimg_mp_op("Function 'critical()'");
17641               p1 = code._width;
17642               arg1 = compile(ss + 9,se1,depth1,p_ref,true);
17643               CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
17644               _cimg_mp_return(arg1);
17645             }
17646 
17647             if (!std::strncmp(ss,"crop(",5)) { // Image crop
17648               _cimg_mp_op("Function 'crop()'");
17649               if (*ss5=='#') { // Index specified
17650                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17651                 p1 = compile(ss6,s0++,depth1,0,is_single);
17652                 _cimg_mp_check_list(false);
17653               } else { p1 = ~0U; s0 = ss5; need_input_copy = true; }
17654               pos = 0;
17655               is_sth = false; // Coordinates specified as a vector?
17656               if (ss5<se1) for (s = s0; s<se; ++s, ++pos) {
17657                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17658                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17659                 arg1 = compile(s,ns,depth1,0,is_single);
17660                 if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17661                   opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
17662                                                   arg1 + (ulongT)_cimg_mp_size(arg1));
17663                   opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(_opcode);
17664                   is_sth = true;
17665                 } else {
17666                   _cimg_mp_check_type(arg1,pos + 1,1,0);
17667                   CImg<ulongT>::vector(arg1).move_to(_opcode);
17668                 }
17669                 s = ns;
17670               }
17671               (_opcode>'y').move_to(opcode);
17672 
17673               arg1 = 0; arg2 = (p1!=~0U);
17674               switch (opcode._height) {
17675               case 0 : case 1 :
17676                 CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
17677                 break;
17678               case 2 :
17679                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
17680                 arg1 = arg2?3:2;
17681                 break;
17682               case 3 :
17683                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
17684                 arg1 = arg2?3:2;
17685                 break;
17686               case 4 :
17687                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
17688                   move_to(opcode);
17689                 arg1 = (is_sth?2:1) + arg2;
17690                 break;
17691               case 5 :
17692                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
17693                   move_to(opcode);
17694                 arg1 = (is_sth?2:1) + arg2;
17695                 break;
17696               case 6 :
17697                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
17698                                     _cimg_mp_boundary).move_to(opcode);
17699                 arg1 = (is_sth?2:4) + arg2;
17700                 break;
17701               case 7 :
17702                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
17703                                     opcode[6]).move_to(opcode);
17704                 arg1 = (is_sth?2:4) + arg2;
17705                 break;
17706               case 8 :
17707                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
17708                                     opcode[7],_cimg_mp_boundary).move_to(opcode);
17709                 arg1 = (is_sth?2:5) + arg2;
17710                 break;
17711               case 9 :
17712                 arg1 = (is_sth?2:5) + arg2;
17713                 break;
17714               default : // Error -> too much arguments
17715                 *se = saved_char;
17716                 s0 = ss - 4>expr._data?ss - 4:expr._data;
17717                 cimg::strellipsize(s0,64);
17718                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
17719                                             "CImg<%s>::%s: %s: Too much arguments specified, "
17720                                             "in expression '%s%s%s'.",
17721                                             pixel_type(),_cimg_mp_calling_function,s_op,
17722                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17723               }
17724 
17725               _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
17726               _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
17727               _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
17728               _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
17729               if (opcode[4]!=(ulongT)~0U) {
17730                 _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3);
17731                 opcode[4] = (ulongT)mem[opcode[4]];
17732               }
17733               if (opcode[5]!=(ulongT)~0U) {
17734                 _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3);
17735                 opcode[5] = (ulongT)mem[opcode[5]];
17736               }
17737               if (opcode[6]!=(ulongT)~0U) {
17738                 _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3);
17739                 opcode[6] = (ulongT)mem[opcode[6]];
17740               }
17741               if (opcode[7]!=(ulongT)~0U) {
17742                 _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3);
17743                 opcode[7] = (ulongT)mem[opcode[7]];
17744               }
17745               _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
17746 
17747               if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
17748                   opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
17749                 if (p1!=~0U) {
17750                   _cimg_mp_check_constant(p1,1,1);
17751                   p1 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17752                 }
17753                 const CImg<T> &img = p1!=~0U?listin[p1]:imgin;
17754                 if (!img) {
17755                   *se = saved_char;
17756                   s0 = ss - 4>expr._data?ss - 4:expr._data;
17757                   cimg::strellipsize(s0,64);
17758                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
17759                                               "CImg<%s>::%s: %s: Cannot crop empty image when "
17760                                               "some xyzc-coordinates are unspecified, in expression '%s%s%s'.",
17761                                               pixel_type(),_cimg_mp_calling_function,s_op,
17762                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17763                 }
17764                 if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
17765                 if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
17766                 if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
17767                 if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
17768               }
17769 
17770               pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
17771               CImg<ulongT>::vector((ulongT)mp_crop,
17772                                   pos,p1,
17773                                   *opcode,opcode[1],opcode[2],opcode[3],
17774                                   opcode[4],opcode[5],opcode[6],opcode[7],
17775                                   opcode[8]).move_to(code);
17776               _cimg_mp_return(pos);
17777             }
17778 
17779             if (!std::strncmp(ss,"cross(",6)) { // Cross product
17780               _cimg_mp_op("Function 'cross()'");
17781               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17782               arg1 = compile(ss6,s1,depth1,0,is_single);
17783               arg2 = compile(++s1,se1,depth1,0,is_single);
17784               _cimg_mp_check_type(arg1,1,2,3);
17785               _cimg_mp_check_type(arg2,2,2,3);
17786               pos = vector(3);
17787               CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
17788               _cimg_mp_return(pos);
17789             }
17790 
17791             if (!std::strncmp(ss,"cut(",4)) { // Cut
17792               _cimg_mp_op("Function 'cut()'");
17793               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17794               arg1 = compile(ss4,s1,depth1,0,is_single);
17795               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17796               arg2 = compile(++s1,s2,depth1,0,is_single);
17797               arg3 = compile(++s2,se1,depth1,0,is_single);
17798               _cimg_mp_check_type(arg2,2,1,0);
17799               _cimg_mp_check_type(arg3,3,1,0);
17800               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
17801               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
17802                 val = mem[arg1];
17803                 val1 = mem[arg2];
17804                 val2 = mem[arg3];
17805                 _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
17806               }
17807               _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
17808             }
17809             break;
17810 
17811           case 'd' :
17812             if (*ss1=='(') { // Image depth
17813               _cimg_mp_op("Function 'd()'");
17814               if (*ss2=='#') { // Index specified
17815                 p1 = compile(ss3,se1,depth1,0,is_single);
17816                 _cimg_mp_check_list(false);
17817               } else { if (ss2!=se1) break; p1 = ~0U; }
17818               pos = scalar();
17819               CImg<ulongT>::vector((ulongT)mp_image_d,pos,p1).move_to(code);
17820               _cimg_mp_return(pos);
17821             }
17822 
17823             if (!std::strncmp(ss,"date(",5)) { // Current date or file date
17824               _cimg_mp_op("Function 'date()'");
17825               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17826               arg1 = ss5!=se1?compile(ss5,s1,depth1,0,is_single):~0U;
17827               is_sth = s1++!=se1; // is_filename
17828               pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
17829               if (is_sth) {
17830                 *se1 = 0;
17831                 variable_name.assign(CImg<charT>::string(s1,true,true).unroll('y'),true);
17832                 cimg::strpare(variable_name,false,true);
17833                 ((CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)),variable_name)>'y').
17834                   move_to(opcode);
17835                 *se1 = ')';
17836               } else
17837                 CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)).move_to(opcode);
17838               opcode[2] = opcode._height;
17839               opcode.move_to(code);
17840               _cimg_mp_return(pos);
17841             }
17842 
17843             if (!std::strncmp(ss,"debug(",6)) { // Print debug info
17844               _cimg_mp_op("Function 'debug()'");
17845               p1 = code._width;
17846               arg1 = compile(ss6,se1,depth1,p_ref,is_single);
17847               *se1 = 0;
17848               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
17849               cimg::strpare(variable_name,false,true);
17850               ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
17851                 variable_name)>'y').move_to(opcode);
17852               opcode[2] = opcode._height;
17853               opcode.move_to(code,p1);
17854               *se1 = ')';
17855               _cimg_mp_return(arg1);
17856             }
17857 
17858             if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
17859               _cimg_mp_op("Function 'display()'");
17860               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17861                 CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
17862                 _cimg_mp_return_nan();
17863               }
17864               if (*ss8!='#') { // Vector
17865                 s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17866                 arg1 = compile(ss8,s1,depth1,0,is_single);
17867                 arg2 = 0; arg3 = arg4 = arg5 = 1;
17868                 if (s1<se1) {
17869                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17870                   arg2 = compile(s1 + 1,s2,depth1,0,is_single);
17871                   if (s2<se1) {
17872                     s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17873                     arg3 = compile(s2,s3,depth1,0,is_single);
17874                     if (s3<se1) {
17875                       s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17876                       arg4 = compile(s3,s2,depth1,0,is_single);
17877                       arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
17878                     }
17879                   }
17880                 }
17881                 _cimg_mp_check_type(arg2,2,1,0);
17882                 _cimg_mp_check_type(arg3,3,1,0);
17883                 _cimg_mp_check_type(arg4,4,1,0);
17884                 _cimg_mp_check_type(arg5,5,1,0);
17885 
17886                 c1 = *s1; *s1 = 0;
17887                 variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
17888                 cimg::strpare(variable_name,false,true);
17889                 if (_cimg_mp_is_vector(arg1))
17890                   ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
17891                     variable_name)>'y').move_to(opcode);
17892                 else
17893                   ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
17894                     variable_name)>'y').move_to(opcode);
17895                 opcode[2] = opcode._height;
17896                 opcode.move_to(code);
17897 
17898                 ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
17899                                        arg2,arg3,arg4,arg5),
17900                   variable_name)>'y').move_to(opcode);
17901                 opcode[2] = opcode._height;
17902                 opcode.move_to(code);
17903                 *s1 = c1;
17904                 _cimg_mp_return(arg1);
17905 
17906               } else { // Image
17907                 p1 = compile(ss8 + 1,se1,depth1,0,is_single);
17908                 _cimg_mp_check_list(true);
17909                 CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
17910                 _cimg_mp_return_nan();
17911               }
17912             }
17913 
17914             if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
17915               _cimg_mp_op("Function 'det()'");
17916               arg1 = compile(ss4,se1,depth1,0,is_single);
17917               _cimg_mp_check_matrix_square(arg1,1);
17918               p1 = (unsigned int)std::sqrt((float)_cimg_mp_size(arg1));
17919               _cimg_mp_scalar2(mp_det,arg1,p1);
17920             }
17921 
17922             if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
17923               _cimg_mp_op("Function 'diag()'");
17924               arg1 = compile(ss5,se1,depth1,0,is_single);
17925               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(arg1);
17926               p1 = _cimg_mp_size(arg1);
17927               pos = vector(p1*p1);
17928               CImg<ulongT>::vector((ulongT)mp_diag,pos,arg1,p1).move_to(code);
17929               _cimg_mp_return(pos);
17930             }
17931 
17932             if (!std::strncmp(ss,"dot(",4)) { // Dot product
17933               _cimg_mp_op("Function 'dot()'");
17934               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17935               arg1 = compile(ss4,s1,depth1,0,is_single);
17936               arg2 = compile(++s1,se1,depth1,0,is_single);
17937               _cimg_mp_check_type(arg1,1,2,0);
17938               _cimg_mp_check_type(arg2,2,2,0);
17939               if (_cimg_mp_is_vector(arg1)) _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
17940               _cimg_mp_scalar2(mp_mul,arg1,arg2);
17941             }
17942 
17943             if (!std::strncmp(ss,"do(",3) || !std::strncmp(ss,"dowhile(",8)) { // Do..while
17944               _cimg_mp_op("Function 'dowhile()'");
17945               s0 = *ss2=='('?ss3:ss8;
17946               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17947               arg1 = code._width;
17948               arg6 = mempos;
17949               p1 = compile(s0,s1,depth1,0,is_single); // Body
17950               arg2 = code._width;
17951               p2 = s1<se1?compile(++s1,se1,depth1,0,is_single):p1; // Condition
17952               _cimg_mp_check_type(p2,2,1,0);
17953               CImg<ulongT>::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
17954                                    p1>=arg6 && !_cimg_mp_is_constant(p1),
17955                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
17956               _cimg_mp_return(p1);
17957             }
17958 
17959             if (!std::strncmp(ss,"draw(",5)) { // Draw image
17960               if (!is_single) is_parallelizable = false;
17961               _cimg_mp_op("Function 'draw()'");
17962               if (*ss5=='#') { // Index specified
17963                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17964                 p1 = compile(ss6,s0++,depth1,0,is_single);
17965                 _cimg_mp_check_list(true);
17966               } else { p1 = ~0U; s0 = ss5; }
17967               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17968               arg1 = compile(s0,s1,depth1,0,is_single);
17969               arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17970               arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17971               arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17972               arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17973               s0 = se1;
17974               if (s1<se1) {
17975                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17976                 arg2 = compile(++s1,s0,depth1,0,is_single);
17977                 if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
17978                   p2 = _cimg_mp_size(arg2);
17979                   ++arg2;
17980                   if (p2>1) {
17981                     arg3 = arg2 + 1;
17982                     if (p2>2) {
17983                       arg4 = arg3 + 1;
17984                       if (p2>3) arg5 = arg4 + 1;
17985                     }
17986                   }
17987                   ++s0;
17988                   is_sth = true;
17989                 } else {
17990                   if (s0<se1) {
17991                     is_sth = p1!=~0U;
17992                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17993                     arg3 = compile(++s0,s1,depth1,0,is_single);
17994                     _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
17995                     if (s1<se1) {
17996                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17997                       arg4 = compile(++s1,s0,depth1,0,is_single);
17998                       _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
17999                       if (s0<se1) {
18000                         s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18001                         arg5 = compile(++s0,s1,depth1,0,is_single);
18002                         _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
18003                         s0 = ++s1;
18004                       }
18005                     }
18006                   }
18007                   is_sth = false;
18008                 }
18009               }
18010 
18011               CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
18012                                    0,0,0,0,1,(ulongT)~0U,0,1).move_to(opcode);
18013 
18014               arg2 = arg3 = arg4 = arg5 = ~0U;
18015               p2 = p1!=~0U?0:1;
18016               if (s0<se1) {
18017                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18018                 arg2 = compile(s0,s1,depth1,0,is_single);
18019                 _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
18020                 if (s1<se1) {
18021                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18022                   arg3 = compile(++s1,s0,depth1,0,is_single);
18023                   _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
18024                   if (s0<se1) {
18025                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18026                     arg4 = compile(++s0,s1,depth1,0,is_single);
18027                     _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
18028                     if (s1<se1) {
18029                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18030                       arg5 = compile(++s1,s0,depth1,0,is_single);
18031                       _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
18032                     }
18033                   }
18034                 }
18035               }
18036               if (s0<s1) s0 = s1;
18037 
18038               opcode[8] = (ulongT)arg2;
18039               opcode[9] = (ulongT)arg3;
18040               opcode[10] = (ulongT)arg4;
18041               opcode[11] = (ulongT)arg5;
18042 
18043               if (s0<se1) {
18044                 s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18045                 arg6 = compile(++s0,s1,depth1,0,is_single);
18046                 _cimg_mp_check_type(arg6,0,1,0);
18047                 opcode[12] = arg6;
18048                 if (s1<se1) {
18049                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18050                   p2 = compile(++s1,s0,depth1,0,is_single);
18051                   _cimg_mp_check_type(p2,0,2,0);
18052                   opcode[13] = p2;
18053                   opcode[14] = _cimg_mp_size(p2);
18054                   p3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
18055                   _cimg_mp_check_type(p3,0,1,0);
18056                   opcode[15] = p3;
18057                 }
18058               }
18059               opcode.move_to(code);
18060               _cimg_mp_return(arg1);
18061             }
18062 
18063             break;
18064 
18065           case 'e' :
18066             if (!std::strncmp(ss,"echo(",5)) { // Echo
18067               _cimg_mp_op("Function 'echo()'");
18068               CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(_opcode);
18069               for (s = ss5; s<se; ++s) {
18070                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18071                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18072                 arg1 = compile(s,ns,depth1,0,is_single);
18073                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(_opcode);
18074                 s = ns;
18075               }
18076               (_opcode>'y').move_to(opcode);
18077               opcode[2] = opcode._height;
18078               opcode.move_to(code);
18079               _cimg_mp_return_nan();
18080             }
18081 
18082             if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
18083               _cimg_mp_op("Function 'eig()'");
18084               arg1 = compile(ss4,se1,depth1,0,is_single);
18085               _cimg_mp_check_matrix_square(arg1,1);
18086               p1 = (unsigned int)std::sqrt((float)_cimg_mp_size(arg1));
18087               pos = vector((p1 + 1)*p1);
18088               CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
18089               _cimg_mp_return(pos);
18090             }
18091 
18092             if (!std::strncmp(ss,"end(",4)) { // End
18093               _cimg_mp_op("Function 'end()'");
18094               code.swap(code_end);
18095               compile(ss4,se1,depth1,p_ref,true);
18096               code.swap(code_end);
18097               _cimg_mp_return_nan();
18098             }
18099 
18100             if (!std::strncmp(ss,"ext(",4)) { // Extern
18101               _cimg_mp_op("Function 'ext()'");
18102               if (!is_single) is_parallelizable = false;
18103               CImg<ulongT>::vector((ulongT)mp_ext,0,0).move_to(_opcode);
18104               pos = 1;
18105               for (s = ss4; s<se; ++s) {
18106                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18107                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18108                 arg1 = compile(s,ns,depth1,0,is_single);
18109                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(_opcode);
18110                 s = ns;
18111               }
18112               (_opcode>'y').move_to(opcode);
18113               pos = scalar();
18114               opcode[1] = pos;
18115               opcode[2] = opcode._height;
18116               opcode.move_to(code);
18117               _cimg_mp_return(pos);
18118             }
18119 
18120             if (!std::strncmp(ss,"exp(",4)) { // Exponential
18121               _cimg_mp_op("Function 'exp()'");
18122               arg1 = compile(ss4,se1,depth1,0,is_single);
18123               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
18124               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1]));
18125               _cimg_mp_scalar1(mp_exp,arg1);
18126             }
18127 
18128             if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
18129               _cimg_mp_op("Function 'eye()'");
18130               arg1 = compile(ss4,se1,depth1,0,is_single);
18131               _cimg_mp_check_constant(arg1,1,3);
18132               p1 = (unsigned int)mem[arg1];
18133               pos = vector(p1*p1);
18134               CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
18135               _cimg_mp_return(pos);
18136             }
18137             break;
18138 
18139           case 'f' :
18140             if (!std::strncmp(ss,"fact(",5)) { // Factorial
18141               _cimg_mp_op("Function 'fact()'");
18142               arg1 = compile(ss5,se1,depth1,0,is_single);
18143               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
18144               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial(mem[arg1]));
18145               _cimg_mp_scalar1(mp_factorial,arg1);
18146             }
18147 
18148             if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
18149               _cimg_mp_op("Function 'fibo()'");
18150               arg1 = compile(ss5,se1,depth1,0,is_single);
18151               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
18152               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci(mem[arg1]));
18153               _cimg_mp_scalar1(mp_fibonacci,arg1);
18154             }
18155 
18156             if (!std::strncmp(ss,"find(",5)) { // Find
18157               _cimg_mp_op("Function 'find()'");
18158 
18159               // First argument: data to look at.
18160               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18161               if (*ss5=='#') { // Index specified
18162                 p1 = compile(ss6,s0,depth1,0,is_single);
18163                 _cimg_mp_check_list(false);
18164                 arg1 = ~0U;
18165               } else { // Vector specified
18166                 arg1 = compile(ss5,s0,depth1,0,is_single);
18167                 _cimg_mp_check_type(arg1,1,2,0);
18168                 p1 = ~0U;
18169               }
18170 
18171               // Second argument: data to find.
18172               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18173               arg2 = compile(s0,s1,depth1,0,is_single);
18174 
18175               // Third and fourth arguments: search direction and starting index.
18176               arg3 = 1; arg4 = _cimg_mp_slot_nan;
18177               if (s1<se1) {
18178                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18179                 arg3 = compile(++s1,s0,depth1,0,is_single);
18180                 _cimg_mp_check_type(arg3,3,1,0);
18181                 if (s0<se1) {
18182                   arg4 = compile(++s0,se1,depth1,0,is_single);
18183                   _cimg_mp_check_type(arg4,4,1,0);
18184                 }
18185               }
18186               if (p1!=~0U) {
18187                 if (_cimg_mp_is_vector(arg2))
18188                   _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
18189                 _cimg_mp_scalar4(mp_list_find,p1,arg2,arg3,arg4);
18190               }
18191               if (_cimg_mp_is_vector(arg2))
18192                 _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
18193               _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2,arg3,arg4);
18194             }
18195 
18196             if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
18197               _cimg_mp_op("Function 'for()'");
18198               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18199               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18200               s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18201               arg1 = code._width;
18202               p1 = compile(ss4,s1,depth1,0,is_single); // Init
18203               arg2 = code._width;
18204               p2 = compile(++s1,s2,depth1,0,is_single); // Cond
18205               arg3 = code._width;
18206               arg6 = mempos;
18207               if (s3<se1) { // Body + post
18208                 p3 = compile(s3 + 1,se1,depth1,0,is_single); // Body
18209                 arg4 = code._width;
18210                 pos = compile(++s2,s3,depth1,0,is_single); // Post
18211               } else {
18212                 p3 = compile(++s2,se1,depth1,0,is_single); // Body only
18213                 arg4 = pos = code._width;
18214               }
18215               _cimg_mp_check_type(p2,2,1,0);
18216               arg5 = _cimg_mp_size(pos);
18217               CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
18218                                    arg4 - arg3,code._width - arg4,
18219                                    p3>=arg6 && !_cimg_mp_is_constant(p3),
18220                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
18221               _cimg_mp_return(p3);
18222             }
18223 
18224             if (!std::strncmp(ss,"floor(",6)) { // Floor
18225               _cimg_mp_op("Function 'floor()'");
18226               arg1 = compile(ss6,se1,depth1,0,is_single);
18227               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
18228               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1]));
18229               _cimg_mp_scalar1(mp_floor,arg1);
18230             }
18231 
18232             if (!std::strncmp(ss,"fsize(",6)) { // File size
18233               _cimg_mp_op("Function 'fsize()'");
18234               *se1 = 0;
18235               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
18236               cimg::strpare(variable_name,false,true);
18237               pos = scalar();
18238               ((CImg<ulongT>::vector((ulongT)mp_fsize,pos,0),variable_name)>'y').move_to(opcode);
18239               *se1 = ')';
18240               opcode[2] = opcode._height;
18241               opcode.move_to(code);
18242               _cimg_mp_return(pos);
18243             }
18244             break;
18245 
18246           case 'g' :
18247             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
18248               _cimg_mp_op("Function 'gauss()'");
18249               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18250               arg1 = compile(ss6,s1,depth1,0,is_single);
18251               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
18252               _cimg_mp_check_type(arg2,2,1,0);
18253               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_gauss,arg1,arg2);
18254               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) {
18255                 val1 = mem[arg1];
18256                 val2 = mem[arg2];
18257                 _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/std::sqrt(2*val2*val2*cimg::PI));
18258               }
18259               _cimg_mp_scalar2(mp_gauss,arg1,arg2);
18260             }
18261 
18262             if (!std::strncmp(ss,"gcd(",4)) { // Gcd
18263               _cimg_mp_op("Function 'gcd()'");
18264               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18265               arg1 = compile(ss4,s1,depth1,0,is_single);
18266               arg2 = compile(++s1,se1,depth1,0,is_single);
18267               _cimg_mp_check_type(arg1,1,1,0);
18268               _cimg_mp_check_type(arg2,2,1,0);
18269               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18270                 _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
18271               _cimg_mp_scalar2(mp_gcd,arg1,arg2);
18272             }
18273             break;
18274 
18275           case 'h' :
18276             if (*ss1=='(') { // Image height
18277               _cimg_mp_op("Function 'h()'");
18278               if (*ss2=='#') { // Index specified
18279                 p1 = compile(ss3,se1,depth1,0,is_single);
18280                 _cimg_mp_check_list(false);
18281               } else { if (ss2!=se1) break; p1 = ~0U; }
18282               pos = scalar();
18283               CImg<ulongT>::vector((ulongT)mp_image_h,pos,p1).move_to(code);
18284               _cimg_mp_return(pos);
18285             }
18286             break;
18287 
18288           case 'i' :
18289             if (*ss1=='c' && *ss2=='(') { // Image median
18290               _cimg_mp_op("Function 'ic()'");
18291               if (*ss3=='#') { // Index specified
18292                 p1 = compile(ss4,se1,depth1,0,is_single);
18293                 _cimg_mp_check_list(false);
18294               } else { if (ss3!=se1) break; p1 = ~0U; }
18295               pos = scalar();
18296               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
18297               _cimg_mp_return(pos);
18298             }
18299 
18300             if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
18301               _cimg_mp_op("Function 'if()'");
18302               s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18303               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18304               arg1 = compile(ss3,s1,depth1,0,is_single);
18305               _cimg_mp_check_type(arg1,1,1,0);
18306               if (_cimg_mp_is_constant(arg1)) {
18307                 if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,is_single);
18308                 else return s2<se1?compile(++s2,se1,depth1,0,is_single):0;
18309               }
18310               p2 = code._width;
18311               arg2 = compile(++s1,s2,depth1,0,is_single);
18312               p3 = code._width;
18313               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):
18314                 _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
18315               _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
18316               arg4 = _cimg_mp_size(arg2);
18317               if (arg4) pos = vector(arg4); else pos = scalar();
18318               CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
18319                                   p3 - p2,code._width - p3,arg4).move_to(code,p2);
18320               _cimg_mp_return(pos);
18321             }
18322 
18323             if (!std::strncmp(ss,"init(",5)) { // Init
18324               _cimg_mp_op("Function 'init()'");
18325               code.swap(code_init);
18326               arg1 = compile(ss5,se1,depth1,p_ref,true);
18327               code.swap(code_init);
18328               _cimg_mp_return(arg1);
18329             }
18330 
18331             if (!std::strncmp(ss,"int(",4)) { // Integer cast
18332               _cimg_mp_op("Function 'int()'");
18333               arg1 = compile(ss4,se1,depth1,0,is_single);
18334               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
18335               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]);
18336               _cimg_mp_scalar1(mp_int,arg1);
18337             }
18338 
18339             if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion
18340               _cimg_mp_op("Function 'inv()'");
18341               arg1 = compile(ss4,se1,depth1,0,is_single);
18342               if (_cimg_mp_is_vector(arg1)) {
18343                 _cimg_mp_check_matrix_square(arg1,1);
18344                 p1 = (unsigned int)std::sqrt((float)_cimg_mp_size(arg1));
18345                 pos = vector(p1*p1);
18346                 CImg<ulongT>::vector((ulongT)mp_matrix_inv,pos,arg1,p1).move_to(code);
18347                 _cimg_mp_return(pos);
18348               }
18349               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]);
18350               _cimg_mp_scalar2(mp_div,1,arg1);
18351             }
18352 
18353             if (*ss1=='s') { // Family of 'is_?()' functions
18354 
18355               if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
18356                 _cimg_mp_op("Function 'isbool()'");
18357                 if (ss7==se1) _cimg_mp_return(0);
18358                 arg1 = compile(ss7,se1,depth1,0,is_single);
18359                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
18360                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0);
18361                 _cimg_mp_scalar1(mp_isbool,arg1);
18362               }
18363 
18364               if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
18365                 _cimg_mp_op("Function 'isdir()'");
18366                 *se1 = 0;
18367                 is_sth = cimg::is_directory(ss6);
18368                 *se1 = ')';
18369                 _cimg_mp_return(is_sth?1U:0U);
18370               }
18371 
18372               if (!std::strncmp(ss,"isfile(",7)) { // Is file?
18373                 _cimg_mp_op("Function 'isfile()'");
18374                 *se1 = 0;
18375                 is_sth = cimg::is_file(ss7);
18376                 *se1 = ')';
18377                 _cimg_mp_return(is_sth?1U:0U);
18378               }
18379 
18380               if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
18381                 if (ss5>=se1) _cimg_mp_return(0);
18382                 _cimg_mp_op("Function 'isin()'");
18383                 pos = scalar();
18384                 CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(_opcode);
18385                 for (s = ss5; s<se; ++s) {
18386                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18387                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18388                   arg1 = compile(s,ns,depth1,0,is_single);
18389                   if (_cimg_mp_is_vector(arg1))
18390                     CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
18391                                            arg1 + (ulongT)_cimg_mp_size(arg1)).
18392                       move_to(_opcode);
18393                   else CImg<ulongT>::vector(arg1).move_to(_opcode);
18394                   s = ns;
18395                 }
18396                 (_opcode>'y').move_to(opcode);
18397                 opcode[2] = opcode._height;
18398                 opcode.move_to(code);
18399                 _cimg_mp_return(pos);
18400               }
18401 
18402               if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
18403                 _cimg_mp_op("Function 'isinf()'");
18404                 if (ss6==se1) _cimg_mp_return(0);
18405                 arg1 = compile(ss6,se1,depth1,0,is_single);
18406                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
18407                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
18408                 _cimg_mp_scalar1(mp_isinf,arg1);
18409               }
18410 
18411               if (!std::strncmp(ss,"isint(",6)) { // Is integer?
18412                 _cimg_mp_op("Function 'isint()'");
18413                 if (ss6==se1) _cimg_mp_return(0);
18414                 arg1 = compile(ss6,se1,depth1,0,is_single);
18415                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
18416                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)(cimg::mod(mem[arg1],1.0)==0));
18417                 _cimg_mp_scalar1(mp_isint,arg1);
18418               }
18419 
18420               if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
18421                 _cimg_mp_op("Function 'isnan()'");
18422                 if (ss6==se1) _cimg_mp_return(0);
18423                 arg1 = compile(ss6,se1,depth1,0,is_single);
18424                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
18425                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
18426                 _cimg_mp_scalar1(mp_isnan,arg1);
18427               }
18428 
18429               if (!std::strncmp(ss,"isval(",6)) { // Is value?
18430                 _cimg_mp_op("Function 'isval()'");
18431                 val = 0;
18432                 if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
18433                 _cimg_mp_return(0);
18434               }
18435 
18436             }
18437             break;
18438 
18439           case 'l' :
18440             if (*ss1=='(') { // Size of image list
18441               _cimg_mp_op("Function 'l()'");
18442               if (ss2!=se1) break;
18443               _cimg_mp_scalar0(mp_list_l);
18444             }
18445 
18446             if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
18447               _cimg_mp_op("Function 'log()'");
18448               arg1 = compile(ss4,se1,depth1,0,is_single);
18449               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
18450               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1]));
18451               _cimg_mp_scalar1(mp_log,arg1);
18452             }
18453 
18454             if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
18455               _cimg_mp_op("Function 'log2()'");
18456               arg1 = compile(ss5,se1,depth1,0,is_single);
18457               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
18458               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1]));
18459               _cimg_mp_scalar1(mp_log2,arg1);
18460             }
18461 
18462             if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
18463               _cimg_mp_op("Function 'log10()'");
18464               arg1 = compile(ss6,se1,depth1,0,is_single);
18465               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
18466               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1]));
18467               _cimg_mp_scalar1(mp_log10,arg1);
18468             }
18469 
18470             if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
18471               _cimg_mp_op("Function 'lowercase()'");
18472               arg1 = compile(ss + 10,se1,depth1,0,is_single);
18473               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
18474               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1]));
18475               _cimg_mp_scalar1(mp_lowercase,arg1);
18476             }
18477             break;
18478 
18479           case 'm' :
18480             if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
18481               _cimg_mp_op("Function 'mul()'");
18482               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18483               arg1 = compile(ss4,s1,depth1,0,is_single);
18484               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18485               arg2 = compile(++s1,s2,depth1,0,is_single);
18486               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
18487               _cimg_mp_check_type(arg1,1,2,0);
18488               _cimg_mp_check_type(arg2,2,2,0);
18489               _cimg_mp_check_constant(arg3,3,3);
18490               p1 = _cimg_mp_size(arg1);
18491               p2 = _cimg_mp_size(arg2);
18492               p3 = (unsigned int)mem[arg3];
18493               arg5 = p2/p3;
18494               arg4 = p1/arg5;
18495               if (arg4*arg5!=p1 || arg5*p3!=p2) {
18496                 *se = saved_char;
18497                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18498                 cimg::strellipsize(s0,64);
18499                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18500                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
18501                                             "do not match with third argument 'nb_colsB=%u', "
18502                                             "in expression '%s%s%s'.",
18503                                             pixel_type(),_cimg_mp_calling_function,s_op,
18504                                             s_type(arg1)._data,s_type(arg2)._data,p3,
18505                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18506               }
18507               pos = vector(arg4*p3);
18508               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
18509               _cimg_mp_return(pos);
18510             }
18511             break;
18512 
18513           case 'n' :
18514             if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
18515               _cimg_mp_op("Function 'narg()'");
18516               if (ss5>=se1) _cimg_mp_return(0);
18517               arg1 = 0;
18518               for (s = ss5; s<se; ++s) {
18519                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18520                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18521                 ++arg1; s = ns;
18522               }
18523               _cimg_mp_constant(arg1);
18524             }
18525 
18526             if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
18527                 !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
18528                 (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
18529               _cimg_mp_op("Function 'normP()'");
18530               if (*ss4=='(') { arg1 = 2; s = ss5; }
18531               else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
18532               else if (arg1==~0U) {
18533                 arg1 = compile(ss4,s++,depth1,0,is_single);
18534                 _cimg_mp_check_constant(arg1,0,2);
18535                 arg1 = (unsigned int)mem[arg1];
18536               } else s = std::strchr(ss4,'(') + 1;
18537               pos = scalar();
18538               switch (arg1) {
18539               case 0 :
18540                 CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(_opcode); break;
18541               case 1 :
18542                 CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(_opcode); break;
18543               case 2 :
18544                 CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(_opcode); break;
18545               case ~0U :
18546                 CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(_opcode); break;
18547               default :
18548                 CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
18549                   move_to(_opcode);
18550               }
18551               for ( ; s<se; ++s) {
18552                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18553                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18554                 arg2 = compile(s,ns,depth1,0,is_single);
18555                 if (_cimg_mp_is_vector(arg2))
18556                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
18557                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
18558                     move_to(_opcode);
18559                 else CImg<ulongT>::vector(arg2).move_to(_opcode);
18560                 s = ns;
18561               }
18562 
18563               (_opcode>'y').move_to(opcode);
18564               if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
18565                 _cimg_mp_scalar1(mp_abs,opcode[3]);
18566               opcode[2] = opcode._height;
18567               opcode.move_to(code);
18568               _cimg_mp_return(pos);
18569             }
18570             break;
18571 
18572           case 'p' :
18573             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
18574               _cimg_mp_op("Function 'permut()'");
18575               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18576               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18577               arg1 = compile(ss7,s1,depth1,0,is_single);
18578               arg2 = compile(++s1,s2,depth1,0,is_single);
18579               arg3 = compile(++s2,se1,depth1,0,is_single);
18580               _cimg_mp_check_type(arg1,1,1,0);
18581               _cimg_mp_check_type(arg2,2,1,0);
18582               _cimg_mp_check_type(arg3,3,1,0);
18583               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
18584                 _cimg_mp_constant(cimg::permutations(mem[arg1],mem[arg2],(bool)mem[arg3]));
18585               _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
18586             }
18587 
18588             if (!std::strncmp(ss,"pseudoinv(",10)) { // Matrix/scalar pseudo-inversion
18589               _cimg_mp_op("Function 'pseudoinv()'");
18590               s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18591               arg1 = compile(ss + 10,s1,depth1,0,is_single);
18592               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
18593               _cimg_mp_check_type(arg1,1,2,0);
18594               _cimg_mp_check_constant(arg2,2,3);
18595               p1 = _cimg_mp_size(arg1);
18596               p2 = (unsigned int)mem[arg2];
18597               p3 = p1/p2;
18598               if (p3*p2!=p1) {
18599                 *se = saved_char;
18600                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18601                 cimg::strellipsize(s0,64);
18602                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18603                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
18604                                             "does not match with second argument 'nb_colsA=%u', "
18605                                             "in expression '%s%s%s'.",
18606                                             pixel_type(),_cimg_mp_calling_function,s_op,
18607                                             s_type(arg1)._data,p2,
18608                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18609               }
18610               pos = vector(p1);
18611               CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code);
18612               _cimg_mp_return(pos);
18613             }
18614 
18615             if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
18616               is_sth = ss[5]=='s'; // is prints()
18617               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
18618               s0 = is_sth?ss7:ss6;
18619               if (*s0!='#' || is_sth) { // Regular expression
18620                 for (s = s0; s<se; ++s) {
18621                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18622                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18623                   pos = compile(s,ns,depth1,p_ref,is_single);
18624                   c1 = *ns; *ns = 0;
18625                   variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
18626                   cimg::strpare(variable_name,false,true);
18627                   if (_cimg_mp_is_vector(pos)) // Vector
18628                     ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
18629                       variable_name)>'y').move_to(opcode);
18630                   else // Scalar
18631                     ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
18632                       variable_name)>'y').move_to(opcode);
18633                   opcode[2] = opcode._height;
18634                   opcode.move_to(code);
18635                   *ns = c1; s = ns;
18636                 }
18637                 _cimg_mp_return(pos);
18638               } else { // Image
18639                 p1 = compile(ss7,se1,depth1,0,is_single);
18640                 _cimg_mp_check_list(true);
18641                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
18642                 _cimg_mp_return_nan();
18643               }
18644             }
18645             break;
18646 
18647           case 'r' :
18648             if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
18649               _cimg_mp_op("Function 'resize()'");
18650               if (*ss7!='#') { // Vector
18651                 s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18652                 arg1 = compile(ss7,s1,depth1,0,is_single);
18653                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18654                 arg2 = compile(s1,s2,depth1,0,is_single);
18655                 arg3 = 1;
18656                 arg4 = 0;
18657                 if (s2<se1) {
18658                   s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18659                   arg3 = compile(s2,s1,depth1,0,is_single);
18660                   arg4 = s1<se1?compile(++s1,se1,depth1,0,is_single):0;
18661                 }
18662                 _cimg_mp_check_constant(arg2,2,3);
18663                 arg2 = (unsigned int)mem[arg2];
18664                 _cimg_mp_check_type(arg3,3,1,0);
18665                 _cimg_mp_check_type(arg4,4,1,0);
18666                 pos = vector(arg2);
18667                 CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1),
18668                                      arg3,arg4).move_to(code);
18669                 _cimg_mp_return(pos);
18670 
18671               } else { // Image
18672                 if (!is_single) is_parallelizable = false;
18673                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18674                 p1 = compile(ss8,s0++,depth1,0,is_single);
18675                 _cimg_mp_check_list(true);
18676                 CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
18677                   move_to(opcode);
18678                 pos = 0;
18679                 for (s = s0; s<se && pos<10; ++s) {
18680                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18681                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18682                   arg1 = compile(s,ns,depth1,0,is_single);
18683                   _cimg_mp_check_type(arg1,pos + 2,1,0);
18684                   opcode[pos + 3] = arg1;
18685                   s = ns;
18686                   ++pos;
18687                 }
18688                 if (pos<1 || pos>10) {
18689                   *se = saved_char;
18690                   s0 = ss - 4>expr._data?ss - 4:expr._data;
18691                   cimg::strellipsize(s0,64);
18692                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
18693                                               "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.",
18694                                               pixel_type(),_cimg_mp_calling_function,s_op,
18695                                               pos<1?"Missing":"Too much",
18696                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18697                 }
18698                 opcode.move_to(code);
18699                 _cimg_mp_return_nan();
18700               }
18701             }
18702 
18703             if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
18704               _cimg_mp_op("Function 'reverse()'");
18705               arg1 = compile(ss8,se1,depth1,0,is_single);
18706               if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
18707               p1 = _cimg_mp_size(arg1);
18708               pos = vector(p1);
18709               CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
18710               _cimg_mp_return(pos);
18711             }
18712 
18713             if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
18714               _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
18715               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
18716               arg1 = compile(ss4,s1,depth1,0,is_single);
18717               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
18718               _cimg_mp_check_type(arg2,2,1,0);
18719               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
18720               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18721                 _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
18722                                   cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
18723               _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
18724             }
18725 
18726             if (!std::strncmp(ss,"rot(",4)) { // 2d/3d rotation matrix
18727               _cimg_mp_op("Function 'rot()'");
18728               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18729               arg1 = compile(ss4,s1,depth1,0,is_single);
18730               if (s1<se1) { // 3d rotation
18731                 _cimg_mp_check_type(arg1,1,3,3);
18732                 is_sth = false; // Is coordinates as vector?
18733                 if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18734                   is_sth = true;
18735                   p2 = _cimg_mp_size(arg1);
18736                   ++arg1;
18737                   arg2 = arg3 = 0;
18738                   if (p2>1) {
18739                     arg2 = arg1 + 1;
18740                     if (p2>2) arg3 = arg2 + 1;
18741                   }
18742                   arg4 = compile(++s1,se1,depth1,0,is_single);
18743                 } else {
18744                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18745                   arg2 = compile(++s1,s2,depth1,0,is_single);
18746                   s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18747                   arg3 = compile(++s2,s3,depth1,0,is_single);
18748                   arg4 = compile(++s3,se1,depth1,0,is_single);
18749                   _cimg_mp_check_type(arg2,2,1,0);
18750                   _cimg_mp_check_type(arg3,3,1,0);
18751                 }
18752                 _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
18753                 pos = vector(9);
18754                 CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
18755               } else { // 2d rotation
18756                 _cimg_mp_check_type(arg1,1,1,0);
18757                 pos = vector(4);
18758                 CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
18759               }
18760               _cimg_mp_return(pos);
18761             }
18762 
18763             if (!std::strncmp(ss,"round(",6)) { // Value rounding
18764               _cimg_mp_op("Function 'round()'");
18765               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18766               arg1 = compile(ss6,s1,depth1,0,is_single);
18767               arg2 = 1;
18768               arg3 = 0;
18769               if (s1<se1) {
18770                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18771                 arg2 = compile(++s1,s2,depth1,0,is_single);
18772                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
18773               }
18774               _cimg_mp_check_type(arg2,2,1,0);
18775               _cimg_mp_check_type(arg3,3,1,0);
18776               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
18777               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
18778                 _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
18779               _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
18780             }
18781             break;
18782 
18783           case 's' :
18784             if (*ss1=='(') { // Image spectrum
18785               _cimg_mp_op("Function 's()'");
18786               if (*ss2=='#') { // Index specified
18787                 p1 = compile(ss3,se1,depth1,0,is_single);
18788                 _cimg_mp_check_list(false);
18789               } else { if (ss2!=se1) break; p1 = ~0U; }
18790               pos = scalar();
18791               CImg<ulongT>::vector((ulongT)mp_image_s,pos,p1).move_to(code);
18792               _cimg_mp_return(pos);
18793             }
18794 
18795             if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
18796               _cimg_mp_op("Function 'same()'");
18797               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18798               arg1 = compile(ss5,s1,depth1,0,is_single);
18799               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18800               arg2 = compile(++s1,s2,depth1,0,is_single);
18801               arg3 = 11;
18802               arg4 = 1;
18803               if (s2<se1) {
18804                 s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18805                 arg3 = compile(++s2,s3,depth1,0,is_single);
18806                 _cimg_mp_check_type(arg3,3,1,0);
18807                 arg4 = s3<se1?compile(++s3,se1,depth1,0,is_single):1;
18808               }
18809               p1 = _cimg_mp_size(arg1);
18810               p2 = _cimg_mp_size(arg2);
18811               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
18812             }
18813 
18814             if (!std::strncmp(ss,"shift(",6)) { // Shift vector
18815               _cimg_mp_op("Function 'shift()'");
18816               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18817               arg1 = compile(ss6,s1,depth1,0,is_single);
18818               arg2 = 1; arg3 = 0;
18819               if (s1<se1) {
18820                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18821                 arg2 = compile(s1,s0,depth1,0,is_single);
18822                 arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):0;
18823               }
18824               _cimg_mp_check_type(arg1,1,2,0);
18825               _cimg_mp_check_type(arg2,2,1,0);
18826               _cimg_mp_check_type(arg3,3,1,0);
18827               p1 = _cimg_mp_size(arg1);
18828               pos = vector(p1);
18829               CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
18830               _cimg_mp_return(pos);
18831             }
18832 
18833             if (!std::strncmp(ss,"sign(",5)) { // Sign
18834               _cimg_mp_op("Function 'sign()'");
18835               arg1 = compile(ss5,se1,depth1,0,is_single);
18836               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
18837               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1]));
18838               _cimg_mp_scalar1(mp_sign,arg1);
18839             }
18840 
18841             if (!std::strncmp(ss,"sin(",4)) { // Sine
18842               _cimg_mp_op("Function 'sin()'");
18843               arg1 = compile(ss4,se1,depth1,0,is_single);
18844               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
18845               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1]));
18846               _cimg_mp_scalar1(mp_sin,arg1);
18847             }
18848 
18849             if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
18850               _cimg_mp_op("Function 'sinc()'");
18851               arg1 = compile(ss5,se1,depth1,0,is_single);
18852               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
18853               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1]));
18854               _cimg_mp_scalar1(mp_sinc,arg1);
18855             }
18856 
18857             if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
18858               _cimg_mp_op("Function 'sinh()'");
18859               arg1 = compile(ss5,se1,depth1,0,is_single);
18860               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
18861               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1]));
18862               _cimg_mp_scalar1(mp_sinh,arg1);
18863             }
18864 
18865             if (!std::strncmp(ss,"size(",5)) { // Vector size.
18866               _cimg_mp_op("Function 'size()'");
18867               arg1 = compile(ss5,se1,depth1,0,is_single);
18868               _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
18869             }
18870 
18871             if (!std::strncmp(ss,"solve(",6)) { // Solve linear system
18872               _cimg_mp_op("Function 'solve()'");
18873               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18874               arg1 = compile(ss6,s1,depth1,0,is_single);
18875               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18876               arg2 = compile(++s1,s2,depth1,0,is_single);
18877               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
18878               _cimg_mp_check_type(arg1,1,2,0);
18879               _cimg_mp_check_type(arg2,2,2,0);
18880               _cimg_mp_check_constant(arg3,3,3);
18881               p1 = _cimg_mp_size(arg1);
18882               p2 = _cimg_mp_size(arg2);
18883               p3 = (unsigned int)mem[arg3];
18884               arg5 = p2/p3;
18885               arg4 = p1/arg5;
18886               if (arg4*arg5!=p1 || arg5*p3!=p2) {
18887                 *se = saved_char;
18888                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18889                 cimg::strellipsize(s0,64);
18890                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18891                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
18892                                             "do not match with third argument 'nb_colsB=%u', "
18893                                             "in expression '%s%s%s'.",
18894                                             pixel_type(),_cimg_mp_calling_function,s_op,
18895                                             s_type(arg1)._data,s_type(arg2)._data,p3,
18896                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18897               }
18898               pos = vector(arg4*p3);
18899               CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
18900               _cimg_mp_return(pos);
18901             }
18902 
18903             if (!std::strncmp(ss,"sort(",5)) { // Sort vector
18904               _cimg_mp_op("Function 'sort()'");
18905               if (*ss5!='#') { // Vector
18906                 s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18907                 arg1 = compile(ss5,s1,depth1,0,is_single);
18908                 arg2 = arg3 = 1;
18909                 if (s1<se1) {
18910                   s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18911                   arg2 = compile(s1,s0,depth1,0,is_single);
18912                   arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
18913                 }
18914                 _cimg_mp_check_type(arg1,1,2,0);
18915                 _cimg_mp_check_type(arg2,2,1,0);
18916                 _cimg_mp_check_constant(arg3,3,3);
18917                 arg3 = (unsigned int)mem[arg3];
18918                 p1 = _cimg_mp_size(arg1);
18919                 if (p1%arg3) {
18920                   *se = saved_char;
18921                   s0 = ss - 4>expr._data?ss - 4:expr._data;
18922                   cimg::strellipsize(s0,64);
18923                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
18924                                               "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument "
18925                                               "('%s'), in expression '%s%s%s'.",
18926                                               pixel_type(),_cimg_mp_calling_function,s_op,
18927                                               arg3,s_type(arg1)._data,
18928                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18929                 }
18930                 pos = vector(p1);
18931                 CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code);
18932                 _cimg_mp_return(pos);
18933 
18934               } else { // Image
18935                 s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18936                 p1 = compile(ss6,s1,depth1,0,is_single);
18937                 arg1 = 1;
18938                 arg2 = constant(-1.0);
18939                 if (s1<se1) {
18940                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18941                   arg1 = compile(++s1,s2,depth1,0,is_single);
18942                   if (s2<se1) arg2 = compile(++s2,se1,depth1,0,is_single);
18943                 }
18944                 _cimg_mp_check_type(arg1,2,1,0);
18945                 _cimg_mp_check_type(arg2,3,1,0);
18946                 _cimg_mp_check_list(true);
18947                 CImg<ulongT>::vector((ulongT)mp_image_sort,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
18948                 _cimg_mp_return_nan();
18949               }
18950             }
18951 
18952             if (!std::strncmp(ss,"sqr(",4)) { // Square
18953               _cimg_mp_op("Function 'sqr()'");
18954               arg1 = compile(ss4,se1,depth1,0,is_single);
18955               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
18956               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1]));
18957               _cimg_mp_scalar1(mp_sqr,arg1);
18958             }
18959 
18960             if (!std::strncmp(ss,"sqrt(",5)) { // Square root
18961               _cimg_mp_op("Function 'sqrt()'");
18962               arg1 = compile(ss5,se1,depth1,0,is_single);
18963               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
18964               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1]));
18965               _cimg_mp_scalar1(mp_sqrt,arg1);
18966             }
18967 
18968             if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
18969               _cimg_mp_op("Function 'srand()'");
18970               arg1 = ss6<se1?compile(ss6,se1,depth1,0,is_single):~0U;
18971               if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
18972               _cimg_mp_scalar0(mp_srand0);
18973             }
18974 
18975             if (!std::strncmp(ss,"stats(",6)) { // Image statistics
18976               _cimg_mp_op("Function 'stats()'");
18977               if (*ss6=='#') { // Index specified
18978                 p1 = compile(ss7,se1,depth1,0,is_single);
18979                 _cimg_mp_check_list(false);
18980               } else { if (ss6!=se1) break; p1 = ~0U; }
18981               pos = vector(14);
18982               CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
18983               _cimg_mp_return(pos);
18984             }
18985 
18986             if (!std::strncmp(ss,"stov(",5)) { // String to double
18987               _cimg_mp_op("Function 'stov()'");
18988               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18989               arg1 = compile(ss5,s1,depth1,0,is_single);
18990               arg2 = arg3 = 0;
18991               if (s1<se1) {
18992                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18993                 arg2 = compile(++s1,s2,depth1,0,is_single);
18994                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
18995               }
18996               _cimg_mp_check_type(arg2,2,1,0);
18997               _cimg_mp_check_type(arg3,3,1,0);
18998               p1 = _cimg_mp_size(arg1);
18999               _cimg_mp_scalar4(mp_stov,arg1,p1,arg2,arg3);
19000             }
19001 
19002             if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
19003               _cimg_mp_op("Function 'svd()'");
19004               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19005               arg1 = compile(ss4,s1,depth1,0,is_single);
19006               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
19007               _cimg_mp_check_type(arg1,1,2,0);
19008               _cimg_mp_check_constant(arg2,2,3);
19009               p1 = _cimg_mp_size(arg1);
19010               p2 = (unsigned int)mem[arg2];
19011               p3 = p1/p2;
19012               if (p3*p2!=p1) {
19013                 *se = saved_char;
19014                 s0 = ss - 4>expr._data?ss - 4:expr._data;
19015                 cimg::strellipsize(s0,64);
19016                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19017                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
19018                                             "does not match with second argument 'nb_colsA=%u', "
19019                                             "in expression '%s%s%s'.",
19020                                             pixel_type(),_cimg_mp_calling_function,s_op,
19021                                             s_type(arg1)._data,p2,
19022                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19023               }
19024               pos = vector(p1 + p2 + p2*p2);
19025               CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
19026               _cimg_mp_return(pos);
19027             }
19028             break;
19029 
19030           case 't' :
19031             if (!std::strncmp(ss,"tan(",4)) { // Tangent
19032               _cimg_mp_op("Function 'tan()'");
19033               arg1 = compile(ss4,se1,depth1,0,is_single);
19034               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
19035               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1]));
19036               _cimg_mp_scalar1(mp_tan,arg1);
19037             }
19038 
19039             if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
19040               _cimg_mp_op("Function 'tanh()'");
19041               arg1 = compile(ss5,se1,depth1,0,is_single);
19042               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
19043               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1]));
19044               _cimg_mp_scalar1(mp_tanh,arg1);
19045             }
19046 
19047             if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
19048               _cimg_mp_op("Function 'trace()'");
19049               arg1 = compile(ss6,se1,depth1,0,is_single);
19050               _cimg_mp_check_matrix_square(arg1,1);
19051               p1 = (unsigned int)std::sqrt((float)_cimg_mp_size(arg1));
19052               _cimg_mp_scalar2(mp_trace,arg1,p1);
19053             }
19054 
19055             if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose
19056               _cimg_mp_op("Function 'transp()'");
19057               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19058               arg1 = compile(ss7,s1,depth1,0,is_single);
19059               arg2 = compile(++s1,se1,depth1,0,is_single);
19060               _cimg_mp_check_type(arg1,1,2,0);
19061               _cimg_mp_check_constant(arg2,2,3);
19062               p1 = _cimg_mp_size(arg1);
19063               p2 = (unsigned int)mem[arg2];
19064               p3 = p1/p2;
19065               if (p2*p3!=p1) {
19066                 *se = saved_char;
19067                 s0 = ss - 4>expr._data?ss - 4:expr._data;
19068                 cimg::strellipsize(s0,64);
19069                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19070                                             "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
19071                                             "second argument 'nb_cols=%u', in expression '%s%s%s'.",
19072                                             pixel_type(),_cimg_mp_calling_function,s_op,
19073                                             s_type(arg1)._data,p2,
19074                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19075               }
19076               pos = vector(p3*p2);
19077               CImg<ulongT>::vector((ulongT)mp_transp,pos,arg1,p2,p3).move_to(code);
19078               _cimg_mp_return(pos);
19079             }
19080             break;
19081 
19082           case 'u' :
19083             if (*ss1=='(') { // Random value with uniform distribution
19084               _cimg_mp_op("Function 'u()'");
19085               if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
19086               s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19087               arg1 = compile(ss2,s1,depth1,0,is_single);
19088               if (s1<se1) arg2 = compile(++s1,se1,depth1,0,is_single); else { arg2 = arg1; arg1 = 0; }
19089               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19090               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
19091               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
19092               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
19093               _cimg_mp_scalar2(mp_u,arg1,arg2);
19094             }
19095 
19096             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
19097               _cimg_mp_op("Function 'unref()'");
19098               arg1 = ~0U;
19099               for (s0 = ss6; s0<se1; s0 = s1) {
19100                 if (s0>ss6 && *s0==',') ++s0;
19101                 s1 = s0; while (s1<se1 && *s1!=',') ++s1;
19102                 c1 = *s1;
19103                 if (s1>s0) {
19104                   *s1 = 0;
19105                   arg2 = arg3 = ~0U;
19106                   if (s0[0]=='w' && s0[1]=='h' && !s0[2]) arg1 = reserved_label[arg3 = 0];
19107                   else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && !s0[3]) arg1 = reserved_label[arg3 = 1];
19108                   else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && s0[3]=='s' && !s0[4])
19109                     arg1 = reserved_label[arg3 = 2];
19110                   else if (s0[0]=='p' && s0[1]=='i' && !s0[2]) arg1 = reserved_label[arg3 = 3];
19111                   else if (s0[0]=='i' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 4];
19112                   else if (s0[0]=='i' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 5];
19113                   else if (s0[0]=='i' && s0[1]=='a' && !s0[2]) arg1 = reserved_label[arg3 = 6];
19114                   else if (s0[0]=='i' && s0[1]=='v' && !s0[2]) arg1 = reserved_label[arg3 = 7];
19115                   else if (s0[0]=='i' && s0[1]=='s' && !s0[2]) arg1 = reserved_label[arg3 = 8];
19116                   else if (s0[0]=='i' && s0[1]=='p' && !s0[2]) arg1 = reserved_label[arg3 = 9];
19117                   else if (s0[0]=='i' && s0[1]=='c' && !s0[2]) arg1 = reserved_label[arg3 = 10];
19118                   else if (s0[0]=='x' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 11];
19119                   else if (s0[0]=='y' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 12];
19120                   else if (s0[0]=='z' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 13];
19121                   else if (s0[0]=='c' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 14];
19122                   else if (s0[0]=='x' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 15];
19123                   else if (s0[0]=='y' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 16];
19124                   else if (s0[0]=='z' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 17];
19125                   else if (s0[0]=='c' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 18];
19126                   else if (s0[0]=='i' && s0[1]>='0' && s0[1]<='9' && !s0[2])
19127                     arg1 = reserved_label[arg3 = 19 + s0[1] - '0'];
19128                   else if (!std::strcmp(s0,"interpolation")) arg1 = reserved_label[arg3 = 29];
19129                   else if (!std::strcmp(s0,"boundary")) arg1 = reserved_label[arg3 = 30];
19130                   else if (s0[1]) { // Multi-char variable
19131                     cimglist_for(variable_def,i) if (!std::strcmp(s0,variable_def[i])) {
19132                       arg1 = variable_pos[i]; arg2 = i; break;
19133                     }
19134                   } else arg1 = reserved_label[arg3 = *s0]; // Single-char variable
19135 
19136                   if (arg1!=~0U) {
19137                     if (arg2==~0U) { if (arg3!=~0U) reserved_label[arg3] = ~0U; }
19138                     else {
19139                       variable_def.remove(arg2);
19140                       if (arg2<variable_pos._width - 1)
19141                         std::memmove(variable_pos._data + arg2,variable_pos._data + arg2 + 1,
19142                                      sizeof(uintT)*(variable_pos._width - arg2 - 1));
19143                       --variable_pos._width;
19144                     }
19145                   }
19146                   *s1 = c1;
19147                 } else compile(s0,s1,depth1,0,is_single); // Will throw a 'missing argument' exception
19148               }
19149               _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable.
19150             }
19151 
19152             if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
19153               _cimg_mp_op("Function 'uppercase()'");
19154               arg1 = compile(ss + 10,se1,depth1,0,is_single);
19155               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
19156               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1]));
19157               _cimg_mp_scalar1(mp_uppercase,arg1);
19158             }
19159             break;
19160 
19161           case 'v' :
19162             if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
19163                 !std::strncmp(ss,"vector(",7) ||
19164                 (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
19165               _cimg_mp_op("Function 'vector()'");
19166               arg2 = 0; // Number of specified values.
19167               if (arg1==~0U && *ss6!='(') {
19168                 arg1 = compile(ss6,s++,depth1,0,is_single);
19169                 _cimg_mp_check_constant(arg1,0,3);
19170                 arg1 = (unsigned int)mem[arg1];
19171               } else s = std::strchr(ss6,'(') + 1;
19172 
19173               if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
19174                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19175                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19176                   arg3 = compile(s,ns,depth1,0,is_single);
19177                   if (_cimg_mp_is_vector(arg3)) {
19178                     arg4 = _cimg_mp_size(arg3);
19179                     CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(_opcode);
19180                     arg2+=arg4;
19181                   } else { CImg<ulongT>::vector(arg3).move_to(_opcode); ++arg2; }
19182                   s = ns;
19183                 }
19184               if (arg1==~0U) arg1 = arg2;
19185               _cimg_mp_check_vector0(arg1);
19186               pos = vector(arg1);
19187               _opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
19188               (_opcode>'y').move_to(opcode);
19189               opcode[2] = opcode._height;
19190               opcode.move_to(code);
19191               _cimg_mp_return(pos);
19192             }
19193 
19194             if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
19195               _cimg_mp_op("Function 'vtos()'");
19196               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19197               arg1 = compile(ss5,s1,depth1,0,is_single);
19198               arg2 = 0; arg3 = ~0U;
19199               if (s1<se1) {
19200                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19201                 arg2 = compile(++s1,s2,depth1,0,is_single);
19202                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
19203               }
19204               _cimg_mp_check_type(arg2,2,1,0);
19205               if (arg3==~0U) { // Auto-guess best output vector size
19206                 p1 = _cimg_mp_size(arg1);
19207                 p1 = p1?19*p1 - 1:18;
19208               } else {
19209                 _cimg_mp_check_constant(arg3,3,3);
19210                 p1 = (unsigned int)mem[arg3];
19211               }
19212               pos = vector(p1);
19213               CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
19214               _cimg_mp_return(pos);
19215             }
19216             break;
19217 
19218           case 'w' :
19219             if (*ss1=='(') { // Image width
19220               _cimg_mp_op("Function 'w()'");
19221               if (*ss2=='#') { // Index specified
19222                 p1 = compile(ss3,se1,depth1,0,is_single);
19223                 _cimg_mp_check_list(false);
19224               } else { if (ss2!=se1) break; p1 = ~0U; }
19225               pos = scalar();
19226               CImg<ulongT>::vector((ulongT)mp_image_w,pos,p1).move_to(code);
19227               _cimg_mp_return(pos);
19228             }
19229 
19230             if (*ss1=='h' && *ss2=='(') { // Image width*height
19231               _cimg_mp_op("Function 'wh()'");
19232               if (*ss3=='#') { // Index specified
19233                 p1 = compile(ss4,se1,depth1,0,is_single);
19234                 _cimg_mp_check_list(false);
19235               } else { if (ss3!=se1) break; p1 = ~0U; }
19236               pos = scalar();
19237               CImg<ulongT>::vector((ulongT)mp_image_wh,pos,p1).move_to(code);
19238               _cimg_mp_return(pos);
19239             }
19240 
19241             if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
19242               _cimg_mp_op("Function 'whd()'");
19243               if (*ss4=='#') { // Index specified
19244                 p1 = compile(ss5,se1,depth1,0,is_single);
19245                 _cimg_mp_check_list(false);
19246               } else { if (ss4!=se1) break; p1 = ~0U; }
19247               pos = scalar();
19248               CImg<ulongT>::vector((ulongT)mp_image_whd,pos,p1).move_to(code);
19249               _cimg_mp_return(pos);
19250             }
19251 
19252             if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
19253               _cimg_mp_op("Function 'whds()'");
19254               if (*ss5=='#') { // Index specified
19255                 p1 = compile(ss6,se1,depth1,0,is_single);
19256                 _cimg_mp_check_list(false);
19257               } else { if (ss5!=se1) break; p1 = ~0U; }
19258               pos = scalar();
19259               CImg<ulongT>::vector((ulongT)mp_image_whds,pos,p1).move_to(code);
19260               _cimg_mp_return(pos);
19261             }
19262 
19263             if (!std::strncmp(ss,"while(",6) || !std::strncmp(ss,"whiledo(",8)) { // While...do
19264               _cimg_mp_op("Function 'whiledo()'");
19265               s0 = *ss5=='('?ss6:ss8;
19266               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19267               p1 = code._width;
19268               arg1 = compile(s0,s1,depth1,0,is_single);
19269               p2 = code._width;
19270               arg6 = mempos;
19271               pos = compile(++s1,se1,depth1,0,is_single);
19272               _cimg_mp_check_type(arg1,1,1,0);
19273               arg2 = _cimg_mp_size(pos);
19274               CImg<ulongT>::vector((ulongT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2,
19275                                    pos>=arg6 && !_cimg_mp_is_constant(pos),
19276                                    arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1);
19277               _cimg_mp_return(pos);
19278             }
19279             break;
19280 
19281           case 'x' :
19282             if (!std::strncmp(ss,"xor(",4)) { // Xor
19283               _cimg_mp_op("Function 'xor()'");
19284               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19285               arg1 = compile(ss4,s1,depth1,0,is_single);
19286               arg2 = compile(++s1,se1,depth1,0,is_single);
19287               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19288               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
19289               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
19290               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
19291               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
19292                 _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]);
19293               _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
19294             }
19295             break;
19296           }
19297 
19298           if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) ||
19299               !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
19300               !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
19301               !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"variance(",9) ||
19302               !std::strncmp(ss,"prod(",5) || !std::strncmp(ss,"mean(",5) ||
19303               !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
19304               !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
19305             _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
19306                                   ss[3]=='k'?"Function 'argkth()'":
19307                                   ss[4]=='i'?"Function 'argmin()'":
19308                                   "Function 'argmax()'"):
19309                         *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
19310                         *ss=='k'?"Function 'kth()'":
19311                         *ss=='p'?"Function 'prod()'":
19312                         *ss=='v'?"Function 'variance()'":
19313                         ss[1]=='i'?"Function 'min()'":
19314                         ss[1]=='a'?"Function 'max()'":
19315                         ss[2]=='a'?"Function 'mean()'":"Function 'med()'");
19316             op = *ss=='a'?(ss[1]=='v'?mp_avg:ss[3]=='k'?mp_argkth:ss[4]=='i'?mp_argmin:mp_argmax):
19317               *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
19318               *ss=='k'?mp_kth:
19319               *ss=='p'?mp_prod:
19320               *ss=='v'?mp_variance:
19321               ss[1]=='i'?mp_min:
19322               ss[1]=='a'?mp_max:
19323               ss[2]=='a'?mp_mean:
19324               mp_median;
19325             is_sth = true; // Tell if all arguments are constant
19326             pos = scalar();
19327             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(_opcode);
19328             for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
19329               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19330                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19331               arg2 = compile(s,ns,depth1,0,is_single);
19332               if (_cimg_mp_is_vector(arg2))
19333                 CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19334                                        arg2 + (ulongT)_cimg_mp_size(arg2)).
19335                   move_to(_opcode);
19336               else CImg<ulongT>::vector(arg2).move_to(_opcode);
19337               is_sth&=_cimg_mp_is_constant(arg2);
19338               s = ns;
19339             }
19340             (_opcode>'y').move_to(opcode);
19341             opcode[2] = opcode._height;
19342             if (is_sth) _cimg_mp_constant(op(*this));
19343             opcode.move_to(code);
19344             _cimg_mp_return(pos);
19345           }
19346 
19347           // No corresponding built-in function -> Look for a user-defined macro call.
19348           s0 = strchr(ss,'(');
19349           if (s0) {
19350             variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
19351 
19352             // Count number of specified arguments.
19353             p1 = 0;
19354             for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
19355               while (*s && (signed char)*s<=' ') ++s;
19356               if (*s==')' && !p1) break;
19357               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19358                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19359             }
19360 
19361             arg3 = 0; // Number of possible name matches
19362             cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
19363                                           macro_def[l].back()==(char)p1) {
19364               p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
19365               CImg<charT> _expr = macro_body[l]; // Expression to be substituted
19366 
19367               p1 = 1; // Indice of current parsed argument
19368               for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
19369                 while (*s && (signed char)*s<=' ') ++s;
19370                 if (*s==')' && p1==1) break; // Function has no arguments
19371                 if (p1>p2) { ++p1; break; }
19372                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19373                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19374                 variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
19375                 arg2 = 0;
19376                 cimg_forX(_expr,k) {
19377                   if (_expr[k]==(char)p1) { // Perform argument substitution
19378                     arg1 = _expr._width;
19379                     _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
19380                     std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
19381                     std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
19382                     k+=variable_name._width - 2;
19383                   }
19384                   ++arg2;
19385                 }
19386               }
19387 
19388               // Recompute 'pexpr' and 'level' for evaluating substituted expression.
19389               CImg<charT> _pexpr(_expr._width);
19390               ns = _pexpr._data;
19391               for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
19392                 if ((signed char)*ps>' ') c1 = *ps;
19393                 *(ns++) = c1;
19394               }
19395               *ns = 0;
19396 
19397               CImg<uintT> _level = get_level(_expr);
19398               expr.swap(_expr);
19399               pexpr.swap(_pexpr);
19400               level.swap(_level);
19401               s0 = user_macro;
19402               user_macro = macro_def[l];
19403               pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_single);
19404               user_macro = s0;
19405               level.swap(_level);
19406               pexpr.swap(_pexpr);
19407               expr.swap(_expr);
19408               _cimg_mp_return(pos);
19409             }
19410 
19411             if (arg3) { // Macro name matched but number of arguments does not
19412               CImg<uintT> sig_nargs(arg3);
19413               arg1 = 0;
19414               cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
19415                 sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
19416               *se = saved_char;
19417               cimg::strellipsize(variable_name,64);
19418               s0 = ss - 4>expr._data?ss - 4:expr._data;
19419               cimg::strellipsize(s0,64);
19420               if (sig_nargs._width>1) {
19421                 sig_nargs.sort();
19422                 arg1 = sig_nargs.back();
19423                 --sig_nargs._width;
19424                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19425                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
19426                                             "does not match macro declaration (defined for %s or %u arguments), "
19427                                             "in expression '%s%s%s'.",
19428                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
19429                                             p1,sig_nargs.value_string()._data,arg1,
19430                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19431               } else
19432                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19433                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
19434                                             "does not match macro declaration (defined for %u argument%s), "
19435                                             "in expression '%s%s%s'.",
19436                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
19437                                             p1,*sig_nargs,*sig_nargs!=1?"s":"",
19438                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19439             }
19440           }
19441         } // if (se1==')')
19442 
19443         // Char / string initializer.
19444         if (*se1=='\'' &&
19445             ((se1>ss && *ss=='\'') ||
19446             (se1>ss1 && *ss=='_' && *ss1=='\''))) {
19447           if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
19448           else { _cimg_mp_op("String initializer"); s1 = ss1; }
19449           arg1 = (unsigned int)(se1 - s1); // Original string length.
19450           if (arg1) {
19451             CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
19452             cimg::strunescape(variable_name);
19453             arg1 = (unsigned int)std::strlen(variable_name);
19454           }
19455           if (!arg1) _cimg_mp_return(0); // Empty string -> 0
19456           if (*ss=='_') {
19457             if (arg1==1) _cimg_mp_constant(*variable_name);
19458             *se = saved_char;
19459             cimg::strellipsize(variable_name,64);
19460             s0 = ss - 4>expr._data?ss - 4:expr._data;
19461             cimg::strellipsize(s0,64);
19462             throw CImgArgumentException("[" cimg_appname "_math_parser] "
19463                                         "CImg<%s>::%s: %s: Literal %s contains more than one character, "
19464                                         "in expression '%s%s%s'.",
19465                                         pixel_type(),_cimg_mp_calling_function,s_op,
19466                                         ss1,
19467                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19468           }
19469           pos = vector(arg1);
19470           CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode);
19471           CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode);
19472           std::memcpy((char*)_opcode[1]._data,variable_name,arg1);
19473           (_opcode>'y').move_to(code);
19474           _cimg_mp_return(pos);
19475         }
19476 
19477         // Vector initializer [ ... ].
19478         if (*ss=='[' && *se1==']') {
19479           _cimg_mp_op("Vector initializer");
19480           s1 = ss1; while (s1<se2 && (signed char)*s1<=' ') ++s1;
19481           s2 = se2; while (s2>s1 && (signed char)*s2<=' ') --s2;
19482           if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
19483             arg1 = (unsigned int)(s2 - s1 - 1); // Original string length.
19484             if (arg1) {
19485               CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
19486               cimg::strunescape(variable_name);
19487               arg1 = (unsigned int)std::strlen(variable_name);
19488             }
19489             if (!arg1) _cimg_mp_return(0); // Empty string -> 0
19490             pos = vector(arg1);
19491             CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode);
19492             CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode);
19493             std::memcpy((char*)_opcode[1]._data,variable_name,arg1);
19494             (_opcode>'y').move_to(code);
19495           } else { // Vector values provided as list of items
19496             arg1 = 0; // Number of specified values.
19497             if (*ss1!=']') for (s = ss1; s<se; ++s) {
19498                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19499                                (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
19500                 arg2 = compile(s,ns,depth1,0,is_single);
19501                 if (_cimg_mp_is_vector(arg2)) {
19502                   arg3 = _cimg_mp_size(arg2);
19503                   CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(_opcode);
19504                   arg1+=arg3;
19505                 } else { CImg<ulongT>::vector(arg2).move_to(_opcode); ++arg1; }
19506                 s = ns;
19507               }
19508             _cimg_mp_check_vector0(arg1);
19509             pos = vector(arg1);
19510             _opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
19511             (_opcode>'y').move_to(opcode);
19512             opcode[2] = opcode._height;
19513             opcode.move_to(code);
19514           }
19515           _cimg_mp_return(pos);
19516         }
19517 
19518         // Variables related to the input list of images.
19519         if (*ss1=='#' && ss2<se) {
19520           arg1 = compile(ss2,se,depth1,0,is_single);
19521           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19522           switch (*ss) {
19523           case 'w' : // w#ind
19524             if (!listin) _cimg_mp_return(0);
19525             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width);
19526             _cimg_mp_scalar1(mp_list_width,arg1);
19527           case 'h' : // h#ind
19528             if (!listin) _cimg_mp_return(0);
19529             if (p1!=~0U) _cimg_mp_constant(listin[p1]._height);
19530             _cimg_mp_scalar1(mp_list_height,arg1);
19531           case 'd' : // d#ind
19532             if (!listin) _cimg_mp_return(0);
19533             if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth);
19534             _cimg_mp_scalar1(mp_list_depth,arg1);
19535           case 'r' : // r#ind
19536             if (!listin) _cimg_mp_return(0);
19537             if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared);
19538             _cimg_mp_scalar1(mp_list_is_shared,arg1);
19539           case 's' : // s#ind
19540             if (!listin) _cimg_mp_return(0);
19541             if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum);
19542             _cimg_mp_scalar1(mp_list_spectrum,arg1);
19543           case 'i' : // i#ind
19544             if (!listin) _cimg_mp_return(0);
19545             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
19546                              0,_cimg_mp_boundary);
19547           case 'I' : // I#ind
19548             p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
19549             _cimg_mp_check_vector0(p2);
19550             pos = vector(p2);
19551             CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
19552             _cimg_mp_return(pos);
19553           case 'R' : // R#ind
19554             if (!listin) _cimg_mp_return(0);
19555             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
19556                              0,_cimg_mp_boundary);
19557           case 'G' : // G#ind
19558             if (!listin) _cimg_mp_return(0);
19559             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
19560                              0,_cimg_mp_boundary);
19561           case 'B' : // B#ind
19562             if (!listin) _cimg_mp_return(0);
19563             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
19564                              0,_cimg_mp_boundary);
19565           case 'A' : // A#ind
19566             if (!listin) _cimg_mp_return(0);
19567             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
19568                              0,_cimg_mp_boundary);
19569           }
19570         }
19571 
19572         if (*ss1 && *ss2=='#' && ss3<se) {
19573           arg1 = compile(ss3,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           if (*ss=='w' && *ss1=='h') { // wh#ind
19576             if (!listin) _cimg_mp_return(0);
19577             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
19578             _cimg_mp_scalar1(mp_list_wh,arg1);
19579           }
19580           arg2 = ~0U;
19581 
19582           if (*ss=='i') {
19583             if (*ss1=='c') { // ic#ind
19584               if (!listin) _cimg_mp_return(0);
19585               if (_cimg_mp_is_constant(arg1)) {
19586                 if (!list_median) list_median.assign(listin._width);
19587                 if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
19588                 _cimg_mp_constant(*list_median[p1]);
19589               }
19590               _cimg_mp_scalar1(mp_list_median,arg1);
19591             }
19592             if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
19593               if (!listin) _cimg_mp_return(0);
19594               _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
19595                                0,_cimg_mp_boundary);
19596             }
19597             switch (*ss1) {
19598             case 'm' : arg2 = 0; break; // im#ind
19599             case 'M' : arg2 = 1; break; // iM#ind
19600             case 'a' : arg2 = 2; break; // ia#ind
19601             case 'v' : arg2 = 3; break; // iv#ind
19602             case 's' : arg2 = 12; break; // is#ind
19603             case 'p' : arg2 = 13; break; // ip#ind
19604             }
19605           } else if (*ss1=='m') switch (*ss) {
19606             case 'x' : arg2 = 4; break; // xm#ind
19607             case 'y' : arg2 = 5; break; // ym#ind
19608             case 'z' : arg2 = 6; break; // zm#ind
19609             case 'c' : arg2 = 7; break; // cm#ind
19610             } else if (*ss1=='M') switch (*ss) {
19611             case 'x' : arg2 = 8; break; // xM#ind
19612             case 'y' : arg2 = 9; break; // yM#ind
19613             case 'z' : arg2 = 10; break; // zM#ind
19614             case 'c' : arg2 = 11; break; // cM#ind
19615             }
19616           if (arg2!=~0U) {
19617             if (!listin) _cimg_mp_return(0);
19618             if (_cimg_mp_is_constant(arg1)) {
19619               if (!list_stats) list_stats.assign(listin._width);
19620               if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
19621               _cimg_mp_constant(list_stats(p1,arg2));
19622             }
19623             _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
19624           }
19625         }
19626 
19627         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
19628           arg1 = compile(ss4,se,depth1,0,is_single);
19629           if (!listin) _cimg_mp_return(0);
19630           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19631           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
19632           _cimg_mp_scalar1(mp_list_whd,arg1);
19633         }
19634         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
19635           arg1 = compile(ss5,se,depth1,0,is_single);
19636           if (!listin) _cimg_mp_return(0);
19637           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19638           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
19639           _cimg_mp_scalar1(mp_list_whds,arg1);
19640         }
19641 
19642         if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
19643         if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
19644 
19645         // No known item found, assuming this is an already initialized variable.
19646         variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0;
19647         if (variable_name[1]) { // Multi-char variable
19648           cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i]))
19649             _cimg_mp_return(variable_pos[i]);
19650         } else if (reserved_label[*variable_name]!=~0U) // Single-char variable
19651           _cimg_mp_return(reserved_label[*variable_name]);
19652 
19653         // Reached an unknown item -> error.
19654         is_sth = true; // is_valid_variable_name
19655         if (*variable_name>='0' && *variable_name<='9') is_sth = false;
19656         else for (ns = variable_name._data; *ns; ++ns)
19657                if (!is_varchar(*ns)) { is_sth = false; break; }
19658 
19659         *se = saved_char;
19660         c1 = *se1;
19661         cimg::strellipsize(variable_name,64);
19662         s0 = ss - 4>expr._data?ss - 4:expr._data;
19663         cimg::strellipsize(s0,64);
19664         if (is_sth)
19665           throw CImgArgumentException("[" cimg_appname "_math_parser] "
19666                                       "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.",
19667                                       pixel_type(),_cimg_mp_calling_function,
19668                                       variable_name._data,
19669                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19670         s1 = std::strchr(ss,'(');
19671         s_op = s1 && c1==')'?"function call":"item";
19672         throw CImgArgumentException("[" cimg_appname "_math_parser] "
19673                                     "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.",
19674                                     pixel_type(),_cimg_mp_calling_function,
19675                                     s_op,variable_name._data,
19676                                     s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19677       }
19678 
19679       // Evaluation procedure.
19680       double operator()(const double x, const double y, const double z, const double c) {
19681         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
19682         for (p_code = code; p_code<p_code_end; ++p_code) {
19683           opcode._data = p_code->_data;
19684           const ulongT target = opcode[1];
19685           mem[target] = _cimg_mp_defunc(*this);
19686         }
19687         return *result;
19688       }
19689 
19690       // Evaluation procedure (return output values in vector 'output').
19691       template<typename t>
19692       void operator()(const double x, const double y, const double z, const double c, t *const output) {
19693         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
19694         for (p_code = code; p_code<p_code_end; ++p_code) {
19695           opcode._data = p_code->_data;
19696           const ulongT target = opcode[1];
19697           mem[target] = _cimg_mp_defunc(*this);
19698         }
19699         if (result_dim) {
19700           const double *ptrs = result + 1;
19701           t *ptrd = output;
19702           for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
19703         } else *output = (t)*result;
19704       }
19705 
19706       // Evaluation procedure for the end() blocks.
19707       void end() {
19708         if (code_end.is_empty()) return;
19709         if (imgin) {
19710           mem[_cimg_mp_slot_x] = imgin._width - 1.0;
19711           mem[_cimg_mp_slot_y] = imgin._height - 1.0f;
19712           mem[_cimg_mp_slot_z] = imgin._depth - 1.0f;
19713           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.0f;
19714         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
19715         p_code_end = code_end.end();
19716         for (p_code = code_end; p_code<p_code_end; ++p_code) {
19717           opcode._data = p_code->_data;
19718           const ulongT target = opcode[1];
19719           mem[target] = _cimg_mp_defunc(*this);
19720         }
19721       }
19722 
19723       // Return type of a memory element as a string.
19724       CImg<charT> s_type(const unsigned int arg) const {
19725         CImg<charT> res;
19726         if (_cimg_mp_is_vector(arg)) { // Vector
19727           CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
19728           std::sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
19729         } else CImg<charT>::string("scalar").move_to(res);
19730         return res;
19731       }
19732 
19733       // Insert constant value in memory.
19734       unsigned int constant(const double val) {
19735 
19736         // Search for built-in constant.
19737         if (val==(double)(int)val) {
19738           if (val>=0 && val<=10) return (unsigned int)val;
19739           if (val<0 && val>=-5) return (unsigned int)(10 - val);
19740         }
19741         if (val==0.5) return 16;
19742         if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
19743 
19744         // Search for constant already requested before (in const cache).
19745         unsigned int ind = ~0U;
19746         if (constcache_size<1024) {
19747           if (!constcache_size) {
19748             constcache_vals.assign(16,1,1,1,0);
19749             constcache_inds.assign(16,1,1,1,0);
19750             *constcache_vals = val;
19751             constcache_size = 1;
19752             ind = 0;
19753           } else { // Dichotomic search
19754             const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
19755             if (val_beg>=val) ind = 0;
19756             else if (val_end==val) ind = constcache_size - 1;
19757             else if (val_end<val) ind = constcache_size;
19758             else {
19759               unsigned int i0 = 1, i1 = constcache_size - 2;
19760               while (i0<=i1) {
19761                 const unsigned int mid = (i0 + i1)/2;
19762                 if (constcache_vals[mid]==val) { i0 = mid; break; }
19763                 else if (constcache_vals[mid]<val) i0 = mid + 1;
19764                 else i1 = mid - 1;
19765               }
19766               ind = i0;
19767             }
19768 
19769             if (ind>=constcache_size || constcache_vals[ind]!=val) {
19770               ++constcache_size;
19771               if (constcache_size>constcache_vals._width) {
19772                 constcache_vals.resize(-200,1,1,1,0);
19773                 constcache_inds.resize(-200,1,1,1,0);
19774               }
19775               const int l = constcache_size - (int)ind - 1;
19776               if (l>0) {
19777                 std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
19778                 std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
19779               }
19780               constcache_vals[ind] = val;
19781               constcache_inds[ind] = 0;
19782             }
19783           }
19784           if (constcache_inds[ind]) return constcache_inds[ind];
19785         }
19786 
19787         // Insert new constant in memory if necessary.
19788         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
19789         const unsigned int pos = mempos++;
19790         mem[pos] = val;
19791         memtype[pos] = 1; // Set constant property
19792         if (ind!=~0U) constcache_inds[ind] = pos;
19793         return pos;
19794       }
19795 
19796       // Insert code instructions for processing scalars.
19797       unsigned int scalar() { // Insert new scalar in memory.
19798         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
19799         return mempos++;
19800       }
19801 
19802       unsigned int scalar0(const mp_func op) {
19803         const unsigned int pos = scalar();
19804         CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
19805         return pos;
19806       }
19807 
19808       unsigned int scalar1(const mp_func op, const unsigned int arg1) {
19809         const unsigned int pos =
19810           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar();
19811         CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
19812         return pos;
19813       }
19814 
19815       unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
19816         const unsigned int pos =
19817           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19818           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar();
19819         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
19820         return pos;
19821       }
19822 
19823       unsigned int scalar3(const mp_func op,
19824                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
19825         const unsigned int pos =
19826           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19827           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19828           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar();
19829         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
19830         return pos;
19831       }
19832 
19833       unsigned int scalar4(const mp_func op,
19834                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19835                            const unsigned int arg4) {
19836         const unsigned int pos =
19837           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19838           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19839           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19840           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar();
19841         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
19842         return pos;
19843       }
19844 
19845       unsigned int scalar5(const mp_func op,
19846                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19847                            const unsigned int arg4, const unsigned int arg5) {
19848         const unsigned int pos =
19849           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19850           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19851           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19852           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19853           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar();
19854         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
19855         return pos;
19856       }
19857 
19858       unsigned int scalar6(const mp_func op,
19859                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19860                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
19861         const unsigned int pos =
19862           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19863           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19864           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19865           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19866           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
19867           arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar();
19868         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
19869         return pos;
19870       }
19871 
19872       unsigned int scalar7(const mp_func op,
19873                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19874                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
19875                            const unsigned int arg7) {
19876         const unsigned int pos =
19877           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19878           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19879           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19880           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19881           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
19882           arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
19883           arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar();
19884         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
19885         return pos;
19886       }
19887 
19888       // Return a string that defines the calling function + the user-defined function scope.
19889       CImg<charT> calling_function_s() const {
19890         CImg<charT> res;
19891         const unsigned int
19892           l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
19893           l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
19894         if (l2) {
19895           res.assign(l1 + l2 + 48);
19896           cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
19897         } else {
19898           res.assign(l1 + l2 + 4);
19899           cimg_snprintf(res,res._width,"%s()",calling_function);
19900         }
19901         return res;
19902       }
19903 
19904       // Return true if specified argument can be a part of an allowed  variable name.
19905       bool is_varchar(const char c) const {
19906         return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
19907       }
19908 
19909       // Insert code instructions for processing vectors.
19910       bool is_comp_vector(const unsigned int arg) const {
19911         unsigned int siz = _cimg_mp_size(arg);
19912         if (siz>8) return false;
19913         const int *ptr = memtype.data(arg + 1);
19914         bool is_tmp = true;
19915         while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
19916         return is_tmp;
19917       }
19918 
19919       void set_variable_vector(const unsigned int arg) {
19920         unsigned int siz = _cimg_mp_size(arg);
19921         int *ptr = memtype.data(arg + 1);
19922         while (siz-->0) *(ptr++) = -1;
19923       }
19924 
19925       unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory
19926         if (mempos + siz>=mem._width) {
19927           mem.resize(2*mem._width + siz,1,1,1,0);
19928           memtype.resize(mem._width,1,1,1,0);
19929         }
19930         const unsigned int pos = mempos++;
19931         mem[pos] = cimg::type<double>::nan();
19932         memtype[pos] = siz + 1;
19933         mempos+=siz;
19934         return pos;
19935       }
19936 
19937       unsigned int vector(const unsigned int siz, const double value) { // Insert new initialized vector
19938         const unsigned int pos = vector(siz);
19939         double *ptr = &mem[pos] + 1;
19940         for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
19941         return pos;
19942       }
19943 
19944       unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory
19945         const unsigned int
19946           siz = _cimg_mp_size(arg),
19947           pos = vector(siz);
19948         CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
19949         return pos;
19950       }
19951 
19952       void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
19953         const unsigned int siz = _cimg_mp_size(pos);
19954         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
19955         else {
19956           code.insert(siz);
19957           for (unsigned int k = 1; k<=siz; ++k)
19958             CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
19959         }
19960       }
19961 
19962       void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
19963         const unsigned int siz = _cimg_mp_size(pos);
19964         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
19965         else {
19966           code.insert(siz);
19967           for (unsigned int k = 1; k<=siz; ++k)
19968             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
19969         }
19970       }
19971 
19972       unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
19973         const unsigned int
19974           siz = _cimg_mp_size(arg1),
19975           pos = is_comp_vector(arg1)?arg1:vector(siz);
19976         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
19977         else {
19978           code.insert(siz);
19979           for (unsigned int k = 1; k<=siz; ++k)
19980             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
19981         }
19982         return pos;
19983       }
19984 
19985       unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
19986         const unsigned int
19987           siz = _cimg_mp_size(arg1),
19988           pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz);
19989         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
19990         else {
19991           code.insert(siz);
19992           for (unsigned int k = 1; k<=siz; ++k)
19993             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
19994         }
19995         return pos;
19996       }
19997 
19998       unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
19999         const unsigned int
20000           siz = _cimg_mp_size(arg1),
20001           pos = is_comp_vector(arg1)?arg1:vector(siz);
20002         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
20003         else {
20004           code.insert(siz);
20005           for (unsigned int k = 1; k<=siz; ++k)
20006             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
20007         }
20008         return pos;
20009       }
20010 
20011       unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
20012         const unsigned int
20013           siz = _cimg_mp_size(arg2),
20014           pos = is_comp_vector(arg2)?arg2:vector(siz);
20015         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
20016         else {
20017           code.insert(siz);
20018           for (unsigned int k = 1; k<=siz; ++k)
20019             CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
20020         }
20021         return pos;
20022       }
20023 
20024       unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
20025                                const unsigned int arg3) {
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_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).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,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
20034         }
20035         return pos;
20036       }
20037 
20038       // Check if a memory slot is a positive integer constant scalar value.
20039       // 'mode' can be:
20040       // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
20041       void check_constant(const unsigned int arg, const unsigned int n_arg,
20042                           const unsigned int mode,
20043                           char *const ss, char *const se, const char saved_char) {
20044         _cimg_mp_check_type(arg,n_arg,1,0);
20045         if (!(_cimg_mp_is_constant(arg) &&
20046               (!mode || (double)(int)mem[arg]==mem[arg]) &&
20047               (mode<2 || mem[arg]>=(mode==3)))) {
20048           const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
20049             n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth ":
20050             n_arg==9?"Ninth ":"One of the ";
20051           *se = saved_char;
20052           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20053           cimg::strellipsize(s0,64);
20054           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20055                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, "
20056                                       "in expression '%s%s%s'.",
20057                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20058                                       s_arg,*s_arg?"argument":"Argument",s_type(arg)._data,
20059                                       !mode?"":mode==1?"n integer":
20060                                       mode==2?" positive integer":" strictly positive integer",
20061                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20062         }
20063       }
20064 
20065       // Check a matrix is square.
20066       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
20067                                char *const ss, char *const se, const char saved_char) {
20068         _cimg_mp_check_type(arg,n_arg,2,0);
20069         const unsigned int
20070           siz = _cimg_mp_size(arg),
20071           n = (unsigned int)std::sqrt((float)siz);
20072         if (n*n!=siz) {
20073           const char *s_arg;
20074           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
20075           else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One ";
20076           *se = saved_char;
20077           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20078           cimg::strellipsize(s0,64);
20079           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20080                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') "
20081                                       "cannot be considered as a square matrix, in expression '%s%s%s'.",
20082                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20083                                       s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
20084                                       s_type(arg)._data,
20085                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20086         }
20087       }
20088 
20089       // Check type compatibility for one argument.
20090       // Bits of 'mode' tells what types are allowed:
20091       // { 1 = scalar | 2 = vectorN }.
20092       // If 'N' is not zero, it also restricts the vectors to be of size N only.
20093       void check_type(const unsigned int arg, const unsigned int n_arg,
20094                       const unsigned int mode, const unsigned int N,
20095                       char *const ss, char *const se, const char saved_char) {
20096         const bool
20097           is_scalar = _cimg_mp_is_scalar(arg),
20098           is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
20099         bool cond = false;
20100         if (mode&1) cond|=is_scalar;
20101         if (mode&2) cond|=is_vector;
20102         if (!cond) {
20103           const char *s_arg;
20104           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
20105           else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
20106                  n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth":
20107                  n_arg==9?"Ninth":"One of the ";
20108           CImg<charT> sb_type(32);
20109           if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
20110           else if (mode==2) {
20111             if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
20112             else cimg_snprintf(sb_type,sb_type._width,"'vector'");
20113           } else {
20114             if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
20115             else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
20116           }
20117           *se = saved_char;
20118           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20119           cimg::strellipsize(s0,64);
20120           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20121                                       "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
20122                                       "in expression '%s%s%s'.",
20123                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20124                                       s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
20125                                       s_type(arg)._data,sb_type._data,
20126                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20127         }
20128       }
20129 
20130       // Check that listin or listout are not empty.
20131       void check_list(const bool is_out,
20132                       char *const ss, char *const se, const char saved_char) {
20133         if ((!is_out && !listin) || (is_out && !listout)) {
20134           *se = saved_char;
20135           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20136           cimg::strellipsize(s0,64);
20137           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20138                                       "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
20139                                       "in expression '%s%s%s'.",
20140                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20141                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20142         }
20143       }
20144 
20145       // Check a vector is not 0-dimensional, or with unknown dimension at compile time.
20146       void check_vector0(const unsigned int dim,
20147                          char *const ss, char *const se, const char saved_char) {
20148         char *s0 = 0;
20149         if (!dim) {
20150           *se = saved_char;
20151           s0 = ss - 4>expr._data?ss - 4:expr._data;
20152           cimg::strellipsize(s0,64);
20153           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20154                                       "CImg<%s>::%s: %s%s Invalid construction of a 0-dimensional vector, "
20155                                       "in expression '%s%s%s'.",
20156                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20157                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20158         } else if (dim==~0U) {
20159           *se = saved_char;
20160           s0 = ss - 4>expr._data?ss - 4:expr._data;
20161           cimg::strellipsize(s0,64);
20162           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20163                                       "CImg<%s>::%s: %s%s Invalid construction of a vector with possible dynamic size, "
20164                                       "in expression '%s%s%s'.",
20165                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20166                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20167         }
20168       }
20169 
20170       // Evaluation functions, known by the parser.
20171       // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
20172       // so we can store pointers to them directly in the opcode vectors.
20173 #ifdef _mp_arg
20174 #undef _mp_arg
20175 #endif
20176 #define _mp_arg(x) mp.mem[mp.opcode[x]]
20177 
20178       static double mp_abs(_cimg_math_parser& mp) {
20179         return cimg::abs(_mp_arg(2));
20180       }
20181 
20182       static double mp_add(_cimg_math_parser& mp) {
20183         return _mp_arg(2) + _mp_arg(3);
20184       }
20185 
20186       static double mp_acos(_cimg_math_parser& mp) {
20187         return std::acos(_mp_arg(2));
20188       }
20189 
20190       static double mp_arg(_cimg_math_parser& mp) {
20191         const int _ind = (int)_mp_arg(4);
20192         const unsigned int
20193           nb_args = (unsigned int)mp.opcode[2] - 4,
20194           ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
20195           siz = (unsigned int)mp.opcode[3];
20196         if (siz>0) {
20197           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
20198           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
20199           return cimg::type<double>::nan();
20200         }
20201         if (ind>=nb_args) return 0;
20202         return _mp_arg(ind + 4);
20203       }
20204 
20205       static double mp_argkth(_cimg_math_parser& mp) {
20206         const unsigned int i_end = (unsigned int)mp.opcode[2];
20207         const double val = mp_kth(mp);
20208         for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.0;
20209         return 1;
20210       }
20211 
20212       static double mp_argmin(_cimg_math_parser& mp) {
20213         const unsigned int i_end = (unsigned int)mp.opcode[2];
20214         double val = _mp_arg(3);
20215         unsigned int argval = 0;
20216         for (unsigned int i = 4; i<i_end; ++i) {
20217           const double _val = _mp_arg(i);
20218           if (_val<val) { val = _val; argval = i - 3; }
20219         }
20220         return (double)argval;
20221       }
20222 
20223       static double mp_argmax(_cimg_math_parser& mp) {
20224         const unsigned int i_end = (unsigned int)mp.opcode[2];
20225         double val = _mp_arg(3);
20226         unsigned int argval = 0;
20227         for (unsigned int i = 4; i<i_end; ++i) {
20228           const double _val = _mp_arg(i);
20229           if (_val>val) { val = _val; argval = i - 3; }
20230         }
20231         return (double)argval;
20232       }
20233 
20234       static double mp_asin(_cimg_math_parser& mp) {
20235         return std::asin(_mp_arg(2));
20236       }
20237 
20238       static double mp_atan(_cimg_math_parser& mp) {
20239         return std::atan(_mp_arg(2));
20240       }
20241 
20242       static double mp_atan2(_cimg_math_parser& mp) {
20243         return std::atan2(_mp_arg(2),_mp_arg(3));
20244       }
20245 
20246       static double mp_avg(_cimg_math_parser& mp) {
20247         const unsigned int i_end = (unsigned int)mp.opcode[2];
20248         double val = _mp_arg(3);
20249         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
20250         return val/(i_end - 3);
20251       }
20252 
20253       static double mp_bitwise_and(_cimg_math_parser& mp) {
20254         return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
20255       }
20256 
20257       static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
20258         return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
20259       }
20260 
20261       static double mp_bitwise_not(_cimg_math_parser& mp) {
20262         // Limit result to 32bits such that it can be entirely represented as a 'double'.
20263         return (double)~(unsigned int)_mp_arg(2);
20264       }
20265 
20266       static double mp_bitwise_or(_cimg_math_parser& mp) {
20267         return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
20268       }
20269 
20270       static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
20271         return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
20272       }
20273 
20274       static double mp_bitwise_xor(_cimg_math_parser& mp) {
20275         return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
20276       }
20277 
20278       static double mp_bool(_cimg_math_parser& mp) {
20279         return (double)(bool)_mp_arg(2);
20280       }
20281 
20282       static double mp_break(_cimg_math_parser& mp) {
20283         mp.break_type = 1;
20284         mp.p_code = mp.p_break - 1;
20285         return cimg::type<double>::nan();
20286       }
20287 
20288       static double mp_breakpoint(_cimg_math_parser& mp) {
20289         cimg_abort_init;
20290         cimg_abort_test;
20291         cimg::unused(mp);
20292         return cimg::type<double>::nan();
20293       }
20294 
20295       static double mp_cats(_cimg_math_parser& mp) {
20296         const double *ptrd = &_mp_arg(1) + 1;
20297         const unsigned int
20298           sizd = (unsigned int)mp.opcode[2],
20299           nb_args = (unsigned int)(mp.opcode[3] - 4)/2;
20300         CImgList<charT> _str;
20301         for (unsigned int n = 0; n<nb_args; ++n) {
20302           const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
20303           if (siz) { // Vector argument
20304             const double *ptrs = &_mp_arg(4 + 2*n) + 1;
20305             unsigned int l = 0;
20306             while (l<siz && ptrs[l]) ++l;
20307             CImg<doubleT>(ptrs,l,1,1,1,true).move_to(_str);
20308           } else CImg<charT>::vector((char)_mp_arg(4 + 2*n)).move_to(_str); // Scalar argument
20309         }
20310         CImg(1,1,1,1,0).move_to(_str);
20311         const CImg<charT> str = _str>'x';
20312         const unsigned int l = std::min(str._width,sizd);
20313         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
20314         return cimg::type<double>::nan();
20315       }
20316 
20317       static double mp_cbrt(_cimg_math_parser& mp) {
20318         return cimg::cbrt(_mp_arg(2));
20319       }
20320 
20321       static double mp_ceil(_cimg_math_parser& mp) {
20322         return std::ceil(_mp_arg(2));
20323       }
20324 
20325       static double mp_complex_abs(_cimg_math_parser& mp) {
20326         return cimg::_hypot(_mp_arg(2),_mp_arg(3));
20327       }
20328 
20329       static double mp_complex_conj(_cimg_math_parser& mp) {
20330         const double *ptrs = &_mp_arg(2) + 1;
20331         double *ptrd = &_mp_arg(1) + 1;
20332         *(ptrd++) = *(ptrs++);
20333         *ptrd = -*(ptrs);
20334         return cimg::type<double>::nan();
20335       }
20336 
20337       static double mp_complex_div_sv(_cimg_math_parser& mp) {
20338         const double
20339           *ptr2 = &_mp_arg(3) + 1,
20340           r1 = _mp_arg(2),
20341           r2 = *(ptr2++), i2 = *ptr2;
20342         double *ptrd = &_mp_arg(1) + 1;
20343         const double denom = r2*r2 + i2*i2;
20344         *(ptrd++) = r1*r2/denom;
20345         *ptrd =  -r1*i2/denom;
20346         return cimg::type<double>::nan();
20347       }
20348 
20349       static double mp_complex_div_vv(_cimg_math_parser& mp) {
20350         const double
20351           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
20352           r1 = *(ptr1++), i1 = *ptr1,
20353           r2 = *(ptr2++), i2 = *ptr2;
20354         double *ptrd = &_mp_arg(1) + 1;
20355         const double denom = r2*r2 + i2*i2;
20356         *(ptrd++) = (r1*r2 + i1*i2)/denom;
20357         *ptrd = (r2*i1 - r1*i2)/denom;
20358         return cimg::type<double>::nan();
20359       }
20360 
20361       static double mp_complex_exp(_cimg_math_parser& mp) {
20362         double *ptrd = &_mp_arg(1) + 1;
20363         const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r);
20364         *(ptrd++) = er*std::cos(i);
20365         *(ptrd++) = er*std::sin(i);
20366         return cimg::type<double>::nan();
20367       }
20368 
20369       static double mp_complex_log(_cimg_math_parser& mp) {
20370         double *ptrd = &_mp_arg(1) + 1;
20371         const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs);
20372         *(ptrd++) = 0.5*std::log(r*r + i*i);
20373         *(ptrd++) = std::atan2(i,r);
20374         return cimg::type<double>::nan();
20375       }
20376 
20377       static double mp_complex_mul(_cimg_math_parser& mp) {
20378         const double
20379           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
20380           r1 = *(ptr1++), i1 = *ptr1,
20381           r2 = *(ptr2++), i2 = *ptr2;
20382         double *ptrd = &_mp_arg(1) + 1;
20383         *(ptrd++) = r1*r2 - i1*i2;
20384         *(ptrd++) = r1*i2 + r2*i1;
20385         return cimg::type<double>::nan();
20386       }
20387 
20388       static void _mp_complex_pow(const double r1, const double i1,
20389                                   const double r2, const double i2,
20390                                   double *ptrd) {
20391         double ro, io;
20392         if (cimg::abs(i2)<1e-15) { // Exponent is real
20393           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
20394             if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
20395             else ro = io = 0;
20396           } else {
20397             const double
20398               mod1_2 = r1*r1 + i1*i1,
20399               phi1 = std::atan2(i1,r1),
20400               modo = std::pow(mod1_2,0.5*r2),
20401               phio = r2*phi1;
20402             ro = modo*std::cos(phio);
20403             io = modo*std::sin(phio);
20404           }
20405         } else { // Exponent is complex
20406           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
20407           const double
20408             mod1_2 = r1*r1 + i1*i1,
20409             phi1 = std::atan2(i1,r1),
20410             modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
20411             phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
20412           ro = modo*std::cos(phio);
20413           io = modo*std::sin(phio);
20414         }
20415         *(ptrd++) = ro;
20416         *ptrd = io;
20417       }
20418 
20419       static double mp_complex_pow_ss(_cimg_math_parser& mp) {
20420         const double val1 = _mp_arg(2), val2 = _mp_arg(3);
20421         double *ptrd = &_mp_arg(1) + 1;
20422         _mp_complex_pow(val1,0,val2,0,ptrd);
20423         return cimg::type<double>::nan();
20424       }
20425 
20426       static double mp_complex_pow_sv(_cimg_math_parser& mp) {
20427         const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
20428         double *ptrd = &_mp_arg(1) + 1;
20429         _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
20430         return cimg::type<double>::nan();
20431       }
20432 
20433       static double mp_complex_pow_vs(_cimg_math_parser& mp) {
20434         const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
20435         double *ptrd = &_mp_arg(1) + 1;
20436         _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
20437         return cimg::type<double>::nan();
20438       }
20439 
20440       static double mp_complex_pow_vv(_cimg_math_parser& mp) {
20441         const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
20442         double *ptrd = &_mp_arg(1) + 1;
20443         _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
20444         return cimg::type<double>::nan();
20445       }
20446 
20447       static double mp_continue(_cimg_math_parser& mp) {
20448         mp.break_type = 2;
20449         mp.p_code = mp.p_break - 1;
20450         return cimg::type<double>::nan();
20451       }
20452 
20453       static double mp_cos(_cimg_math_parser& mp) {
20454         return std::cos(_mp_arg(2));
20455       }
20456 
20457       static double mp_cosh(_cimg_math_parser& mp) {
20458         return std::cosh(_mp_arg(2));
20459       }
20460 
20461       static double mp_critical(_cimg_math_parser& mp) {
20462         const double res = _mp_arg(1);
20463         cimg_pragma_openmp(critical(mp_critical))
20464         {
20465           for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
20466                mp.p_code<p_end; ++mp.p_code) { // Evaluate body
20467             mp.opcode._data = mp.p_code->_data;
20468             const ulongT target = mp.opcode[1];
20469             mp.mem[target] = _cimg_mp_defunc(mp);
20470           }
20471         }
20472         --mp.p_code;
20473         return res;
20474       }
20475 
20476       static double mp_crop(_cimg_math_parser& mp) {
20477         double *ptrd = &_mp_arg(1) + 1;
20478         const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
20479         const unsigned int
20480           dx = (unsigned int)mp.opcode[7],
20481           dy = (unsigned int)mp.opcode[8],
20482           dz = (unsigned int)mp.opcode[9],
20483           dc = (unsigned int)mp.opcode[10];
20484         const bool boundary_conditions = (bool)_mp_arg(11);
20485         unsigned int ind = (unsigned int)mp.opcode[2];
20486         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
20487         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
20488         if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
20489         else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
20490                                                                  x + dx - 1,y + dy - 1,
20491                                                                  z + dz - 1,c + dc - 1,
20492                                                                  boundary_conditions);
20493         return cimg::type<double>::nan();
20494       }
20495 
20496       static double mp_cross(_cimg_math_parser& mp) {
20497         CImg<doubleT>
20498           vout(&_mp_arg(1) + 1,1,3,1,1,true),
20499           v1(&_mp_arg(2) + 1,1,3,1,1,true),
20500           v2(&_mp_arg(3) + 1,1,3,1,1,true);
20501         (vout = v1).cross(v2);
20502         return cimg::type<double>::nan();
20503       }
20504 
20505       static double mp_cut(_cimg_math_parser& mp) {
20506         double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
20507         return val<cmin?cmin:val>cmax?cmax:val;
20508       }
20509 
20510       static double mp_date(_cimg_math_parser& mp) {
20511         const unsigned int
20512           _arg = (unsigned int)mp.opcode[3],
20513           _siz = (unsigned int)mp.opcode[4],
20514           siz = _siz?_siz:1;
20515         const double *const arg_in = _arg==~0U?0:&_mp_arg(3) + (_siz?1:0);
20516         double *const arg_out = &_mp_arg(1) + (_siz?1:0);
20517         if (arg_in) std::memcpy(arg_out,arg_in,siz*sizeof(double));
20518         else for (unsigned int i = 0; i<siz; ++i) arg_out[i] = i;
20519 
20520         CImg<charT> filename(mp.opcode[2] - 5);
20521         if (filename) {
20522           const ulongT *ptrs = mp.opcode._data + 5;
20523           cimg_for(filename,ptrd,char) *ptrd = (char)*(ptrs++);
20524           cimg::fdate(filename,arg_out,siz);
20525         } else cimg::date(arg_out,siz);
20526         return _siz?cimg::type<double>::nan():*arg_out;
20527       }
20528 
20529       static double mp_debug(_cimg_math_parser& mp) {
20530         CImg<charT> expr(mp.opcode[2] - 4);
20531         const ulongT *ptrs = mp.opcode._data + 4;
20532         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
20533         cimg::strellipsize(expr);
20534         const ulongT g_target = mp.opcode[1];
20535 
20536 #ifndef cimg_use_openmp
20537         const unsigned int n_thread = 0;
20538 #else
20539         const unsigned int n_thread = omp_get_thread_num();
20540 #endif
20541         cimg_pragma_openmp(critical(mp_debug))
20542         {
20543           std::fprintf(cimg::output(),
20544                        "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20545                        "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
20546                        (void*)&mp,n_thread,mp.debug_indent,' ',
20547                        expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
20548           std::fflush(cimg::output());
20549           mp.debug_indent+=3;
20550         }
20551         const CImg<ulongT> *const p_end = (++mp.p_code) + mp.opcode[3];
20552         CImg<ulongT> _op;
20553         for ( ; mp.p_code<p_end; ++mp.p_code) {
20554           const CImg<ulongT> &op = *mp.p_code;
20555           mp.opcode._data = op._data;
20556 
20557           _op.assign(1,op._height - 1);
20558           const ulongT *ptrs = op._data + 1;
20559           for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
20560             *ptrd = *(ptrs++);
20561 
20562           const ulongT target = mp.opcode[1];
20563           mp.mem[target] = _cimg_mp_defunc(mp);
20564           cimg_pragma_openmp(critical(mp_debug))
20565           {
20566             std::fprintf(cimg::output(),
20567                          "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20568                          "Opcode %p = [ %p,%s ] -> mem[%u] = %g",
20569                          (void*)&mp,n_thread,mp.debug_indent,' ',
20570                          (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
20571                          (unsigned int)target,mp.mem[target]);
20572             std::fflush(cimg::output());
20573           }
20574         }
20575         cimg_pragma_openmp(critical(mp_debug))
20576         {
20577           mp.debug_indent-=3;
20578           std::fprintf(cimg::output(),
20579             "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20580             "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)",
20581             (void*)&mp,n_thread,mp.debug_indent,' ',
20582             expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
20583           std::fflush(cimg::output());
20584         }
20585         --mp.p_code;
20586         return mp.mem[g_target];
20587       }
20588 
20589       static double mp_decrement(_cimg_math_parser& mp) {
20590         return _mp_arg(2) - 1;
20591       }
20592 
20593       static double mp_det(_cimg_math_parser& mp) {
20594         const double *ptrs = &_mp_arg(2) + 1;
20595         const unsigned int k = (unsigned int)mp.opcode[3];
20596         return CImg<doubleT>(ptrs,k,k,1,1,true).det();
20597       }
20598 
20599       static double mp_diag(_cimg_math_parser& mp) {
20600         double *ptrd = &_mp_arg(1) + 1;
20601         const double *ptrs = &_mp_arg(2) + 1;
20602         const unsigned int k = (unsigned int)mp.opcode[3];
20603         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptrs,1,k,1,1,true).get_diagonal();
20604         return cimg::type<double>::nan();
20605       }
20606 
20607       static double mp_display_memory(_cimg_math_parser& mp) {
20608         cimg::unused(mp);
20609         std::fputc('\n',cimg::output());
20610         mp.mem.display("[" cimg_appname "_math_parser] Memory snapshot");
20611         return cimg::type<double>::nan();
20612       }
20613 
20614       static double mp_display(_cimg_math_parser& mp) {
20615         const unsigned int
20616           _siz = (unsigned int)mp.opcode[3],
20617           siz = _siz?_siz:1;
20618         const double *const ptr = &_mp_arg(1) + (_siz?1:0);
20619         const int
20620           w = (int)_mp_arg(4),
20621           h = (int)_mp_arg(5),
20622           d = (int)_mp_arg(6),
20623           s = (int)_mp_arg(7);
20624         CImg<doubleT> img;
20625         if (w>0 && h>0 && d>0 && s>0) {
20626           if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
20627           else img.assign(ptr,siz).resize(w,h,d,s,-1);
20628         } else img.assign(ptr,1,siz,1,1,true);
20629 
20630         CImg<charT> expr(mp.opcode[2] - 8);
20631         const ulongT *ptrs = mp.opcode._data + 8;
20632         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
20633         ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
20634         cimg::strellipsize(expr);
20635         std::fputc('\n',cimg::output());
20636         img.display(expr._data);
20637         return cimg::type<double>::nan();
20638       }
20639 
20640       static double mp_div(_cimg_math_parser& mp) {
20641         return _mp_arg(2)/_mp_arg(3);
20642       }
20643 
20644       static double mp_dot(_cimg_math_parser& mp) {
20645         const unsigned int siz = (unsigned int)mp.opcode[4];
20646         return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
20647           dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
20648       }
20649 
20650       static double mp_dowhile(_cimg_math_parser& mp) {
20651         const ulongT
20652           mem_body = mp.opcode[1],
20653           mem_cond = mp.opcode[2];
20654         const CImg<ulongT>
20655           *const p_body = ++mp.p_code,
20656           *const p_cond = p_body + mp.opcode[3],
20657           *const p_end = p_cond + mp.opcode[4];
20658         const unsigned int vsiz = (unsigned int)mp.opcode[5];
20659         if (mp.opcode[6]) { // Set default value for result and condition if necessary
20660           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
20661           else mp.mem[mem_body] = cimg::type<double>::nan();
20662         }
20663         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
20664 
20665         const unsigned int _break_type = mp.break_type;
20666         mp.break_type = 0;
20667         do {
20668           for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
20669             mp.opcode._data = mp.p_code->_data;
20670             const ulongT target = mp.opcode[1];
20671             mp.mem[target] = _cimg_mp_defunc(mp);
20672           }
20673           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20674           for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
20675             mp.opcode._data = mp.p_code->_data;
20676             const ulongT target = mp.opcode[1];
20677             mp.mem[target] = _cimg_mp_defunc(mp);
20678           }
20679           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20680         } while (mp.mem[mem_cond]);
20681         mp.break_type = _break_type;
20682         mp.p_code = p_end - 1;
20683         return mp.mem[mem_body];
20684       }
20685 
20686       static double mp_draw(_cimg_math_parser& mp) {
20687         const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
20688         unsigned int ind = (unsigned int)mp.opcode[3];
20689 
20690         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
20691         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
20692         unsigned int
20693           dx = (unsigned int)mp.opcode[8],
20694           dy = (unsigned int)mp.opcode[9],
20695           dz = (unsigned int)mp.opcode[10],
20696           dc = (unsigned int)mp.opcode[11];
20697         dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
20698         dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
20699         dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
20700         dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
20701 
20702         const ulongT sizS = mp.opcode[2];
20703         if (sizS<(ulongT)dx*dy*dz*dc)
20704           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
20705                                       "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
20706                                       "(%lu values) do not match.",
20707                                       mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
20708         CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
20709         const float opacity = (float)_mp_arg(12);
20710 
20711         if (img._data) {
20712           if (mp.opcode[13]!=~0U) { // Opacity mask specified
20713             const ulongT sizM = mp.opcode[14];
20714             if (sizM<(ulongT)dx*dy*dz)
20715               throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
20716                                           "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
20717                                           "(%lu values) do not match.",
20718                                           mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
20719             const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
20720             img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
20721           } else img.draw_image(x,y,z,c,S,opacity);
20722         }
20723         return cimg::type<double>::nan();
20724       }
20725 
20726       static double mp_echo(_cimg_math_parser& mp) {
20727         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
20728         CImgList<charT> _str;
20729         CImg<charT> it;
20730         for (unsigned int n = 0; n<nb_args; ++n) {
20731           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
20732           if (siz) { // Vector argument -> string
20733             const double *ptr = &_mp_arg(3 + 2*n) + 1;
20734             unsigned int l = 0;
20735             while (l<siz && ptr[l]) ++l;
20736             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
20737           } else { // Scalar argument -> number
20738             it.assign(256);
20739             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
20740             CImg<charT>::string(it,false,true).move_to(_str);
20741           }
20742         }
20743         CImg(1,1,1,1,0).move_to(_str);
20744         const CImg<charT> str = _str>'x';
20745         std::fprintf(cimg::output(),"\n%s",str._data);
20746         return cimg::type<double>::nan();
20747       }
20748 
20749       static double mp_eq(_cimg_math_parser& mp) {
20750         return (double)(_mp_arg(2)==_mp_arg(3));
20751       }
20752 
20753       static double mp_ext(_cimg_math_parser& mp) {
20754         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
20755         CImgList<charT> _str;
20756         CImg<charT> it;
20757         for (unsigned int n = 0; n<nb_args; ++n) {
20758           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
20759           if (siz) { // Vector argument -> string
20760             const double *ptr = &_mp_arg(3 + 2*n) + 1;
20761             unsigned int l = 0;
20762             while (l<siz && ptr[l]) ++l;
20763             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
20764           } else { // Scalar argument -> number
20765             it.assign(256);
20766             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
20767             CImg<charT>::string(it,false,true).move_to(_str);
20768           }
20769         }
20770         CImg(1,1,1,1,0).move_to(_str);
20771         CImg<charT> str = _str>'x';
20772 #ifdef cimg_mp_ext_function
20773         cimg_mp_ext_function(str);
20774 #endif
20775         return cimg::type<double>::nan();
20776       }
20777 
20778       static double mp_exp(_cimg_math_parser& mp) {
20779         return std::exp(_mp_arg(2));
20780       }
20781 
20782       static double mp_eye(_cimg_math_parser& mp) {
20783         double *ptrd = &_mp_arg(1) + 1;
20784         const unsigned int k = (unsigned int)mp.opcode[2];
20785         CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
20786         return cimg::type<double>::nan();
20787       }
20788 
20789       static double mp_factorial(_cimg_math_parser& mp) {
20790         return cimg::factorial(_mp_arg(2));
20791       }
20792 
20793       static double mp_fibonacci(_cimg_math_parser& mp) {
20794         return cimg::fibonacci((int)_mp_arg(2));
20795       }
20796 
20797       static double mp_find(_cimg_math_parser& mp) {
20798         const bool is_forward = (bool)_mp_arg(5);
20799         const ulongT siz = (ulongT)mp.opcode[3];
20800         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz - 1);
20801         if (ind<0 || ind>=(longT)siz) return -1.;
20802         const double
20803           *const ptrb = &_mp_arg(2) + 1,
20804           *const ptre = ptrb + siz,
20805           val = _mp_arg(4),
20806           *ptr = ptrb + ind;
20807 
20808         // Forward search
20809         if (is_forward) {
20810           while (ptr<ptre && *ptr!=val) ++ptr;
20811           return ptr==ptre?-1.:(double)(ptr - ptrb);
20812         }
20813 
20814         // Backward search.
20815         while (ptr>=ptrb && *ptr!=val) --ptr;
20816         return ptr<ptrb?-1.:(double)(ptr - ptrb);
20817       }
20818 
20819       static double mp_find_seq(_cimg_math_parser& mp) {
20820         const bool is_forward = (bool)_mp_arg(6);
20821         const ulongT
20822           siz1 = (ulongT)mp.opcode[3],
20823           siz2 = (ulongT)mp.opcode[5];
20824         longT ind = (longT)(mp.opcode[7]!=_cimg_mp_slot_nan?_mp_arg(7):is_forward?0:siz1 - 1);
20825         if (ind<0 || ind>=(longT)siz1) return -1.;
20826         const double
20827           *const ptr1b = &_mp_arg(2) + 1,
20828           *const ptr1e = ptr1b + siz1,
20829           *const ptr2b = &_mp_arg(4) + 1,
20830           *const ptr2e = ptr2b + siz2,
20831           *ptr1 = ptr1b + ind,
20832           *p1 = 0,
20833           *p2 = 0;
20834 
20835         // Forward search.
20836         if (is_forward) {
20837           do {
20838             while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
20839             p1 = ptr1 + 1;
20840             p2 = ptr2b + 1;
20841             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
20842           } while (p2<ptr2e && ++ptr1<ptr1e);
20843           return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
20844         }
20845 
20846         // Backward search.
20847         do {
20848           while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
20849           p1 = ptr1 + 1;
20850           p2 = ptr2b + 1;
20851           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
20852         } while (p2<ptr2e && --ptr1>=ptr1b);
20853         return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
20854       }
20855 
20856       static double mp_floor(_cimg_math_parser& mp) {
20857         return std::floor(_mp_arg(2));
20858       }
20859 
20860       static double mp_for(_cimg_math_parser& mp) {
20861         const ulongT
20862           mem_body = mp.opcode[1],
20863           mem_cond = mp.opcode[3];
20864         const CImg<ulongT>
20865           *const p_init = ++mp.p_code,
20866           *const p_cond = p_init + mp.opcode[4],
20867           *const p_body = p_cond + mp.opcode[5],
20868           *const p_post = p_body + mp.opcode[6],
20869           *const p_end = p_post + mp.opcode[7];
20870         const unsigned int vsiz = (unsigned int)mp.opcode[2];
20871         bool is_cond = false;
20872         if (mp.opcode[8]) { // Set default value for result and condition if necessary
20873           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
20874           else mp.mem[mem_body] = cimg::type<double>::nan();
20875         }
20876         if (mp.opcode[9]) mp.mem[mem_cond] = 0;
20877         const unsigned int _break_type = mp.break_type;
20878         mp.break_type = 0;
20879 
20880         for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
20881           mp.opcode._data = mp.p_code->_data;
20882           const ulongT target = mp.opcode[1];
20883           mp.mem[target] = _cimg_mp_defunc(mp);
20884         }
20885 
20886         if (!mp.break_type) do {
20887             for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
20888               mp.opcode._data = mp.p_code->_data;
20889               const ulongT target = mp.opcode[1];
20890               mp.mem[target] = _cimg_mp_defunc(mp);
20891             }
20892             if (mp.break_type==1) break;
20893 
20894             is_cond = (bool)mp.mem[mem_cond];
20895             if (is_cond && !mp.break_type) {
20896               for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
20897                 mp.opcode._data = mp.p_code->_data;
20898                 const ulongT target = mp.opcode[1];
20899                 mp.mem[target] = _cimg_mp_defunc(mp);
20900               }
20901               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20902 
20903               for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
20904                 mp.opcode._data = mp.p_code->_data;
20905                 const ulongT target = mp.opcode[1];
20906                 mp.mem[target] = _cimg_mp_defunc(mp);
20907               }
20908               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20909             }
20910           } while (is_cond);
20911 
20912         mp.break_type = _break_type;
20913         mp.p_code = p_end - 1;
20914         return mp.mem[mem_body];
20915       }
20916 
20917       static double mp_fsize(_cimg_math_parser& mp) {
20918         const CImg<charT> filename(mp.opcode._data + 3,mp.opcode[2] - 3);
20919         return (double)cimg::fsize(filename);
20920       }
20921 
20922       static double mp_g(_cimg_math_parser& mp) {
20923         cimg::unused(mp);
20924         return cimg::grand();
20925       }
20926 
20927       static double mp_gauss(_cimg_math_parser& mp) {
20928         const double x = _mp_arg(2), s = _mp_arg(3);
20929         return std::exp(-x*x/(2*s*s))/std::sqrt(2*s*s*cimg::PI);
20930       }
20931 
20932       static double mp_gcd(_cimg_math_parser& mp) {
20933         return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
20934       }
20935 
20936       static double mp_gt(_cimg_math_parser& mp) {
20937         return (double)(_mp_arg(2)>_mp_arg(3));
20938       }
20939 
20940       static double mp_gte(_cimg_math_parser& mp) {
20941         return (double)(_mp_arg(2)>=_mp_arg(3));
20942       }
20943 
20944       static double mp_i(_cimg_math_parser& mp) {
20945         return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
20946                                        (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
20947       }
20948 
20949       static double mp_if(_cimg_math_parser& mp) {
20950         const bool is_cond = (bool)_mp_arg(2);
20951         const ulongT
20952           mem_left = mp.opcode[3],
20953           mem_right = mp.opcode[4];
20954         const CImg<ulongT>
20955           *const p_right = ++mp.p_code + mp.opcode[5],
20956           *const p_end = p_right + mp.opcode[6];
20957         const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
20958         if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
20959             mp.opcode._data = mp.p_code->_data;
20960             const ulongT target = mp.opcode[1];
20961             mp.mem[target] = _cimg_mp_defunc(mp);
20962           }
20963         else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
20964             mp.opcode._data = mp.p_code->_data;
20965             const ulongT target = mp.opcode[1];
20966             mp.mem[target] = _cimg_mp_defunc(mp);
20967           }
20968         if (mp.p_code==mp.p_break) --mp.p_code;
20969         else mp.p_code = p_end - 1;
20970         if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
20971         return mp.mem[is_cond?mem_left:mem_right];
20972       }
20973 
20974       static double mp_image_d(_cimg_math_parser& mp) {
20975         unsigned int ind = (unsigned int)mp.opcode[2];
20976         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
20977         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
20978         return (double)img.depth();
20979       }
20980 
20981       static double mp_image_display(_cimg_math_parser& mp) {
20982         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
20983         cimg::mutex(6);
20984         CImg<T> &img = mp.listout[ind];
20985         CImg<charT> title(256);
20986         std::fputc('\n',cimg::output());
20987         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
20988         img.display(title);
20989         cimg::mutex(6,0);
20990         return cimg::type<double>::nan();
20991       }
20992 
20993       static double mp_image_h(_cimg_math_parser& mp) {
20994         unsigned int ind = (unsigned int)mp.opcode[2];
20995         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
20996         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
20997         return (double)img.height();
20998       }
20999 
21000       static double mp_image_median(_cimg_math_parser& mp) {
21001         unsigned int ind = (unsigned int)mp.opcode[2];
21002         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21003         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21004         return (double)img.median();
21005       }
21006 
21007       static double mp_image_print(_cimg_math_parser& mp) {
21008         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
21009         cimg::mutex(6);
21010         CImg<T> &img = mp.listout[ind];
21011         CImg<charT> title(256);
21012         std::fputc('\n',cimg::output());
21013         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
21014         img.print(title);
21015         cimg::mutex(6,0);
21016         return cimg::type<double>::nan();
21017       }
21018 
21019       static double mp_image_resize(_cimg_math_parser& mp) {
21020         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
21021         cimg::mutex(6);
21022         CImg<T> &img = mp.listout[ind];
21023         const double
21024           _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
21025           _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
21026           _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
21027           _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
21028         const unsigned int
21029           w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
21030           h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
21031           d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
21032           s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
21033           interp = (int)_mp_arg(7);
21034         if (mp.is_fill && img._data==mp.imgout._data) {
21035           cimg::mutex(6,0);
21036           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
21037                                       "Cannot both fill and resize image (%u,%u,%u,%u) "
21038                                       "to new dimensions (%u,%u,%u,%u).",
21039                                       img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
21040         }
21041         const unsigned int
21042           boundary = (int)_mp_arg(8);
21043         const float
21044           cx = (float)_mp_arg(9),
21045           cy = (float)_mp_arg(10),
21046           cz = (float)_mp_arg(11),
21047           cc = (float)_mp_arg(12);
21048         img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
21049         cimg::mutex(6,0);
21050         return cimg::type<double>::nan();
21051       }
21052 
21053       static double mp_image_s(_cimg_math_parser& mp) {
21054         unsigned int ind = (unsigned int)mp.opcode[2];
21055         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21056         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21057         return (double)img.spectrum();
21058       }
21059 
21060       static double mp_image_sort(_cimg_math_parser& mp) {
21061         const bool is_increasing = (bool)_mp_arg(3);
21062         const unsigned int
21063           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
21064           axis = (unsigned int)_mp_arg(4);
21065         cimg::mutex(6);
21066         CImg<T> &img = mp.listout[ind];
21067         img.sort(is_increasing,
21068                  axis==0 || axis=='x'?'x':
21069                  axis==1 || axis=='y'?'y':
21070                  axis==2 || axis=='z'?'z':
21071                  axis==3 || axis=='c'?'c':0);
21072         cimg::mutex(6,0);
21073         return cimg::type<double>::nan();
21074       }
21075 
21076       static double mp_image_stats(_cimg_math_parser& mp) {
21077         double *ptrd = &_mp_arg(1) + 1;
21078         unsigned int ind = (unsigned int)mp.opcode[2];
21079         if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
21080         else {
21081           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21082           CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
21083         }
21084         return cimg::type<double>::nan();
21085       }
21086 
21087       static double mp_image_w(_cimg_math_parser& mp) {
21088         unsigned int ind = (unsigned int)mp.opcode[2];
21089         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21090         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21091         return (double)img.width();
21092       }
21093 
21094       static double mp_image_wh(_cimg_math_parser& mp) {
21095         unsigned int ind = (unsigned int)mp.opcode[2];
21096         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21097         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21098         return (double)img.width()*img.height();
21099       }
21100 
21101       static double mp_image_whd(_cimg_math_parser& mp) {
21102         unsigned int ind = (unsigned int)mp.opcode[2];
21103         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21104         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21105         return (double)img.width()*img.height()*img.depth();
21106       }
21107 
21108       static double mp_image_whds(_cimg_math_parser& mp) {
21109         unsigned int ind = (unsigned int)mp.opcode[2];
21110         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21111         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21112         return (double)img.width()*img.height()*img.depth()*img.spectrum();
21113       }
21114 
21115       static double mp_increment(_cimg_math_parser& mp) {
21116         return _mp_arg(2) + 1;
21117       }
21118 
21119       static double mp_int(_cimg_math_parser& mp) {
21120         return (double)(longT)_mp_arg(2);
21121       }
21122 
21123       static double mp_ioff(_cimg_math_parser& mp) {
21124         const unsigned int
21125           boundary_conditions = (unsigned int)_mp_arg(3);
21126         const CImg<T> &img = mp.imgin;
21127         const longT
21128           off = (longT)_mp_arg(2),
21129           whds = (longT)img.size();
21130         if (off>=0 && off<whds) return (double)img[off];
21131         if (img._data) switch (boundary_conditions) {
21132           case 3 : { // Mirror
21133             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21134             return (double)img[moff<whds?moff:whds2 - moff - 1];
21135           }
21136           case 2 : // Periodic
21137             return (double)img[cimg::mod(off,whds)];
21138           case 1 : // Neumann
21139             return (double)img[off<0?0:whds - 1];
21140           default : // Dirichlet
21141             return 0;
21142           }
21143         return 0;
21144       }
21145 
21146       static double mp_isbool(_cimg_math_parser& mp) {
21147         const double val = _mp_arg(2);
21148         return (double)(val==0.0 || val==1.0);
21149       }
21150 
21151       static double mp_isin(_cimg_math_parser& mp) {
21152         const unsigned int i_end = (unsigned int)mp.opcode[2];
21153         const double val = _mp_arg(3);
21154         for (unsigned int i = 4; i<i_end; ++i)
21155           if (val==_mp_arg(i)) return 1.0;
21156         return 0.0;
21157       }
21158 
21159       static double mp_isinf(_cimg_math_parser& mp) {
21160         return (double)cimg::type<double>::is_inf(_mp_arg(2));
21161       }
21162 
21163       static double mp_isint(_cimg_math_parser& mp) {
21164         return (double)(cimg::mod(_mp_arg(2),1.0)==0);
21165       }
21166 
21167       static double mp_isnan(_cimg_math_parser& mp) {
21168         return (double)cimg::type<double>::is_nan(_mp_arg(2));
21169       }
21170 
21171       static double mp_ixyzc(_cimg_math_parser& mp) {
21172         const unsigned int
21173           interpolation = (unsigned int)_mp_arg(6),
21174           boundary_conditions = (unsigned int)_mp_arg(7);
21175         const CImg<T> &img = mp.imgin;
21176         const double
21177           x = _mp_arg(2), y = _mp_arg(3),
21178           z = _mp_arg(4), c = _mp_arg(5);
21179         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21180           case 3 : { // Mirror
21181             const int
21182               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21183               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21184               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21185             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21186                                my<img.height()?my:h2 - my - 1,
21187                                mz<img.depth()?mz:d2 - mz - 1,
21188                                mc<img.spectrum()?mc:s2 - mc - 1);
21189           }
21190           case 2 : // Periodic
21191             return (double)img(cimg::mod((int)x,img.width()),
21192                                cimg::mod((int)y,img.height()),
21193                                cimg::mod((int)z,img.depth()),
21194                                cimg::mod((int)c,img.spectrum()));
21195           case 1 : // Neumann
21196             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21197           default : // Dirichlet
21198             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21199           } else switch (boundary_conditions) { // Linear interpolation
21200           case 3 : { // Mirror
21201             const float
21202               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21203               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21204               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21205             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21206                                               my<img.height()?my:h2 - my - 1,
21207                                               mz<img.depth()?mz:d2 - mz - 1,
21208                                               mc<img.spectrum()?mc:s2 - mc - 1);
21209           }
21210           case 2 : // Periodic
21211             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21212                                               cimg::mod((float)y,(float)img.height()),
21213                                               cimg::mod((float)z,(float)img.depth()),
21214                                               cimg::mod((float)c,(float)img.spectrum()));
21215           case 1 : // Neumann
21216             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21217           default : // Dirichlet
21218             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21219           }
21220       }
21221 
21222       static double mp_joff(_cimg_math_parser& mp) {
21223         const unsigned int
21224           boundary_conditions = (unsigned int)_mp_arg(3);
21225         const int
21226           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21227           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21228         const CImg<T> &img = mp.imgin;
21229         const longT
21230           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
21231           whds = (longT)img.size();
21232         if (off>=0 && off<whds) return (double)img[off];
21233         if (img._data) switch (boundary_conditions) {
21234           case 3 : { // Mirror
21235             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21236             return (double)img[moff<whds?moff:whds2 - moff - 1];
21237           }
21238           case 2 : // Periodic
21239             return (double)img[cimg::mod(off,whds)];
21240           case 1 : // Neumann
21241             return (double)img[off<0?0:whds - 1];
21242           default : // Dirichlet
21243             return 0;
21244           }
21245         return 0;
21246       }
21247 
21248       static double mp_jxyzc(_cimg_math_parser& mp) {
21249         const unsigned int
21250           interpolation = (unsigned int)_mp_arg(6),
21251           boundary_conditions = (unsigned int)_mp_arg(7);
21252         const CImg<T> &img = mp.imgin;
21253         const double
21254           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21255           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
21256           x = ox + _mp_arg(2), y = oy + _mp_arg(3),
21257           z = oz + _mp_arg(4), c = oc + _mp_arg(5);
21258         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21259           case 3 : { // Mirror
21260             const int
21261               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21262               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21263               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21264             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21265                                my<img.height()?my:h2 - my - 1,
21266                                mz<img.depth()?mz:d2 - mz - 1,
21267                                mc<img.spectrum()?mc:s2 - mc - 1);
21268           }
21269           case 2 : // Periodic
21270             return (double)img(cimg::mod((int)x,img.width()),
21271                                cimg::mod((int)y,img.height()),
21272                                cimg::mod((int)z,img.depth()),
21273                                cimg::mod((int)c,img.spectrum()));
21274           case 1 : // Neumann
21275             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21276           default : // Dirichlet
21277             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21278           } else switch (boundary_conditions) { // Linear interpolation
21279           case 3 : { // Mirror
21280             const float
21281               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21282               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21283               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21284             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21285                                               my<img.height()?my:h2 - my - 1,
21286                                               mz<img.depth()?mz:d2 - mz - 1,
21287                                               mc<img.spectrum()?mc:s2 - mc - 1);
21288           }
21289           case 2 : // Periodic
21290             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21291                                               cimg::mod((float)y,(float)img.height()),
21292                                               cimg::mod((float)z,(float)img.depth()),
21293                                               cimg::mod((float)c,(float)img.spectrum()));
21294           case 1 : // Neumann
21295             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21296           default : // Dirichlet
21297             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21298           }
21299       }
21300 
21301       static double mp_kth(_cimg_math_parser& mp) {
21302         const unsigned int i_end = (unsigned int)mp.opcode[2];
21303         CImg<doubleT> vals(i_end - 4);
21304         double *p = vals.data();
21305         for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
21306         int ind = (int)cimg::round(_mp_arg(3));
21307         if (ind<0) ind+=vals.width() + 1;
21308         ind = std::max(1,std::min(vals.width(),ind));
21309         return vals.kth_smallest(ind - 1);
21310       }
21311 
21312       static double mp_linear_add(_cimg_math_parser& mp) {
21313         return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
21314       }
21315 
21316       static double mp_linear_sub_left(_cimg_math_parser& mp) {
21317         return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
21318       }
21319 
21320       static double mp_linear_sub_right(_cimg_math_parser& mp) {
21321         return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
21322       }
21323 
21324       static double mp_list_depth(_cimg_math_parser& mp) {
21325         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21326         return (double)mp.listin[ind]._depth;
21327       }
21328 
21329       static double mp_list_find(_cimg_math_parser& mp) {
21330         const unsigned int
21331           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21332         const CImg<T> &img = mp.listin[indi];
21333         const bool is_forward = (bool)_mp_arg(4);
21334         const ulongT siz = (ulongT)img.size();
21335         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):is_forward?0:siz - 1);
21336         if (ind<0 || ind>=(longT)siz) return -1.;
21337         const T
21338           *const ptrb = img.data(),
21339           *const ptre = img.end(),
21340           *ptr = ptrb + ind;
21341         const double val = _mp_arg(3);
21342 
21343         // Forward search
21344         if (is_forward) {
21345           while (ptr<ptre && (double)*ptr!=val) ++ptr;
21346           return ptr==ptre?-1.:(double)(ptr - ptrb);
21347         }
21348 
21349         // Backward search.
21350         while (ptr>=ptrb && (double)*ptr!=val) --ptr;
21351         return ptr<ptrb?-1.:(double)(ptr - ptrb);
21352       }
21353 
21354       static double mp_list_find_seq(_cimg_math_parser& mp) {
21355         const unsigned int
21356           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21357         const CImg<T> &img = mp.listin[indi];
21358         const bool is_forward = (bool)_mp_arg(5);
21359         const ulongT
21360           siz1 = (ulongT)img.size(),
21361           siz2 = (ulongT)mp.opcode[4];
21362         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz1 - 1);
21363         if (ind<0 || ind>=(longT)siz1) return -1.;
21364         const T
21365           *const ptr1b = img.data(),
21366           *const ptr1e = ptr1b + siz1,
21367           *ptr1 = ptr1b + ind,
21368           *p1 = 0;
21369         const double
21370           *const ptr2b = &_mp_arg(3) + 1,
21371           *const ptr2e = ptr2b + siz2,
21372           *p2 = 0;
21373 
21374         // Forward search.
21375         if (is_forward) {
21376           do {
21377             while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
21378             p1 = ptr1 + 1;
21379             p2 = ptr2b + 1;
21380             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
21381           } while (p2<ptr2e && ++ptr1<ptr1e);
21382           return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
21383         }
21384 
21385         // Backward search.
21386         do {
21387           while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
21388           p1 = ptr1 + 1;
21389           p2 = ptr2b + 1;
21390           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
21391         } while (p2<ptr2e && --ptr1>=ptr1b);
21392         return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
21393       }
21394 
21395       static double mp_list_height(_cimg_math_parser& mp) {
21396         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21397         return (double)mp.listin[ind]._height;
21398       }
21399 
21400       static double mp_list_ioff(_cimg_math_parser& mp) {
21401         const unsigned int
21402           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21403           boundary_conditions = (unsigned int)_mp_arg(4);
21404         const CImg<T> &img = mp.listin[ind];
21405         const longT
21406           off = (longT)_mp_arg(3),
21407           whds = (longT)img.size();
21408         if (off>=0 && off<whds) return (double)img[off];
21409         if (img._data) switch (boundary_conditions) {
21410           case 3 : { // Mirror
21411             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21412             return (double)img[moff<whds?moff:whds2 - moff - 1];
21413           }
21414           case 2 : // Periodic
21415             return (double)img[cimg::mod(off,whds)];
21416           case 1 : // Neumann
21417             return (double)img[off<0?0:whds - 1];
21418           default : // Dirichlet
21419             return 0;
21420           }
21421         return 0;
21422       }
21423 
21424       static double mp_list_is_shared(_cimg_math_parser& mp) {
21425         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21426         return (double)mp.listin[ind]._is_shared;
21427       }
21428 
21429       static double mp_list_ixyzc(_cimg_math_parser& mp) {
21430         const unsigned int
21431           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21432           interpolation = (unsigned int)_mp_arg(7),
21433           boundary_conditions = (unsigned int)_mp_arg(8);
21434         const CImg<T> &img = mp.listin[ind];
21435         const double
21436           x = _mp_arg(3), y = _mp_arg(4),
21437           z = _mp_arg(5), c = _mp_arg(6);
21438         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21439           case 3 : { // Mirror
21440             const int
21441               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21442               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21443               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21444             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21445                                my<img.height()?my:h2 - my - 1,
21446                                mz<img.depth()?mz:d2 - mz - 1,
21447                                mc<img.spectrum()?mc:s2 - mc - 1);
21448           }
21449           case 2 : // Periodic
21450             return (double)img(cimg::mod((int)x,img.width()),
21451                                cimg::mod((int)y,img.height()),
21452                                cimg::mod((int)z,img.depth()),
21453                                cimg::mod((int)c,img.spectrum()));
21454           case 1 : // Neumann
21455             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21456           default : // Dirichlet
21457             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21458           } else switch (boundary_conditions) { // Linear interpolation
21459           case 3 : { // Mirror
21460             const float
21461               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21462               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21463               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21464             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21465                                               my<img.height()?my:h2 - my - 1,
21466                                               mz<img.depth()?mz:d2 - mz - 1,
21467                                               mc<img.spectrum()?mc:s2 - mc - 1);
21468           }
21469           case 2 : // Periodic
21470             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21471                                               cimg::mod((float)y,(float)img.height()),
21472                                               cimg::mod((float)z,(float)img.depth()),
21473                                               cimg::mod((float)c,(float)img.spectrum()));
21474           case 1 : // Neumann
21475             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21476           default : // Dirichlet
21477             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21478           }
21479       }
21480 
21481       static double mp_list_joff(_cimg_math_parser& mp) {
21482         const unsigned int
21483           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21484           boundary_conditions = (unsigned int)_mp_arg(4);
21485         const int
21486           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21487           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21488         const CImg<T> &img = mp.listin[ind];
21489         const longT
21490           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21491           whds = (longT)img.size();
21492         if (off>=0 && off<whds) return (double)img[off];
21493         if (img._data) switch (boundary_conditions) {
21494           case 3 : { // Mirror
21495             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21496             return (double)img[moff<whds?moff:whds2 - moff - 1];
21497           }
21498           case 2 : // Periodic
21499             return (double)img[cimg::mod(off,whds)];
21500           case 1 : // Neumann
21501             return (double)img[off<0?0:whds - 1];
21502           default : // Dirichlet
21503             return 0;
21504           }
21505         return 0;
21506       }
21507 
21508       static double mp_list_jxyzc(_cimg_math_parser& mp) {
21509         const unsigned int
21510           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21511           interpolation = (unsigned int)_mp_arg(7),
21512           boundary_conditions = (unsigned int)_mp_arg(8);
21513         const CImg<T> &img = mp.listin[ind];
21514         const double
21515           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21516           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
21517           x = ox + _mp_arg(3), y = oy + _mp_arg(4),
21518           z = oz + _mp_arg(5), c = oc + _mp_arg(6);
21519         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21520           case 3 : { // Mirror
21521             const int
21522               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21523               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21524               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21525             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21526                                my<img.height()?my:h2 - my - 1,
21527                                mz<img.depth()?mz:d2 - mz - 1,
21528                                mc<img.spectrum()?mc:s2 - mc - 1);
21529           }
21530           case 2 : // Periodic
21531             return (double)img(cimg::mod((int)x,img.width()),
21532                                cimg::mod((int)y,img.height()),
21533                                cimg::mod((int)z,img.depth()),
21534                                cimg::mod((int)c,img.spectrum()));
21535           case 1 : // Neumann
21536             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21537           default : // Dirichlet
21538             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21539           } else switch (boundary_conditions) { // Linear interpolation
21540           case 3 : { // Mirror
21541             const float
21542               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21543               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21544               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21545             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21546                                               my<img.height()?my:h2 - my - 1,
21547                                               mz<img.depth()?mz:d2 - mz - 1,
21548                                               mc<img.spectrum()?mc:s2 - mc - 1);
21549           }
21550           case 2 : // Periodic
21551             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21552                                               cimg::mod((float)y,(float)img.height()),
21553                                               cimg::mod((float)z,(float)img.depth()),
21554                                               cimg::mod((float)c,(float)img.spectrum()));
21555           case 1 : // Neumann
21556             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21557           default : // Dirichlet
21558             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21559           }
21560       }
21561 
21562       static double mp_list_l(_cimg_math_parser& mp) {
21563         return (double)mp.listout.width();
21564       }
21565 
21566       static double mp_list_median(_cimg_math_parser& mp) {
21567         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21568         if (!mp.list_median) mp.list_median.assign(mp.listin._width);
21569         if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
21570         return *mp.list_median[ind];
21571       }
21572 
21573       static double mp_list_set_ioff(_cimg_math_parser& mp) {
21574         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21575         CImg<T> &img = mp.listout[ind];
21576         const longT
21577           off = (longT)_mp_arg(3),
21578           whds = (longT)img.size();
21579         const double val = _mp_arg(1);
21580         if (off>=0 && off<whds) img[off] = (T)val;
21581         return val;
21582       }
21583 
21584       static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
21585         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21586         CImg<T> &img = mp.listout[ind];
21587         const int
21588           x = (int)_mp_arg(3), y = (int)_mp_arg(4),
21589           z = (int)_mp_arg(5), c = (int)_mp_arg(6);
21590         const double val = _mp_arg(1);
21591         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
21592             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
21593           img(x,y,z,c) = (T)val;
21594         return val;
21595       }
21596 
21597       static double mp_list_set_joff(_cimg_math_parser& mp) {
21598         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21599         CImg<T> &img = mp.listout[ind];
21600         const int
21601           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21602           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21603         const longT
21604           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21605           whds = (longT)img.size();
21606         const double val = _mp_arg(1);
21607         if (off>=0 && off<whds) img[off] = (T)val;
21608         return val;
21609       }
21610 
21611       static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
21612         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21613         CImg<T> &img = mp.listout[ind];
21614         const double
21615           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21616           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
21617         const int
21618           x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
21619           z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
21620         const double val = _mp_arg(1);
21621         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
21622             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
21623           img(x,y,z,c) = (T)val;
21624         return val;
21625       }
21626 
21627       static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
21628         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21629         CImg<T> &img = mp.listout[ind];
21630         const longT
21631           off = (longT)_mp_arg(3),
21632           whd = (longT)img.width()*img.height()*img.depth();
21633         const T val = (T)_mp_arg(1);
21634         if (off>=0 && off<whd) {
21635           T *ptrd = &img[off];
21636           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21637         }
21638         return _mp_arg(1);
21639       }
21640 
21641       static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
21642         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21643         CImg<T> &img = mp.listout[ind];
21644         const longT
21645           off = (longT)_mp_arg(3),
21646           whd = (longT)img.width()*img.height()*img.depth();
21647         const double *ptrs = &_mp_arg(1) + 1;
21648         if (off>=0 && off<whd) {
21649           const unsigned int vsiz = (unsigned int)mp.opcode[4];
21650           T *ptrd = &img[off];
21651           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21652         }
21653         return cimg::type<double>::nan();
21654       }
21655 
21656       static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
21657         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21658         CImg<T> &img = mp.listout[ind];
21659         const int
21660           x = (int)_mp_arg(3),
21661           y = (int)_mp_arg(4),
21662           z = (int)_mp_arg(5);
21663         const T val = (T)_mp_arg(1);
21664         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21665           T *ptrd = &img(x,y,z);
21666           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21667           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21668         }
21669         return _mp_arg(1);
21670       }
21671 
21672       static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
21673         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21674         CImg<T> &img = mp.listout[ind];
21675         const int
21676           x = (int)_mp_arg(3),
21677           y = (int)_mp_arg(4),
21678           z = (int)_mp_arg(5);
21679         const double *ptrs = &_mp_arg(1) + 1;
21680         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21681           const unsigned int vsiz = (unsigned int)mp.opcode[6];
21682           T *ptrd = &img(x,y,z);
21683           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21684           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21685         }
21686         return cimg::type<double>::nan();
21687       }
21688 
21689       static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
21690         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21691         CImg<T> &img = mp.listout[ind];
21692         const int
21693           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21694           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21695         const longT
21696           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21697           whd = (longT)img.width()*img.height()*img.depth();
21698         const T val = (T)_mp_arg(1);
21699         if (off>=0 && off<whd) {
21700           T *ptrd = &img[off];
21701           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21702         }
21703         return _mp_arg(1);
21704       }
21705 
21706       static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
21707         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21708         CImg<T> &img = mp.listout[ind];
21709         const int
21710           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21711           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21712         const longT
21713           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21714           whd = (longT)img.width()*img.height()*img.depth();
21715         const double *ptrs = &_mp_arg(1) + 1;
21716         if (off>=0 && off<whd) {
21717           const unsigned int vsiz = (unsigned int)mp.opcode[4];
21718           T *ptrd = &img[off];
21719           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21720         }
21721         return cimg::type<double>::nan();
21722       }
21723 
21724       static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
21725         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21726         CImg<T> &img = mp.listout[ind];
21727         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
21728         const int
21729           x = (int)(ox + _mp_arg(3)),
21730           y = (int)(oy + _mp_arg(4)),
21731           z = (int)(oz + _mp_arg(5));
21732         const T val = (T)_mp_arg(1);
21733         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21734           T *ptrd = &img(x,y,z);
21735           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21736           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21737         }
21738         return _mp_arg(1);
21739       }
21740 
21741       static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
21742         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21743         CImg<T> &img = mp.listout[ind];
21744         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
21745         const int
21746           x = (int)(ox + _mp_arg(3)),
21747           y = (int)(oy + _mp_arg(4)),
21748           z = (int)(oz + _mp_arg(5));
21749         const double *ptrs = &_mp_arg(1) + 1;
21750         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21751           const unsigned int vsiz = (unsigned int)mp.opcode[6];
21752           T *ptrd = &img(x,y,z);
21753           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21754           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21755         }
21756         return cimg::type<double>::nan();
21757       }
21758 
21759       static double mp_list_spectrum(_cimg_math_parser& mp) {
21760         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21761         return (double)mp.listin[ind]._spectrum;
21762       }
21763 
21764       static double mp_list_stats(_cimg_math_parser& mp) {
21765         const unsigned int
21766           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21767           k = (unsigned int)mp.opcode[3];
21768         if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
21769         if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
21770         return mp.list_stats(ind,k);
21771       }
21772 
21773       static double mp_list_wh(_cimg_math_parser& mp) {
21774         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21775         return (double)mp.listin[ind]._width*mp.listin[ind]._height;
21776       }
21777 
21778       static double mp_list_whd(_cimg_math_parser& mp) {
21779         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21780         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
21781       }
21782 
21783       static double mp_list_whds(_cimg_math_parser& mp) {
21784         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21785         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
21786       }
21787 
21788       static double mp_list_width(_cimg_math_parser& mp) {
21789         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21790         return (double)mp.listin[ind]._width;
21791       }
21792 
21793       static double mp_list_Ioff(_cimg_math_parser& mp) {
21794         double *ptrd = &_mp_arg(1) + 1;
21795         const unsigned int
21796           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21797           boundary_conditions = (unsigned int)_mp_arg(4),
21798           vsiz = (unsigned int)mp.opcode[5];
21799         const CImg<T> &img = mp.listin[ind];
21800         const longT
21801           off = (longT)_mp_arg(3),
21802           whd = (longT)img.width()*img.height()*img.depth();
21803         const T *ptrs;
21804         if (off>=0 && off<whd) {
21805           ptrs = &img[off];
21806           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21807           return cimg::type<double>::nan();
21808         }
21809         if (img._data) switch (boundary_conditions) {
21810           case 3 : { // Mirror
21811             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
21812             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
21813             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21814             return cimg::type<double>::nan();
21815           }
21816           case 2 : // Periodic
21817             ptrs = &img[cimg::mod(off,whd)];
21818             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21819             return cimg::type<double>::nan();
21820           case 1 : // Neumann
21821             ptrs = off<0?&img[0]:&img[whd - 1];
21822             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21823             return cimg::type<double>::nan();
21824           default : // Dirichlet
21825             std::memset(ptrd,0,vsiz*sizeof(double));
21826             return cimg::type<double>::nan();
21827           }
21828         std::memset(ptrd,0,vsiz*sizeof(double));
21829         return cimg::type<double>::nan();
21830       }
21831 
21832       static double mp_list_Ixyz(_cimg_math_parser& mp) {
21833         double *ptrd = &_mp_arg(1) + 1;
21834         const unsigned int
21835           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21836           interpolation = (unsigned int)_mp_arg(6),
21837           boundary_conditions = (unsigned int)_mp_arg(7),
21838           vsiz = (unsigned int)mp.opcode[8];
21839         const CImg<T> &img = mp.listin[ind];
21840         const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
21841         const ulongT whd = (ulongT)img._width*img._height*img._depth;
21842         const T *ptrs;
21843         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21844           case 3 : { // Mirror
21845             const int
21846               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
21847               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
21848               cx = mx<img.width()?mx:w2 - mx - 1,
21849               cy = my<img.height()?my:h2 - my - 1,
21850               cz = mz<img.depth()?mz:d2 - mz - 1;
21851             ptrs = &img(cx,cy,cz);
21852             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21853           } break;
21854           case 2 : { // Periodic
21855             const int
21856               cx = cimg::mod((int)x,img.width()),
21857               cy = cimg::mod((int)y,img.height()),
21858               cz = cimg::mod((int)z,img.depth());
21859             ptrs = &img(cx,cy,cz);
21860             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21861           } break;
21862           case 1 : { // Neumann
21863             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
21864             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21865           } break;
21866           default : // Dirichlet
21867             if (img.containsXYZC(x,y,z)) {
21868               ptrs = &img((int)x,(int)y,(int)z);
21869               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21870             } else std::memset(ptrd,0,vsiz*sizeof(double));
21871           } else switch (boundary_conditions) { // Linear interpolation
21872           case 3 : { // Mirror
21873             const float
21874               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
21875               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
21876               cx = mx<img.width()?mx:w2 - mx - 1,
21877               cy = my<img.height()?my:h2 - my - 1,
21878               cz = mz<img.depth()?mz:d2 - mz - 1;
21879             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
21880           } break;
21881           case 2 : { // Periodic
21882             const float
21883               cx = cimg::mod((float)x,(float)img.width()),
21884               cy = cimg::mod((float)y,(float)img.height()),
21885               cz = cimg::mod((float)z,(float)img.depth());
21886             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
21887           } break;
21888           case 1 : // Neumann
21889             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
21890             break;
21891           case 0 : // Dirichlet
21892             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
21893           }
21894         return cimg::type<double>::nan();
21895       }
21896 
21897       static double mp_list_Joff(_cimg_math_parser& mp) {
21898         double *ptrd = &_mp_arg(1) + 1;
21899         const unsigned int
21900           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21901           boundary_conditions = (unsigned int)_mp_arg(4),
21902           vsiz = (unsigned int)mp.opcode[5];
21903         const int
21904           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
21905         const CImg<T> &img = mp.listin[ind];
21906         const longT
21907           off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
21908           whd = (longT)img.width()*img.height()*img.depth();
21909         const T *ptrs;
21910         if (off>=0 && off<whd) {
21911           ptrs = &img[off];
21912           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21913           return cimg::type<double>::nan();
21914         }
21915         if (img._data) switch (boundary_conditions) {
21916           case 3 : { // Mirror
21917             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
21918             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
21919             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21920             return cimg::type<double>::nan();
21921           }
21922           case 2 : // Periodic
21923             ptrs = &img[cimg::mod(off,whd)];
21924             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21925             return cimg::type<double>::nan();
21926           case 1 : // Neumann
21927             ptrs = off<0?&img[0]:&img[whd - 1];
21928             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21929             return cimg::type<double>::nan();
21930           default : // Dirichlet
21931             std::memset(ptrd,0,vsiz*sizeof(double));
21932             return cimg::type<double>::nan();
21933           }
21934         std::memset(ptrd,0,vsiz*sizeof(double));
21935         return cimg::type<double>::nan();
21936       }
21937 
21938       static double mp_list_Jxyz(_cimg_math_parser& mp) {
21939         double *ptrd = &_mp_arg(1) + 1;
21940         const unsigned int
21941           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21942           interpolation = (unsigned int)_mp_arg(6),
21943           boundary_conditions = (unsigned int)_mp_arg(7),
21944           vsiz = (unsigned int)mp.opcode[8];
21945         const CImg<T> &img = mp.listin[ind];
21946         const double
21947           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
21948           x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
21949         const ulongT whd = (ulongT)img._width*img._height*img._depth;
21950         const T *ptrs;
21951         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21952           case 3 : { // Mirror
21953             const int
21954               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
21955               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
21956               cx = mx<img.width()?mx:w2 - mx - 1,
21957               cy = my<img.height()?my:h2 - my - 1,
21958               cz = mz<img.depth()?mz:d2 - mz - 1;
21959             ptrs = &img(cx,cy,cz);
21960             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21961           } break;
21962           case 2 : { // Periodic
21963             const int
21964               cx = cimg::mod((int)x,img.width()),
21965               cy = cimg::mod((int)y,img.height()),
21966               cz = cimg::mod((int)z,img.depth());
21967             ptrs = &img(cx,cy,cz);
21968             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21969           } break;
21970           case 1 : { // Neumann
21971             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
21972             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21973           } break;
21974           default : // Dirichlet
21975             if (img.containsXYZC(x,y,z)) {
21976               ptrs = &img((int)x,(int)y,(int)z);
21977               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21978             } else std::memset(ptrd,0,vsiz*sizeof(double));
21979           } else switch (boundary_conditions) { // Linear interpolation
21980           case 3 : { // Mirror
21981             const float
21982               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
21983               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
21984               cx = mx<img.width()?mx:w2 - mx - 1,
21985               cy = my<img.height()?my:h2 - my - 1,
21986               cz = mz<img.depth()?mz:d2 - mz - 1;
21987             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
21988           } break;
21989           case 2 : { // Periodic
21990             const float
21991               cx = cimg::mod((float)x,(float)img.width()),
21992               cy = cimg::mod((float)y,(float)img.height()),
21993               cz = cimg::mod((float)z,(float)img.depth());
21994             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
21995           } break;
21996           case 1 : // Neumann
21997             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
21998             break;
21999           default : // Dirichlet
22000             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
22001           }
22002         return cimg::type<double>::nan();
22003       }
22004 
22005       static double mp_log(_cimg_math_parser& mp) {
22006         return std::log(_mp_arg(2));
22007       }
22008 
22009       static double mp_log10(_cimg_math_parser& mp) {
22010         return std::log10(_mp_arg(2));
22011       }
22012 
22013       static double mp_log2(_cimg_math_parser& mp) {
22014         return cimg::log2(_mp_arg(2));
22015       }
22016 
22017       static double mp_logical_and(_cimg_math_parser& mp) {
22018         const bool val_left = (bool)_mp_arg(2);
22019         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
22020         if (!val_left) { mp.p_code = p_end - 1; return 0; }
22021         const ulongT mem_right = mp.opcode[3];
22022         for ( ; mp.p_code<p_end; ++mp.p_code) {
22023           mp.opcode._data = mp.p_code->_data;
22024           const ulongT target = mp.opcode[1];
22025           mp.mem[target] = _cimg_mp_defunc(mp);
22026         }
22027         --mp.p_code;
22028         return (double)(bool)mp.mem[mem_right];
22029       }
22030 
22031       static double mp_logical_not(_cimg_math_parser& mp) {
22032         return (double)!_mp_arg(2);
22033       }
22034 
22035       static double mp_logical_or(_cimg_math_parser& mp) {
22036         const bool val_left = (bool)_mp_arg(2);
22037         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
22038         if (val_left) { mp.p_code = p_end - 1; return 1; }
22039         const ulongT mem_right = mp.opcode[3];
22040         for ( ; mp.p_code<p_end; ++mp.p_code) {
22041           mp.opcode._data = mp.p_code->_data;
22042           const ulongT target = mp.opcode[1];
22043           mp.mem[target] = _cimg_mp_defunc(mp);
22044         }
22045         --mp.p_code;
22046         return (double)(bool)mp.mem[mem_right];
22047       }
22048 
22049       static double mp_lowercase(_cimg_math_parser& mp) {
22050         return cimg::lowercase(_mp_arg(2));
22051       }
22052 
22053       static double mp_lt(_cimg_math_parser& mp) {
22054         return (double)(_mp_arg(2)<_mp_arg(3));
22055       }
22056 
22057       static double mp_lte(_cimg_math_parser& mp) {
22058         return (double)(_mp_arg(2)<=_mp_arg(3));
22059       }
22060 
22061       static double mp_matrix_eig(_cimg_math_parser& mp) {
22062         double *ptrd = &_mp_arg(1) + 1;
22063         const double *ptr1 = &_mp_arg(2) + 1;
22064         const unsigned int k = (unsigned int)mp.opcode[3];
22065         CImg<doubleT> val, vec;
22066         CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
22067         CImg<doubleT>(ptrd,1,k,1,1,true) = val;
22068         CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
22069         return cimg::type<double>::nan();
22070       }
22071 
22072       static double mp_matrix_inv(_cimg_math_parser& mp) {
22073         double *ptrd = &_mp_arg(1) + 1;
22074         const double *ptr1 = &_mp_arg(2) + 1;
22075         const unsigned int k = (unsigned int)mp.opcode[3];
22076         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert();
22077         return cimg::type<double>::nan();
22078       }
22079 
22080       static double mp_matrix_mul(_cimg_math_parser& mp) {
22081         double *ptrd = &_mp_arg(1) + 1;
22082         const double
22083           *ptr1 = &_mp_arg(2) + 1,
22084           *ptr2 = &_mp_arg(3) + 1;
22085         const unsigned int
22086           k = (unsigned int)mp.opcode[4],
22087           l = (unsigned int)mp.opcode[5],
22088           m = (unsigned int)mp.opcode[6];
22089         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
22090         return cimg::type<double>::nan();
22091       }
22092 
22093       static double mp_matrix_pseudoinv(_cimg_math_parser& mp) {
22094         double *ptrd = &_mp_arg(1) + 1;
22095         const double *ptr1 = &_mp_arg(2) + 1;
22096         const unsigned int
22097           k = (unsigned int)mp.opcode[3],
22098           l = (unsigned int)mp.opcode[4];
22099         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert();
22100         return cimg::type<double>::nan();
22101       }
22102 
22103       static double mp_matrix_svd(_cimg_math_parser& mp) {
22104         double *ptrd = &_mp_arg(1) + 1;
22105         const double *ptr1 = &_mp_arg(2) + 1;
22106         const unsigned int
22107           k = (unsigned int)mp.opcode[3],
22108           l = (unsigned int)mp.opcode[4];
22109         CImg<doubleT> U, S, V;
22110         CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
22111         CImg<doubleT>(ptrd,k,l,1,1,true) = U;
22112         CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
22113         CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
22114         return cimg::type<double>::nan();
22115       }
22116 
22117       static double mp_max(_cimg_math_parser& mp) {
22118         const unsigned int i_end = (unsigned int)mp.opcode[2];
22119         double val = _mp_arg(3);
22120         for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
22121         return val;
22122       }
22123 
22124       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
22125                                         const longT siz, const long inc) {
22126         const longT
22127           off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
22128           eoff = off + (siz - 1)*inc;
22129         if (off<0 || eoff>=mp.mem.width())
22130           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
22131                                       "Out-of-bounds variable pointer "
22132                                       "(length: %ld, increment: %ld, offset start: %ld, "
22133                                       "offset end: %ld, offset max: %u).",
22134                                       mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
22135         return &mp.mem[off];
22136       }
22137 
22138       static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
22139                                       const longT siz, const long inc) {
22140         const unsigned ind = (unsigned int)p_ref[1];
22141         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())];
22142         const bool is_relative = (bool)p_ref[2];
22143         int ox, oy, oz, oc;
22144         longT off = 0;
22145         if (is_relative) {
22146           ox = (int)mp.mem[_cimg_mp_slot_x];
22147           oy = (int)mp.mem[_cimg_mp_slot_y];
22148           oz = (int)mp.mem[_cimg_mp_slot_z];
22149           oc = (int)mp.mem[_cimg_mp_slot_c];
22150           off = img.offset(ox,oy,oz,oc);
22151         }
22152         if ((*p_ref)%2) {
22153           const int
22154             x = (int)mp.mem[p_ref[3]],
22155             y = (int)mp.mem[p_ref[4]],
22156             z = (int)mp.mem[p_ref[5]],
22157             c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
22158           off+=img.offset(x,y,z,c);
22159         } else off+=(longT)mp.mem[p_ref[3]];
22160         const longT eoff = off + (siz - 1)*inc;
22161         if (off<0 || eoff>=(longT)img.size())
22162           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
22163                                       "Out-of-bounds image pointer "
22164                                       "(length: %ld, increment: %ld, offset start: %ld, "
22165                                       "offset end: %ld, offset max: %lu).",
22166                                       mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
22167         return (float*)&img[off];
22168       }
22169 
22170       static double mp_memcopy(_cimg_math_parser& mp) {
22171         longT siz = (longT)_mp_arg(4);
22172         const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
22173         const float
22174           _opacity = (float)_mp_arg(7),
22175           opacity = (float)cimg::abs(_opacity),
22176           omopacity = 1 - std::max(_opacity,0.0f);
22177         if (siz>0) {
22178           const bool
22179             is_doubled = mp.opcode[8]<=1,
22180             is_doubles = mp.opcode[15]<=1;
22181           if (is_doubled && is_doubles) { // (double*) <- (double*)
22182             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
22183             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
22184             if (inc_d==1 && inc_s==1 && _opacity>=1) {
22185               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
22186               else std::memmove(ptrd,ptrs,siz*sizeof(double));
22187             } else {
22188               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
22189                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22190                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22191               } else { // Overlapping buffers
22192                 CImg<doubleT> buf((unsigned int)siz);
22193                 cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
22194                 ptrs = buf;
22195                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
22196                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
22197               }
22198             }
22199           } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
22200             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
22201             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
22202             if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22203             else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22204           } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
22205             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
22206             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
22207             if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22208             else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
22209           } else { // (float*) <- (float*)
22210             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
22211             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
22212             if (inc_d==1 && inc_s==1 && _opacity>=1) {
22213               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
22214               else std::memmove(ptrd,ptrs,siz*sizeof(float));
22215             } else {
22216               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
22217                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22218                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22219               } else { // Overlapping buffers
22220                 CImg<floatT> buf((unsigned int)siz);
22221                 cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
22222                 ptrs = buf;
22223                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
22224                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
22225               }
22226             }
22227           }
22228         }
22229         return _mp_arg(1);
22230       }
22231 
22232       static double mp_min(_cimg_math_parser& mp) {
22233         const unsigned int i_end = (unsigned int)mp.opcode[2];
22234         double val = _mp_arg(3);
22235         for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
22236         return val;
22237       }
22238 
22239       static double mp_minus(_cimg_math_parser& mp) {
22240         return -_mp_arg(2);
22241       }
22242 
22243       static double mp_mean(_cimg_math_parser& mp) {
22244         const unsigned int i_end = (unsigned int)mp.opcode[2];
22245         double val = _mp_arg(3);
22246         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
22247         return val/(i_end - 3);
22248       }
22249 
22250       static double mp_median(_cimg_math_parser& mp) {
22251         const unsigned int i_end = (unsigned int)mp.opcode[2];
22252         switch (i_end - 3) {
22253         case 1 : return _mp_arg(3);
22254         case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
22255         case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
22256         case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
22257         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));
22258         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),
22259                                      _mp_arg(10),_mp_arg(11));
22260         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),
22261                                       _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
22262         }
22263         CImg<doubleT> vals(i_end - 3);
22264         double *p = vals.data();
22265         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
22266         return vals.median();
22267       }
22268 
22269       static double mp_modulo(_cimg_math_parser& mp) {
22270         return cimg::mod(_mp_arg(2),_mp_arg(3));
22271       }
22272 
22273       static double mp_mul(_cimg_math_parser& mp) {
22274         return _mp_arg(2)*_mp_arg(3);
22275       }
22276 
22277       static double mp_mul2(_cimg_math_parser& mp) {
22278         return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
22279       }
22280 
22281       static double mp_neq(_cimg_math_parser& mp) {
22282         return (double)(_mp_arg(2)!=_mp_arg(3));
22283       }
22284 
22285       static double mp_norm0(_cimg_math_parser& mp) {
22286         const unsigned int i_end = (unsigned int)mp.opcode[2];
22287         switch (i_end - 3) {
22288         case 1 : return _mp_arg(3)!=0;
22289         case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
22290         }
22291         double res = 0;
22292         for (unsigned int i = 3; i<i_end; ++i)
22293           res+=_mp_arg(i)==0?0:1;
22294         return res;
22295       }
22296 
22297       static double mp_norm1(_cimg_math_parser& mp) {
22298         const unsigned int i_end = (unsigned int)mp.opcode[2];
22299         switch (i_end - 3) {
22300         case 1 : return cimg::abs(_mp_arg(3));
22301         case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
22302         }
22303         double res = 0;
22304         for (unsigned int i = 3; i<i_end; ++i)
22305           res+=cimg::abs(_mp_arg(i));
22306         return res;
22307       }
22308 
22309       static double mp_norm2(_cimg_math_parser& mp) {
22310         const unsigned int i_end = (unsigned int)mp.opcode[2];
22311         switch (i_end - 3) {
22312         case 1 : return cimg::abs(_mp_arg(3));
22313         case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
22314         }
22315         double res = 0;
22316         for (unsigned int i = 3; i<i_end; ++i)
22317           res+=cimg::sqr(_mp_arg(i));
22318         return std::sqrt(res);
22319       }
22320 
22321       static double mp_norminf(_cimg_math_parser& mp) {
22322         const unsigned int i_end = (unsigned int)mp.opcode[2];
22323         switch (i_end - 3) {
22324         case 1 : return cimg::abs(_mp_arg(3));
22325         case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
22326         }
22327         double res = 0;
22328         for (unsigned int i = 3; i<i_end; ++i) {
22329           const double val = cimg::abs(_mp_arg(i));
22330           if (val>res) res = val;
22331         }
22332         return res;
22333       }
22334 
22335       static double mp_normp(_cimg_math_parser& mp) {
22336         const unsigned int i_end = (unsigned int)mp.opcode[2];
22337         if (i_end==4) return cimg::abs(_mp_arg(3));
22338         const double p = (double)mp.opcode[3];
22339         double res = 0;
22340         for (unsigned int i = 4; i<i_end; ++i)
22341           res+=std::pow(cimg::abs(_mp_arg(i)),p);
22342         res = std::pow(res,1/p);
22343         return res>0?res:0.0;
22344       }
22345 
22346       static double mp_permutations(_cimg_math_parser& mp) {
22347         return cimg::permutations(_mp_arg(2),_mp_arg(3),(bool)_mp_arg(4));
22348       }
22349 
22350       static double mp_pow(_cimg_math_parser& mp) {
22351         const double v = _mp_arg(2), p = _mp_arg(3);
22352         return std::pow(v,p);
22353       }
22354 
22355       static double mp_pow0_25(_cimg_math_parser& mp) {
22356         const double val = _mp_arg(2);
22357         return std::sqrt(std::sqrt(val));
22358       }
22359 
22360       static double mp_pow3(_cimg_math_parser& mp) {
22361         const double val = _mp_arg(2);
22362         return val*val*val;
22363       }
22364 
22365       static double mp_pow4(_cimg_math_parser& mp) {
22366         const double val = _mp_arg(2);
22367         return val*val*val*val;
22368       }
22369 
22370       static double mp_print(_cimg_math_parser& mp) {
22371           const double val = _mp_arg(1);
22372           const bool print_char = (bool)mp.opcode[3];
22373           cimg_pragma_openmp(critical(mp_print))
22374           {
22375             CImg<charT> expr(mp.opcode[2] - 4);
22376             const ulongT *ptrs = mp.opcode._data + 4;
22377             cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
22378             cimg::strellipsize(expr);
22379             cimg::mutex(6);
22380             if (print_char)
22381               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g = '%c'",expr._data,val,(int)val);
22382             else
22383               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g",expr._data,val);
22384             std::fflush(cimg::output());
22385             cimg::mutex(6,0);
22386           }
22387           return val;
22388       }
22389 
22390       static double mp_prod(_cimg_math_parser& mp) {
22391         const unsigned int i_end = (unsigned int)mp.opcode[2];
22392         double val = _mp_arg(3);
22393         for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
22394         return val;
22395       }
22396 
22397       static double mp_copy(_cimg_math_parser& mp) {
22398         return _mp_arg(2);
22399       }
22400 
22401       static double mp_rol(_cimg_math_parser& mp) {
22402         return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
22403       }
22404 
22405       static double mp_ror(_cimg_math_parser& mp) {
22406         return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
22407       }
22408 
22409       static double mp_rot2d(_cimg_math_parser& mp) {
22410         double *ptrd = &_mp_arg(1) + 1;
22411         const float
22412           theta = (float)_mp_arg(2)*cimg::PI/180,
22413           ca = std::cos(theta),
22414           sa = std::sin(theta);
22415         *(ptrd++) = ca;
22416         *(ptrd++) = -sa;
22417         *(ptrd++) = sa;
22418         *ptrd = ca;
22419         return cimg::type<double>::nan();
22420       }
22421 
22422       static double mp_rot3d(_cimg_math_parser& mp) {
22423         double *ptrd = &_mp_arg(1) + 1;
22424         const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5);
22425         CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta);
22426         return cimg::type<double>::nan();
22427       }
22428 
22429       static double mp_round(_cimg_math_parser& mp) {
22430         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
22431       }
22432 
22433       static double mp_self_add(_cimg_math_parser& mp) {
22434         return _mp_arg(1)+=_mp_arg(2);
22435       }
22436 
22437       static double mp_self_bitwise_and(_cimg_math_parser& mp) {
22438         double &val = _mp_arg(1);
22439         return val = (double)((longT)val & (longT)_mp_arg(2));
22440       }
22441 
22442       static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
22443         double &val = _mp_arg(1);
22444         return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
22445       }
22446 
22447       static double mp_self_bitwise_or(_cimg_math_parser& mp) {
22448         double &val = _mp_arg(1);
22449         return val = (double)((longT)val | (longT)_mp_arg(2));
22450       }
22451 
22452       static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
22453         double &val = _mp_arg(1);
22454         return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
22455       }
22456 
22457       static double mp_self_decrement(_cimg_math_parser& mp) {
22458         return --_mp_arg(1);
22459       }
22460 
22461       static double mp_self_increment(_cimg_math_parser& mp) {
22462         return ++_mp_arg(1);
22463       }
22464 
22465       static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
22466         unsigned int
22467           ptrd = (unsigned int)mp.opcode[1] + 1,
22468           siz = (unsigned int)mp.opcode[2];
22469         mp_func op = (mp_func)mp.opcode[3];
22470         CImg<ulongT> l_opcode(1,3);
22471         l_opcode[2] = mp.opcode[4]; // Scalar argument.
22472         l_opcode.swap(mp.opcode);
22473         ulongT &target = mp.opcode[1];
22474         while (siz-->0) { target = ptrd++; (*op)(mp); }
22475         l_opcode.swap(mp.opcode);
22476         return cimg::type<double>::nan();
22477       }
22478 
22479       static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
22480         unsigned int
22481           ptrd = (unsigned int)mp.opcode[1] + 1,
22482           siz = (unsigned int)mp.opcode[2],
22483           ptrs = (unsigned int)mp.opcode[4] + 1;
22484         mp_func op = (mp_func)mp.opcode[3];
22485         CImg<ulongT> l_opcode(1,4);
22486         l_opcode.swap(mp.opcode);
22487         ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
22488         while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
22489         l_opcode.swap(mp.opcode);
22490         return cimg::type<double>::nan();
22491       }
22492 
22493       static double mp_self_mul(_cimg_math_parser& mp) {
22494         return _mp_arg(1)*=_mp_arg(2);
22495       }
22496 
22497       static double mp_self_div(_cimg_math_parser& mp) {
22498         return _mp_arg(1)/=_mp_arg(2);
22499       }
22500 
22501       static double mp_self_modulo(_cimg_math_parser& mp) {
22502         double &val = _mp_arg(1);
22503         return val = cimg::mod(val,_mp_arg(2));
22504       }
22505 
22506       static double mp_self_pow(_cimg_math_parser& mp) {
22507         double &val = _mp_arg(1);
22508         return val = std::pow(val,_mp_arg(2));
22509       }
22510 
22511       static double mp_self_sub(_cimg_math_parser& mp) {
22512         return _mp_arg(1)-=_mp_arg(2);
22513       }
22514 
22515       static double mp_set_ioff(_cimg_math_parser& mp) {
22516         CImg<T> &img = mp.imgout;
22517         const longT
22518           off = (longT)_mp_arg(2),
22519           whds = (longT)img.size();
22520         const double val = _mp_arg(1);
22521         if (off>=0 && off<whds) img[off] = (T)val;
22522         return val;
22523       }
22524 
22525       static double mp_set_ixyzc(_cimg_math_parser& mp) {
22526         CImg<T> &img = mp.imgout;
22527         const int
22528           x = (int)_mp_arg(2), y = (int)_mp_arg(3),
22529           z = (int)_mp_arg(4), c = (int)_mp_arg(5);
22530         const double val = _mp_arg(1);
22531         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
22532             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
22533           img(x,y,z,c) = (T)val;
22534         return val;
22535       }
22536 
22537       static double mp_set_joff(_cimg_math_parser& mp) {
22538         CImg<T> &img = mp.imgout;
22539         const int
22540           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22541           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22542         const longT
22543           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22544           whds = (longT)img.size();
22545         const double val = _mp_arg(1);
22546         if (off>=0 && off<whds) img[off] = (T)val;
22547         return val;
22548       }
22549 
22550       static double mp_set_jxyzc(_cimg_math_parser& mp) {
22551         CImg<T> &img = mp.imgout;
22552         const double
22553           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
22554           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
22555         const int
22556           x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
22557           z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
22558         const double val = _mp_arg(1);
22559         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
22560             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
22561           img(x,y,z,c) = (T)val;
22562         return val;
22563       }
22564 
22565       static double mp_set_Ioff_s(_cimg_math_parser& mp) {
22566         CImg<T> &img = mp.imgout;
22567         const longT
22568           off = (longT)_mp_arg(2),
22569           whd = (longT)img.width()*img.height()*img.depth();
22570         const T val = (T)_mp_arg(1);
22571         if (off>=0 && off<whd) {
22572           T *ptrd = &img[off];
22573           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22574         }
22575         return _mp_arg(1);
22576       }
22577 
22578       static double mp_set_Ioff_v(_cimg_math_parser& mp) {
22579         CImg<T> &img = mp.imgout;
22580         const longT
22581           off = (longT)_mp_arg(2),
22582           whd = (longT)img.width()*img.height()*img.depth();
22583         const double *ptrs = &_mp_arg(1) + 1;
22584         if (off>=0 && off<whd) {
22585           const unsigned int vsiz = (unsigned int)mp.opcode[3];
22586           T *ptrd = &img[off];
22587           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22588         }
22589         return cimg::type<double>::nan();
22590       }
22591 
22592       static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
22593         CImg<T> &img = mp.imgout;
22594         const int
22595           x = (int)_mp_arg(2),
22596           y = (int)_mp_arg(3),
22597           z = (int)_mp_arg(4);
22598         const T val = (T)_mp_arg(1);
22599         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22600           T *ptrd = &img(x,y,z);
22601           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22602           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22603         }
22604         return _mp_arg(1);
22605       }
22606 
22607       static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
22608         CImg<T> &img = mp.imgout;
22609         const int
22610           x = (int)_mp_arg(2),
22611           y = (int)_mp_arg(3),
22612           z = (int)_mp_arg(4);
22613         const double *ptrs = &_mp_arg(1) + 1;
22614         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22615           const unsigned int vsiz = (unsigned int)mp.opcode[5];
22616           T *ptrd = &img(x,y,z);
22617           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22618           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22619         }
22620         return cimg::type<double>::nan();
22621       }
22622 
22623       static double mp_set_Joff_s(_cimg_math_parser& mp) {
22624         CImg<T> &img = mp.imgout;
22625         const int
22626           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22627           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22628         const longT
22629           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22630           whd = (longT)img.width()*img.height()*img.depth();
22631         const T val = (T)_mp_arg(1);
22632         if (off>=0 && off<whd) {
22633           T *ptrd = &img[off];
22634           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22635         }
22636         return _mp_arg(1);
22637       }
22638 
22639       static double mp_set_Joff_v(_cimg_math_parser& mp) {
22640         CImg<T> &img = mp.imgout;
22641         const int
22642           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22643           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22644         const longT
22645           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22646           whd = (longT)img.width()*img.height()*img.depth();
22647         const double *ptrs = &_mp_arg(1) + 1;
22648         if (off>=0 && off<whd) {
22649           const unsigned int vsiz = (unsigned int)mp.opcode[3];
22650           T *ptrd = &img[off];
22651           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22652         }
22653         return cimg::type<double>::nan();
22654       }
22655 
22656       static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
22657         CImg<T> &img = mp.imgout;
22658         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
22659         const int
22660           x = (int)(ox + _mp_arg(2)),
22661           y = (int)(oy + _mp_arg(3)),
22662           z = (int)(oz + _mp_arg(4));
22663         const T val = (T)_mp_arg(1);
22664         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22665           T *ptrd = &img(x,y,z);
22666           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22667           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22668         }
22669         return _mp_arg(1);
22670       }
22671 
22672       static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
22673         CImg<T> &img = mp.imgout;
22674         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
22675         const int
22676           x = (int)(ox + _mp_arg(2)),
22677           y = (int)(oy + _mp_arg(3)),
22678           z = (int)(oz + _mp_arg(4));
22679         const double *ptrs = &_mp_arg(1) + 1;
22680         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22681           const unsigned int vsiz = (unsigned int)mp.opcode[5];
22682           T *ptrd = &img(x,y,z);
22683           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22684           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22685         }
22686         return cimg::type<double>::nan();
22687       }
22688 
22689       static double mp_shift(_cimg_math_parser& mp) {
22690         double *const ptrd = &_mp_arg(1) + 1;
22691         const double *const ptrs = &_mp_arg(2) + 1;
22692         const unsigned int siz = (unsigned int)mp.opcode[3];
22693         const int
22694           shift = (int)_mp_arg(4),
22695           boundary_conditions = (int)_mp_arg(5);
22696         CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
22697         return cimg::type<double>::nan();
22698       }
22699 
22700       static double mp_sign(_cimg_math_parser& mp) {
22701         return cimg::sign(_mp_arg(2));
22702       }
22703 
22704       static double mp_sin(_cimg_math_parser& mp) {
22705         return std::sin(_mp_arg(2));
22706       }
22707 
22708       static double mp_sinc(_cimg_math_parser& mp) {
22709         return cimg::sinc(_mp_arg(2));
22710       }
22711 
22712       static double mp_sinh(_cimg_math_parser& mp) {
22713         return std::sinh(_mp_arg(2));
22714       }
22715 
22716       static double mp_solve(_cimg_math_parser& mp) {
22717         double *ptrd = &_mp_arg(1) + 1;
22718         const double
22719           *ptr1 = &_mp_arg(2) + 1,
22720           *ptr2 = &_mp_arg(3) + 1;
22721         const unsigned int
22722           k = (unsigned int)mp.opcode[4],
22723           l = (unsigned int)mp.opcode[5],
22724           m = (unsigned int)mp.opcode[6];
22725         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));
22726         return cimg::type<double>::nan();
22727       }
22728 
22729       static double mp_sort(_cimg_math_parser& mp) {
22730         double *const ptrd = &_mp_arg(1) + 1;
22731         const double *const ptrs = &_mp_arg(2) + 1;
22732         const unsigned int
22733           siz = (unsigned int)mp.opcode[3],
22734           chunk_siz = (unsigned int)mp.opcode[5];
22735         const bool is_increasing = (bool)_mp_arg(4);
22736         CImg<doubleT>(ptrd,chunk_siz,siz/chunk_siz,1,1,true) = CImg<doubleT>(ptrs,chunk_siz,siz/chunk_siz,1,1,true).
22737           get_sort(is_increasing,chunk_siz>1?'y':0);
22738         return cimg::type<double>::nan();
22739       }
22740 
22741       static double mp_sqr(_cimg_math_parser& mp) {
22742         return cimg::sqr(_mp_arg(2));
22743       }
22744 
22745       static double mp_sqrt(_cimg_math_parser& mp) {
22746         return std::sqrt(_mp_arg(2));
22747       }
22748 
22749       static double mp_srand(_cimg_math_parser& mp) {
22750         return cimg::srand((unsigned int)_mp_arg(2));
22751       }
22752 
22753       static double mp_srand0(_cimg_math_parser& mp) {
22754         cimg::unused(mp);
22755         return cimg::srand();
22756       }
22757 
22758       static double mp_std(_cimg_math_parser& mp) {
22759         const unsigned int i_end = (unsigned int)mp.opcode[2];
22760         CImg<doubleT> vals(i_end - 3);
22761         double *p = vals.data();
22762         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
22763         return std::sqrt(vals.variance());
22764       }
22765 
22766       static double mp_string_init(_cimg_math_parser& mp) {
22767         const char *ptrs = (char*)&mp.opcode[3];
22768         unsigned int
22769           ptrd = (unsigned int)mp.opcode[1] + 1,
22770           siz = (unsigned int)mp.opcode[2];
22771         while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
22772         return cimg::type<double>::nan();
22773       }
22774 
22775       static double mp_stov(_cimg_math_parser& mp) {
22776         const double *ptrs = &_mp_arg(2);
22777         const ulongT siz = (ulongT)mp.opcode[3];
22778         longT ind = (longT)_mp_arg(4);
22779         const bool is_strict = (bool)_mp_arg(5);
22780         double val = cimg::type<double>::nan();
22781         if (ind<0 || ind>=(longT)siz) return val;
22782         if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
22783 
22784         CImg<charT> ss(siz + 1 - ind);
22785         char sep;
22786         ptrs+=1 + ind; cimg_forX(ss,i) ss[i] = (char)*(ptrs++); ss.back() = 0;
22787 
22788         int err = std::sscanf(ss,"%lf%c",&val,&sep);
22789 #if cimg_OS==2
22790         // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
22791         // to read those particular values.
22792         if (!err && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
22793           bool is_positive = true;
22794           const char *s = ss;
22795           if (*s=='+') ++s; else if (*s=='-') { ++s; is_positive = false; }
22796           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); err = 1; }
22797           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); err = 1; }
22798           if (err==1 && !is_positive) val = -val;
22799         }
22800 #endif
22801         if (is_strict && err!=1) return cimg::type<double>::nan();
22802         return val;
22803       }
22804 
22805       static double mp_sub(_cimg_math_parser& mp) {
22806         return _mp_arg(2) - _mp_arg(3);
22807       }
22808 
22809       static double mp_sum(_cimg_math_parser& mp) {
22810         const unsigned int i_end = (unsigned int)mp.opcode[2];
22811         double val = _mp_arg(3);
22812         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
22813         return val;
22814       }
22815 
22816       static double mp_tan(_cimg_math_parser& mp) {
22817         return std::tan(_mp_arg(2));
22818       }
22819 
22820       static double mp_tanh(_cimg_math_parser& mp) {
22821         return std::tanh(_mp_arg(2));
22822       }
22823 
22824       static double mp_trace(_cimg_math_parser& mp) {
22825         const double *ptrs = &_mp_arg(2) + 1;
22826         const unsigned int k = (unsigned int)mp.opcode[3];
22827         return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
22828       }
22829 
22830       static double mp_transp(_cimg_math_parser& mp) {
22831         double *ptrd = &_mp_arg(1) + 1;
22832         const double *ptrs = &_mp_arg(2) + 1;
22833         const unsigned int
22834           k = (unsigned int)mp.opcode[3],
22835           l = (unsigned int)mp.opcode[4];
22836         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
22837         return cimg::type<double>::nan();
22838       }
22839 
22840       static double mp_u(_cimg_math_parser& mp) {
22841         return cimg::rand(_mp_arg(2),_mp_arg(3));
22842       }
22843 
22844       static double mp_uppercase(_cimg_math_parser& mp) {
22845         return cimg::uppercase(_mp_arg(2));
22846       }
22847 
22848       static double mp_variance(_cimg_math_parser& mp) {
22849         const unsigned int i_end = (unsigned int)mp.opcode[2];
22850         CImg<doubleT> vals(i_end - 3);
22851         double *p = vals.data();
22852         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
22853         return vals.variance();
22854       }
22855 
22856       static double mp_vector_copy(_cimg_math_parser& mp) {
22857         std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
22858         return cimg::type<double>::nan();
22859       }
22860 
22861       static double mp_vector_crop(_cimg_math_parser& mp) {
22862         double *const ptrd = &_mp_arg(1) + 1;
22863         const double *const ptrs = &_mp_arg(2) + 1;
22864         const longT
22865           length = (longT)mp.opcode[3],
22866           start = (longT)_mp_arg(4),
22867           sublength = (longT)mp.opcode[5];
22868         if (start<0 || start + sublength>length)
22869           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
22870                                       "Out-of-bounds sub-vector request "
22871                                       "(length: %ld, start: %ld, sub-length: %ld).",
22872                                       mp.imgin.pixel_type(),length,start,sublength);
22873         std::memcpy(ptrd,ptrs + start,sublength*sizeof(double));
22874         return cimg::type<double>::nan();
22875       }
22876 
22877       static double mp_vector_init(_cimg_math_parser& mp) {
22878         unsigned int
22879           ptrs = 4U,
22880           ptrd = (unsigned int)mp.opcode[1] + 1,
22881           siz = (unsigned int)mp.opcode[3];
22882         switch (mp.opcode[2] - 4) {
22883         case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
22884         case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
22885         default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
22886         }
22887         return cimg::type<double>::nan();
22888       }
22889 
22890       static double mp_vector_eq(_cimg_math_parser& mp) {
22891         const double
22892           *ptr1 = &_mp_arg(2) + 1,
22893           *ptr2 = &_mp_arg(4) + 1;
22894         unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
22895         const int N = (int)_mp_arg(6);
22896         const bool case_sensitive = (bool)_mp_arg(7);
22897         bool still_equal = true;
22898         double value;
22899         if (!N) return true;
22900 
22901         // Compare all values.
22902         if (N<0) {
22903           if (p1>0 && p2>0) { // Vector == vector
22904             if (p1!=p2) return false;
22905             if (case_sensitive)
22906               while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
22907             else
22908               while (still_equal && p1--)
22909                 still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
22910             return still_equal;
22911           } else if (p1>0 && !p2) { // Vector == scalar
22912             value = _mp_arg(4);
22913             if (!case_sensitive) value = cimg::lowercase(value);
22914             while (still_equal && p1--) still_equal = *(ptr1++)==value;
22915             return still_equal;
22916           } else if (!p1 && p2>0) { // Scalar == vector
22917             value = _mp_arg(2);
22918             if (!case_sensitive) value = cimg::lowercase(value);
22919             while (still_equal && p2--) still_equal = *(ptr2++)==value;
22920             return still_equal;
22921           } else { // Scalar == scalar
22922             if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
22923             else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
22924           }
22925         }
22926 
22927         // Compare only first N values.
22928         if (p1>0 && p2>0) { // Vector == vector
22929           n = cimg::min((unsigned int)N,p1,p2);
22930           if (case_sensitive)
22931             while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
22932           else
22933             while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
22934           return still_equal;
22935         } else if (p1>0 && !p2) { // Vector == scalar
22936           n = std::min((unsigned int)N,p1);
22937           value = _mp_arg(4);
22938           if (!case_sensitive) value = cimg::lowercase(value);
22939           while (still_equal && n--) still_equal = *(ptr1++)==value;
22940           return still_equal;
22941         } else if (!p1 && p2>0) { // Scalar == vector
22942           n = std::min((unsigned int)N,p2);
22943           value = _mp_arg(2);
22944           if (!case_sensitive) value = cimg::lowercase(value);
22945           while (still_equal && n--) still_equal = *(ptr2++)==value;
22946           return still_equal;
22947         }  // Scalar == scalar
22948         if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
22949         return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
22950       }
22951 
22952       static double mp_vector_off(_cimg_math_parser& mp) {
22953         const unsigned int
22954           ptr = (unsigned int)mp.opcode[2] + 1,
22955           siz = (unsigned int)mp.opcode[3];
22956         const int off = (int)_mp_arg(4);
22957         return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
22958       }
22959 
22960       static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
22961         unsigned int
22962           siz = (unsigned int)mp.opcode[2],
22963           ptrs = (unsigned int)mp.opcode[5] + 1;
22964         double *ptrd = &_mp_arg(1) + 1;
22965         mp_func op = (mp_func)mp.opcode[3];
22966         CImg<ulongT> l_opcode(4);
22967         l_opcode[2] = mp.opcode[4]; // Scalar argument1
22968         l_opcode.swap(mp.opcode);
22969         ulongT &argument2 = mp.opcode[3];
22970         while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
22971         l_opcode.swap(mp.opcode);
22972         return cimg::type<double>::nan();
22973       }
22974 
22975       static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
22976         unsigned int
22977           siz = (unsigned int)mp.opcode[2],
22978           ptrs = (unsigned int)mp.opcode[4] + 1;
22979         double *ptrd = &_mp_arg(1) + 1;
22980         mp_func op = (mp_func)mp.opcode[3];
22981         CImg<ulongT> l_opcode(1,3);
22982         l_opcode.swap(mp.opcode);
22983         ulongT &argument = mp.opcode[2];
22984         while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
22985         l_opcode.swap(mp.opcode);
22986         return cimg::type<double>::nan();
22987       }
22988 
22989       static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
22990         unsigned int
22991           siz = (unsigned int)mp.opcode[2],
22992           ptrs = (unsigned int)mp.opcode[4] + 1;
22993         double *ptrd = &_mp_arg(1) + 1;
22994         mp_func op = (mp_func)mp.opcode[3];
22995         CImg<ulongT> l_opcode(1,4);
22996         l_opcode[3] = mp.opcode[5]; // Scalar argument2
22997         l_opcode.swap(mp.opcode);
22998         ulongT &argument1 = mp.opcode[2];
22999         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
23000         l_opcode.swap(mp.opcode);
23001         return cimg::type<double>::nan();
23002       }
23003 
23004       static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
23005         unsigned int
23006           siz = (unsigned int)mp.opcode[2],
23007           ptrs = (unsigned int)mp.opcode[4] + 1;
23008         double *ptrd = &_mp_arg(1) + 1;
23009         mp_func op = (mp_func)mp.opcode[3];
23010         CImg<ulongT> l_opcode(1,5);
23011         l_opcode[3] = mp.opcode[5]; // Scalar argument2
23012         l_opcode[4] = mp.opcode[6]; // Scalar argument3
23013         l_opcode.swap(mp.opcode);
23014         ulongT &argument1 = mp.opcode[2];
23015         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
23016         l_opcode.swap(mp.opcode);
23017         return cimg::type<double>::nan();
23018       }
23019 
23020       static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
23021         unsigned int
23022           siz = (unsigned int)mp.opcode[2],
23023           ptrs1 = (unsigned int)mp.opcode[4] + 1,
23024           ptrs2 = (unsigned int)mp.opcode[5] + 1;
23025         double *ptrd = &_mp_arg(1) + 1;
23026         mp_func op = (mp_func)mp.opcode[3];
23027         CImg<ulongT> l_opcode(1,4);
23028         l_opcode.swap(mp.opcode);
23029         ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
23030         while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
23031         l_opcode.swap(mp.opcode);
23032         return cimg::type<double>::nan();
23033       }
23034 
23035       static double mp_vector_neq(_cimg_math_parser& mp) {
23036         return !mp_vector_eq(mp);
23037       }
23038 
23039       static double mp_vector_print(_cimg_math_parser& mp) {
23040         const bool print_string = (bool)mp.opcode[4];
23041         cimg_pragma_openmp(critical(mp_vector_print))
23042         {
23043           CImg<charT> expr(mp.opcode[2] - 5);
23044           const ulongT *ptrs = mp.opcode._data + 5;
23045           cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23046           cimg::strellipsize(expr);
23047           unsigned int
23048             ptr = (unsigned int)mp.opcode[1] + 1,
23049             siz0 = (unsigned int)mp.opcode[3],
23050             siz = siz0;
23051           cimg::mutex(6);
23052           std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",expr._data);
23053           unsigned int count = 0;
23054           while (siz-->0) {
23055             if (count>=64 && siz>=64) {
23056               std::fprintf(cimg::output(),"...,");
23057               ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
23058               siz = 64;
23059             } else std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":"");
23060             ++count;
23061           }
23062           if (print_string) {
23063             CImg<charT> str(siz0 + 1);
23064             ptr = (unsigned int)mp.opcode[1] + 1;
23065             for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
23066             str[siz0] = 0;
23067             cimg::strellipsize(str,1024,false);
23068             std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
23069           } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
23070           std::fflush(cimg::output());
23071           cimg::mutex(6,0);
23072         }
23073         return cimg::type<double>::nan();
23074       }
23075 
23076       static double mp_vector_resize(_cimg_math_parser& mp) {
23077         double *const ptrd = &_mp_arg(1) + 1;
23078         const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
23079         const int
23080           interpolation = (int)_mp_arg(5),
23081           boundary_conditions = (int)_mp_arg(6);
23082         if (p2) { // Resize vector
23083           const double *const ptrs = &_mp_arg(3) + 1;
23084           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
23085             get_resize(p1,1,1,1,interpolation,boundary_conditions);
23086         } else { // Resize scalar
23087           const double value = _mp_arg(3);
23088           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
23089                                                                                   boundary_conditions);
23090         }
23091         return cimg::type<double>::nan();
23092       }
23093 
23094       static double mp_vector_reverse(_cimg_math_parser& mp) {
23095         double *const ptrd = &_mp_arg(1) + 1;
23096         const double *const ptrs = &_mp_arg(2) + 1;
23097         const unsigned int p1 = (unsigned int)mp.opcode[3];
23098         CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
23099         return cimg::type<double>::nan();
23100       }
23101 
23102       static double mp_vector_set_off(_cimg_math_parser& mp) {
23103         const unsigned int
23104           ptr = (unsigned int)mp.opcode[2] + 1,
23105           siz = (unsigned int)mp.opcode[3];
23106         const int off = (int)_mp_arg(4);
23107         if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5);
23108         return _mp_arg(5);
23109       }
23110 
23111       static double mp_vtos(_cimg_math_parser& mp) {
23112         double *ptrd = &_mp_arg(1) + 1;
23113         const unsigned int
23114           sizd = (unsigned int)mp.opcode[2],
23115           sizs = (unsigned int)mp.opcode[4];
23116         const int nb_digits = (int)_mp_arg(5);
23117         CImg<charT> format(8);
23118         switch (nb_digits) {
23119         case -1 : std::strcpy(format,"%g"); break;
23120         case 0 : std::strcpy(format,"%.17g"); break;
23121         default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
23122         }
23123         CImg<charT> str;
23124         if (sizs) { // Vector expression
23125           const double *ptrs = &_mp_arg(3) + 1;
23126           CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
23127         } else { // Scalar expression
23128           str.assign(sizd + 1);
23129           cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
23130         }
23131         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
23132         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
23133         return cimg::type<double>::nan();
23134       }
23135 
23136       static double mp_whiledo(_cimg_math_parser& mp) {
23137         const ulongT
23138           mem_body = mp.opcode[1],
23139           mem_cond = mp.opcode[2];
23140         const CImg<ulongT>
23141           *const p_cond = ++mp.p_code,
23142           *const p_body = p_cond + mp.opcode[3],
23143           *const p_end = p_body + mp.opcode[4];
23144         const unsigned int vsiz = (unsigned int)mp.opcode[5];
23145         bool is_cond = false;
23146         if (mp.opcode[6]) { // Set default value for result and condition if necessary
23147           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
23148           else mp.mem[mem_body] = cimg::type<double>::nan();
23149         }
23150         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
23151         const unsigned int _break_type = mp.break_type;
23152         mp.break_type = 0;
23153         do {
23154           for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
23155             mp.opcode._data = mp.p_code->_data;
23156             const ulongT target = mp.opcode[1];
23157             mp.mem[target] = _cimg_mp_defunc(mp);
23158           }
23159           if (mp.break_type==1) break;
23160           is_cond = (bool)mp.mem[mem_cond];
23161           if (is_cond && !mp.break_type) // Evaluate body
23162             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
23163               mp.opcode._data = mp.p_code->_data;
23164               const ulongT target = mp.opcode[1];
23165               mp.mem[target] = _cimg_mp_defunc(mp);
23166             }
23167           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23168         } while (is_cond);
23169 
23170         mp.break_type = _break_type;
23171         mp.p_code = p_end - 1;
23172         return mp.mem[mem_body];
23173       }
23174 
23175       static double mp_Ioff(_cimg_math_parser& mp) {
23176         double *ptrd = &_mp_arg(1) + 1;
23177         const unsigned int
23178           boundary_conditions = (unsigned int)_mp_arg(3),
23179           vsiz = (unsigned int)mp.opcode[4];
23180         const CImg<T> &img = mp.imgin;
23181         const longT
23182           off = (longT)_mp_arg(2),
23183           whd = (longT)img.width()*img.height()*img.depth();
23184         const T *ptrs;
23185         if (off>=0 && off<whd) {
23186           ptrs = &img[off];
23187           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23188           return cimg::type<double>::nan();
23189         }
23190         if (img._data) switch (boundary_conditions) {
23191           case 3 : { // Mirror
23192             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
23193             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
23194             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23195             return cimg::type<double>::nan();
23196           }
23197           case 2 : // Periodic
23198             ptrs = &img[cimg::mod(off,whd)];
23199             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23200             return cimg::type<double>::nan();
23201           case 1 : // Neumann
23202             ptrs = off<0?&img[0]:&img[whd - 1];
23203             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23204             return cimg::type<double>::nan();
23205           default : // Dirichlet
23206             std::memset(ptrd,0,vsiz*sizeof(double));
23207             return cimg::type<double>::nan();
23208           }
23209         std::memset(ptrd,0,vsiz*sizeof(double));
23210         return cimg::type<double>::nan();
23211       }
23212 
23213       static double mp_Ixyz(_cimg_math_parser& mp) {
23214         double *ptrd = &_mp_arg(1) + 1;
23215         const unsigned int
23216           interpolation = (unsigned int)_mp_arg(5),
23217           boundary_conditions = (unsigned int)_mp_arg(6),
23218           vsiz = (unsigned int)mp.opcode[7];
23219         const CImg<T> &img = mp.imgin;
23220         const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
23221         const ulongT whd = (ulongT)img._width*img._height*img._depth;
23222         const T *ptrs;
23223         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
23224           case 3 : { // Mirror
23225             const int
23226               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
23227               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
23228               cx = mx<img.width()?mx:w2 - mx - 1,
23229               cy = my<img.height()?my:h2 - my - 1,
23230               cz = mz<img.depth()?mz:d2 - mz - 1;
23231             ptrs = &img(cx,cy,cz);
23232             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23233           } break;
23234           case 2 : { // Periodic
23235             const int
23236               cx = cimg::mod((int)x,img.width()),
23237               cy = cimg::mod((int)y,img.height()),
23238               cz = cimg::mod((int)z,img.depth());
23239             ptrs = &img(cx,cy,cz);
23240             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23241           } break;
23242           case 1 : { // Neumann
23243             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
23244             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23245           } break;
23246           default : // Dirichlet
23247             if (img.containsXYZC(x,y,z)) {
23248               ptrs = &img((int)x,(int)y,(int)z);
23249               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23250             } else std::memset(ptrd,0,vsiz*sizeof(double));
23251           } else switch (boundary_conditions) { // Linear interpolation
23252           case 3 : { // Mirror
23253             const float
23254               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
23255               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
23256               cx = mx<img.width()?mx:w2 - mx - 1,
23257               cy = my<img.height()?my:h2 - my - 1,
23258               cz = mz<img.depth()?mz:d2 - mz - 1;
23259             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23260           } break;
23261           case 2 : { // Periodic
23262             const float
23263               cx = cimg::mod((float)x,(float)img.width()),
23264               cy = cimg::mod((float)y,(float)img.height()),
23265               cz = cimg::mod((float)z,(float)img.depth());
23266             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23267           } break;
23268           case 1 : // Neumann
23269             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
23270             break;
23271           default : // Dirichlet
23272             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
23273           }
23274         return cimg::type<double>::nan();
23275       }
23276 
23277       static double mp_Joff(_cimg_math_parser& mp) {
23278         double *ptrd = &_mp_arg(1) + 1;
23279         const unsigned int
23280           boundary_conditions = (unsigned int)_mp_arg(3),
23281           vsiz = (unsigned int)mp.opcode[4];
23282         const CImg<T> &img = mp.imgin;
23283         const int
23284           ox = (int)mp.mem[_cimg_mp_slot_x],
23285           oy = (int)mp.mem[_cimg_mp_slot_y],
23286           oz = (int)mp.mem[_cimg_mp_slot_z];
23287         const longT
23288           off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
23289           whd = (longT)img.width()*img.height()*img.depth();
23290         const T *ptrs;
23291         if (off>=0 && off<whd) {
23292           ptrs = &img[off];
23293           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23294           return cimg::type<double>::nan();
23295         }
23296         if (img._data) switch (boundary_conditions) {
23297           case 3 : { // Mirror
23298             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
23299             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
23300             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23301             return cimg::type<double>::nan();
23302           }
23303           case 2 : // Periodic
23304             ptrs = &img[cimg::mod(off,whd)];
23305             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23306             return cimg::type<double>::nan();
23307           case 1 : // Neumann
23308             ptrs = off<0?&img[0]:&img[whd - 1];
23309             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23310             return cimg::type<double>::nan();
23311           default : // Dirichlet
23312             std::memset(ptrd,0,vsiz*sizeof(double));
23313             return cimg::type<double>::nan();
23314           }
23315         std::memset(ptrd,0,vsiz*sizeof(double));
23316         return cimg::type<double>::nan();
23317       }
23318 
23319       static double mp_Jxyz(_cimg_math_parser& mp) {
23320         double *ptrd = &_mp_arg(1) + 1;
23321         const unsigned int
23322           interpolation = (unsigned int)_mp_arg(5),
23323           boundary_conditions = (unsigned int)_mp_arg(6),
23324           vsiz = (unsigned int)mp.opcode[7];
23325         const CImg<T> &img = mp.imgin;
23326         const double
23327           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
23328           x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
23329         const ulongT whd = (ulongT)img._width*img._height*img._depth;
23330         const T *ptrs;
23331         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
23332           case 3 : { // Mirror
23333             const int
23334               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
23335               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
23336               cx = mx<img.width()?mx:w2 - mx - 1,
23337               cy = my<img.height()?my:h2 - my - 1,
23338               cz = mz<img.depth()?mz:d2 - mz - 1;
23339             ptrs = &img(cx,cy,cz);
23340             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23341           } break;
23342           case 2 : { // Periodic
23343             const int
23344               cx = cimg::mod((int)x,img.width()),
23345               cy = cimg::mod((int)y,img.height()),
23346               cz = cimg::mod((int)z,img.depth());
23347             ptrs = &img(cx,cy,cz);
23348             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23349           } break;
23350           case 1 : { // Neumann
23351             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
23352             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23353           } break;
23354           default : // Dirichlet
23355             if (img.containsXYZC(x,y,z)) {
23356               ptrs = &img((int)x,(int)y,(int)z);
23357               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23358             } else std::memset(ptrd,0,vsiz*sizeof(double));
23359           } else switch (boundary_conditions) { // Linear interpolation
23360           case 3 : { // Mirror
23361             const float
23362               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
23363               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
23364               cx = mx<img.width()?mx:w2 - mx - 1,
23365               cy = my<img.height()?my:h2 - my - 1,
23366               cz = mz<img.depth()?mz:d2 - mz - 1;
23367             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23368           } break;
23369           case 2 : { // Periodic
23370             const float
23371               cx = cimg::mod((float)x,(float)img.width()),
23372               cy = cimg::mod((float)y,(float)img.height()),
23373               cz = cimg::mod((float)z,(float)img.depth());
23374             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23375           } break;
23376           case 1 : // Neumann
23377             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
23378             break;
23379           default : // Dirichlet
23380             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
23381           }
23382         return cimg::type<double>::nan();
23383       }
23384 
23385 #undef _mp_arg
23386 
23387     }; // struct _cimg_math_parser {}
23388 
23389     //! Compute the square value of each pixel value.
23390     /**
23391        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$.
23392        \note
23393        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23394        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23395        \par Example
23396        \code
23397        const CImg<float> img("reference.jpg");
23398        (img,img.get_sqr().normalize(0,255)).display();
23399        \endcode
23400        \image html ref_sqr.jpg
23401     **/
23402     CImg<T>& sqr() {
23403       if (is_empty()) return *this;
23404       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
23405       cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
23406       return *this;
23407     }
23408 
23409     //! Compute the square value of each pixel value \newinstance.
23410     CImg<Tfloat> get_sqr() const {
23411       return CImg<Tfloat>(*this,false).sqr();
23412     }
23413 
23414     //! Compute the square root of each pixel value.
23415     /**
23416        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$.
23417        \note
23418        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23419        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23420        \par Example
23421        \code
23422        const CImg<float> img("reference.jpg");
23423        (img,img.get_sqrt().normalize(0,255)).display();
23424        \endcode
23425        \image html ref_sqrt.jpg
23426     **/
23427     CImg<T>& sqrt() {
23428       if (is_empty()) return *this;
23429       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23430       cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
23431       return *this;
23432     }
23433 
23434     //! Compute the square root of each pixel value \newinstance.
23435     CImg<Tfloat> get_sqrt() const {
23436       return CImg<Tfloat>(*this,false).sqrt();
23437     }
23438 
23439     //! Compute the exponential of each pixel value.
23440     /**
23441        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$.
23442        \note
23443        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23444        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23445     **/
23446     CImg<T>& exp() {
23447       if (is_empty()) return *this;
23448       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
23449       cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
23450       return *this;
23451     }
23452 
23453     //! Compute the exponential of each pixel value \newinstance.
23454     CImg<Tfloat> get_exp() const {
23455       return CImg<Tfloat>(*this,false).exp();
23456     }
23457 
23458     //! Compute the logarithm of each pixel value.
23459     /**
23460        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
23461        \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
23462        \note
23463        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23464        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23465     **/
23466     CImg<T>& log() {
23467       if (is_empty()) return *this;
23468       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
23469       cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
23470       return *this;
23471     }
23472 
23473     //! Compute the logarithm of each pixel value \newinstance.
23474     CImg<Tfloat> get_log() const {
23475       return CImg<Tfloat>(*this,false).log();
23476     }
23477 
23478     //! Compute the base-2 logarithm of each pixel value.
23479     /**
23480        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
23481        \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
23482        \note
23483        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23484        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23485     **/
23486     CImg<T>& log2() {
23487       if (is_empty()) return *this;
23488       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
23489       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd);
23490       return *this;
23491     }
23492 
23493     //! Compute the base-10 logarithm of each pixel value \newinstance.
23494     CImg<Tfloat> get_log2() const {
23495       return CImg<Tfloat>(*this,false).log2();
23496     }
23497 
23498     //! Compute the base-10 logarithm of each pixel value.
23499     /**
23500        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
23501        \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
23502        \note
23503        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23504        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23505     **/
23506     CImg<T>& log10() {
23507       if (is_empty()) return *this;
23508       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
23509       cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
23510       return *this;
23511     }
23512 
23513     //! Compute the base-10 logarithm of each pixel value \newinstance.
23514     CImg<Tfloat> get_log10() const {
23515       return CImg<Tfloat>(*this,false).log10();
23516     }
23517 
23518     //! Compute the absolute value of each pixel value.
23519     /**
23520        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$.
23521        \note
23522        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23523        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23524     **/
23525     CImg<T>& abs() {
23526       if (is_empty()) return *this;
23527       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
23528       cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
23529       return *this;
23530     }
23531 
23532     //! Compute the absolute value of each pixel value \newinstance.
23533     CImg<Tfloat> get_abs() const {
23534       return CImg<Tfloat>(*this,false).abs();
23535     }
23536 
23537     //! Compute the sign of each pixel value.
23538     /**
23539        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
23540        \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
23541        \note
23542        - The sign is set to:
23543          - \c 1 if pixel value is strictly positive.
23544          - \c -1 if pixel value is strictly negative.
23545          - \c 0 if pixel value is equal to \c 0.
23546        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23547        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23548     **/
23549     CImg<T>& sign() {
23550       if (is_empty()) return *this;
23551       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23552       cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
23553       return *this;
23554     }
23555 
23556     //! Compute the sign of each pixel value \newinstance.
23557     CImg<Tfloat> get_sign() const {
23558       return CImg<Tfloat>(*this,false).sign();
23559     }
23560 
23561     //! Compute the cosine of each pixel value.
23562     /**
23563        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$.
23564        \note
23565        - Pixel values are regarded as being in \e radian.
23566        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23567        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23568     **/
23569     CImg<T>& cos() {
23570       if (is_empty()) return *this;
23571       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23572       cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
23573       return *this;
23574     }
23575 
23576     //! Compute the cosine of each pixel value \newinstance.
23577     CImg<Tfloat> get_cos() const {
23578       return CImg<Tfloat>(*this,false).cos();
23579     }
23580 
23581     //! Compute the sine of each pixel value.
23582     /**
23583        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$.
23584        \note
23585        - Pixel values are regarded as being in \e radian.
23586        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23587        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23588     **/
23589     CImg<T>& sin() {
23590       if (is_empty()) return *this;
23591       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23592       cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
23593       return *this;
23594     }
23595 
23596     //! Compute the sine of each pixel value \newinstance.
23597     CImg<Tfloat> get_sin() const {
23598       return CImg<Tfloat>(*this,false).sin();
23599     }
23600 
23601     //! Compute the sinc of each pixel value.
23602     /**
23603        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
23604        \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
23605        \note
23606        - Pixel values are regarded as being exin \e radian.
23607        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23608        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23609     **/
23610     CImg<T>& sinc() {
23611       if (is_empty()) return *this;
23612       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
23613       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
23614       return *this;
23615     }
23616 
23617     //! Compute the sinc of each pixel value \newinstance.
23618     CImg<Tfloat> get_sinc() const {
23619       return CImg<Tfloat>(*this,false).sinc();
23620     }
23621 
23622     //! Compute the tangent of each pixel value.
23623     /**
23624        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$.
23625        \note
23626        - Pixel values are regarded as being exin \e radian.
23627        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23628        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23629     **/
23630     CImg<T>& tan() {
23631       if (is_empty()) return *this;
23632       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
23633       cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
23634       return *this;
23635     }
23636 
23637     //! Compute the tangent of each pixel value \newinstance.
23638     CImg<Tfloat> get_tan() const {
23639       return CImg<Tfloat>(*this,false).tan();
23640     }
23641 
23642     //! Compute the hyperbolic cosine of each pixel value.
23643     /**
23644        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
23645        \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
23646        \note
23647        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23648        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23649     **/
23650     CImg<T>& cosh() {
23651       if (is_empty()) return *this;
23652       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
23653       cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
23654       return *this;
23655     }
23656 
23657     //! Compute the hyperbolic cosine of each pixel value \newinstance.
23658     CImg<Tfloat> get_cosh() const {
23659       return CImg<Tfloat>(*this,false).cosh();
23660     }
23661 
23662     //! Compute the hyperbolic sine of each pixel value.
23663     /**
23664        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
23665        \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
23666        \note
23667        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23668        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23669     **/
23670     CImg<T>& sinh() {
23671       if (is_empty()) return *this;
23672       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
23673       cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
23674       return *this;
23675     }
23676 
23677     //! Compute the hyperbolic sine of each pixel value \newinstance.
23678     CImg<Tfloat> get_sinh() const {
23679       return CImg<Tfloat>(*this,false).sinh();
23680     }
23681 
23682     //! Compute the hyperbolic tangent of each pixel value.
23683     /**
23684        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
23685        \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
23686        \note
23687        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23688        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23689     **/
23690     CImg<T>& tanh() {
23691       if (is_empty()) return *this;
23692       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
23693       cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
23694       return *this;
23695     }
23696 
23697     //! Compute the hyperbolic tangent of each pixel value \newinstance.
23698     CImg<Tfloat> get_tanh() const {
23699       return CImg<Tfloat>(*this,false).tanh();
23700     }
23701 
23702     //! Compute the arccosine of each pixel value.
23703     /**
23704        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
23705        \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
23706        \note
23707        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23708        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23709     **/
23710     CImg<T>& acos() {
23711       if (is_empty()) return *this;
23712       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23713       cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
23714       return *this;
23715     }
23716 
23717     //! Compute the arccosine of each pixel value \newinstance.
23718     CImg<Tfloat> get_acos() const {
23719       return CImg<Tfloat>(*this,false).acos();
23720     }
23721 
23722     //! Compute the arcsine of each pixel value.
23723     /**
23724        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
23725        \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
23726        \note
23727        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23728        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23729     **/
23730     CImg<T>& asin() {
23731       if (is_empty()) return *this;
23732       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23733       cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
23734       return *this;
23735     }
23736 
23737     //! Compute the arcsine of each pixel value \newinstance.
23738     CImg<Tfloat> get_asin() const {
23739       return CImg<Tfloat>(*this,false).asin();
23740     }
23741 
23742     //! Compute the arctangent of each pixel value.
23743     /**
23744        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
23745        \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
23746        \note
23747        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23748        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23749     **/
23750     CImg<T>& atan() {
23751       if (is_empty()) return *this;
23752       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23753       cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
23754       return *this;
23755     }
23756 
23757     //! Compute the arctangent of each pixel value \newinstance.
23758     CImg<Tfloat> get_atan() const {
23759       return CImg<Tfloat>(*this,false).atan();
23760     }
23761 
23762     //! Compute the arctangent2 of each pixel value.
23763     /**
23764        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
23765        \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
23766        \param img Image whose pixel values specify the second argument of the \c atan2() function.
23767        \note
23768        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23769        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23770        \par Example
23771        \code
23772        const CImg<float>
23773           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'.
23774           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'.
23775           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value.
23776        (img_x,img_y,img_atan2).display();
23777        \endcode
23778     **/
23779     template<typename t>
23780     CImg<T>& atan2(const CImg<t>& img) {
23781       const ulongT siz = size(), isiz = img.size();
23782       if (siz && isiz) {
23783         if (is_overlapped(img)) return atan2(+img);
23784         T *ptrd = _data, *const ptre = _data + siz;
23785         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23786           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23787             *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
23788         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
23789       }
23790       return *this;
23791     }
23792 
23793     //! Compute the arctangent2 of each pixel value \newinstance.
23794     template<typename t>
23795     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
23796       return CImg<Tfloat>(*this,false).atan2(img);
23797     }
23798 
23799     //! In-place pointwise multiplication.
23800     /**
23801        Compute the pointwise multiplication between the image instance and the specified input image \c img.
23802        \param img Input image, as the second operand of the multiplication.
23803        \note
23804        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
23805          instead of an addition.
23806        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
23807        \par Example
23808        \code
23809        CImg<float>
23810          img("reference.jpg"),
23811          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
23812        shade.normalize(0,1);
23813        (img,shade,img.get_mul(shade)).display();
23814        \endcode
23815     **/
23816     template<typename t>
23817     CImg<T>& mul(const CImg<t>& img) {
23818       const ulongT siz = size(), isiz = img.size();
23819       if (siz && isiz) {
23820         if (is_overlapped(img)) return mul(+img);
23821         T *ptrd = _data, *const ptre = _data + siz;
23822         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23823           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23824             *ptrd = (T)(*ptrd * *(ptrs++));
23825         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
23826       }
23827       return *this;
23828     }
23829 
23830     //! In-place pointwise multiplication \newinstance.
23831     template<typename t>
23832     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
23833       return CImg<_cimg_Tt>(*this,false).mul(img);
23834     }
23835 
23836     //! In-place pointwise division.
23837     /**
23838        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
23839     **/
23840     template<typename t>
23841     CImg<T>& div(const CImg<t>& img) {
23842       const ulongT siz = size(), isiz = img.size();
23843       if (siz && isiz) {
23844         if (is_overlapped(img)) return div(+img);
23845         T *ptrd = _data, *const ptre = _data + siz;
23846         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23847           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23848             *ptrd = (T)(*ptrd / *(ptrs++));
23849         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
23850       }
23851       return *this;
23852     }
23853 
23854     //! In-place pointwise division \newinstance.
23855     template<typename t>
23856     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
23857       return CImg<_cimg_Tt>(*this,false).div(img);
23858     }
23859 
23860     //! Raise each pixel value to a specified power.
23861     /**
23862        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$.
23863        \param p Exponent value.
23864        \note
23865        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23866        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23867        \par Example
23868        \code
23869        const CImg<float>
23870          img0("reference.jpg"),           // Load reference color image.
23871          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8.
23872          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5.
23873        (img0,img1,img2).display();
23874        \endcode
23875     **/
23876     CImg<T>& pow(const double p) {
23877       if (is_empty()) return *this;
23878       if (p==-4) {
23879         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23880         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); }
23881         return *this;
23882       }
23883       if (p==-3) {
23884         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23885         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); }
23886         return *this;
23887       }
23888       if (p==-2) {
23889         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23890         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); }
23891         return *this;
23892       }
23893       if (p==-1) {
23894         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23895         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); }
23896         return *this;
23897       }
23898       if (p==-0.5) {
23899         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23900         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); }
23901         return *this;
23902       }
23903       if (p==0) return fill((T)1);
23904       if (p==0.25) return sqrt().sqrt();
23905       if (p==0.5) return sqrt();
23906       if (p==1) return *this;
23907       if (p==2) return sqr();
23908       if (p==3) {
23909         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
23910         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; }
23911         return *this;
23912       }
23913       if (p==4) {
23914         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=131072))
23915         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; }
23916         return *this;
23917       }
23918       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1024))
23919       cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
23920       return *this;
23921     }
23922 
23923     //! Raise each pixel value to a specified power \newinstance.
23924     CImg<Tfloat> get_pow(const double p) const {
23925       return CImg<Tfloat>(*this,false).pow(p);
23926     }
23927 
23928     //! Raise each pixel value to a power, specified from an expression.
23929     /**
23930        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
23931     **/
23932     CImg<T>& pow(const char *const expression) {
23933       return pow((+*this)._fill(expression,true,true,0,0,"pow",this));
23934     }
23935 
23936     //! Raise each pixel value to a power, specified from an expression \newinstance.
23937     CImg<Tfloat> get_pow(const char *const expression) const {
23938       return CImg<Tfloat>(*this,false).pow(expression);
23939     }
23940 
23941     //! Raise each pixel value to a power, pointwisely specified from another image.
23942     /**
23943        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
23944     **/
23945     template<typename t>
23946     CImg<T>& pow(const CImg<t>& img) {
23947       const ulongT siz = size(), isiz = img.size();
23948       if (siz && isiz) {
23949         if (is_overlapped(img)) return pow(+img);
23950         T *ptrd = _data, *const ptre = _data + siz;
23951         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23952           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23953             *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
23954         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
23955       }
23956       return *this;
23957     }
23958 
23959     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
23960     template<typename t>
23961     CImg<Tfloat> get_pow(const CImg<t>& img) const {
23962       return CImg<Tfloat>(*this,false).pow(img);
23963     }
23964 
23965     //! Compute the bitwise left rotation of each pixel value.
23966     /**
23967        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
23968     **/
23969     CImg<T>& rol(const unsigned int n=1) {
23970       if (is_empty()) return *this;
23971       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23972       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
23973       return *this;
23974     }
23975 
23976     //! Compute the bitwise left rotation of each pixel value \newinstance.
23977     CImg<T> get_rol(const unsigned int n=1) const {
23978       return (+*this).rol(n);
23979     }
23980 
23981     //! Compute the bitwise left rotation of each pixel value.
23982     /**
23983        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
23984     **/
23985     CImg<T>& rol(const char *const expression) {
23986       return rol((+*this)._fill(expression,true,true,0,0,"rol",this));
23987     }
23988 
23989     //! Compute the bitwise left rotation of each pixel value \newinstance.
23990     CImg<T> get_rol(const char *const expression) const {
23991       return (+*this).rol(expression);
23992     }
23993 
23994     //! Compute the bitwise left rotation of each pixel value.
23995     /**
23996        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
23997     **/
23998     template<typename t>
23999     CImg<T>& rol(const CImg<t>& img) {
24000       const ulongT siz = size(), isiz = img.size();
24001       if (siz && isiz) {
24002         if (is_overlapped(img)) return rol(+img);
24003         T *ptrd = _data, *const ptre = _data + siz;
24004         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24005           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24006             *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
24007         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
24008       }
24009       return *this;
24010     }
24011 
24012     //! Compute the bitwise left rotation of each pixel value \newinstance.
24013     template<typename t>
24014     CImg<T> get_rol(const CImg<t>& img) const {
24015       return (+*this).rol(img);
24016     }
24017 
24018     //! Compute the bitwise right rotation of each pixel value.
24019     /**
24020        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
24021     **/
24022     CImg<T>& ror(const unsigned int n=1) {
24023       if (is_empty()) return *this;
24024       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
24025       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
24026       return *this;
24027     }
24028 
24029     //! Compute the bitwise right rotation of each pixel value \newinstance.
24030     CImg<T> get_ror(const unsigned int n=1) const {
24031       return (+*this).ror(n);
24032     }
24033 
24034     //! Compute the bitwise right rotation of each pixel value.
24035     /**
24036        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
24037     **/
24038     CImg<T>& ror(const char *const expression) {
24039       return ror((+*this)._fill(expression,true,true,0,0,"ror",this));
24040     }
24041 
24042     //! Compute the bitwise right rotation of each pixel value \newinstance.
24043     CImg<T> get_ror(const char *const expression) const {
24044       return (+*this).ror(expression);
24045     }
24046 
24047     //! Compute the bitwise right rotation of each pixel value.
24048     /**
24049        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
24050     **/
24051     template<typename t>
24052     CImg<T>& ror(const CImg<t>& img) {
24053       const ulongT siz = size(), isiz = img.size();
24054       if (siz && isiz) {
24055         if (is_overlapped(img)) return ror(+img);
24056         T *ptrd = _data, *const ptre = _data + siz;
24057         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24058           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24059             *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
24060         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
24061       }
24062       return *this;
24063     }
24064 
24065     //! Compute the bitwise right rotation of each pixel value \newinstance.
24066     template<typename t>
24067     CImg<T> get_ror(const CImg<t>& img) const {
24068       return (+*this).ror(img);
24069     }
24070 
24071     //! Pointwise min operator between instance image and a value.
24072     /**
24073        \param val Value used as the reference argument of the min operator.
24074        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24075        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
24076      **/
24077     CImg<T>& min(const T& val) {
24078       if (is_empty()) return *this;
24079       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
24080       cimg_rof(*this,ptrd,T) *ptrd = std::min(*ptrd,val);
24081       return *this;
24082     }
24083 
24084     //! Pointwise min operator between instance image and a value \newinstance.
24085     CImg<T> get_min(const T& val) const {
24086       return (+*this).min(val);
24087     }
24088 
24089     //! Pointwise min operator between two images.
24090     /**
24091        \param img Image used as the reference argument of the min operator.
24092        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24093        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
24094      **/
24095     template<typename t>
24096     CImg<T>& min(const CImg<t>& img) {
24097       const ulongT siz = size(), isiz = img.size();
24098       if (siz && isiz) {
24099         if (is_overlapped(img)) return min(+img);
24100         T *ptrd = _data, *const ptre = _data + siz;
24101         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24102           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24103             *ptrd = std::min((T)*(ptrs++),*ptrd);
24104         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
24105       }
24106       return *this;
24107     }
24108 
24109     //! Pointwise min operator between two images \newinstance.
24110     template<typename t>
24111     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
24112       return CImg<_cimg_Tt>(*this,false).min(img);
24113     }
24114 
24115     //! Pointwise min operator between an image and an expression.
24116     /**
24117        \param expression Math formula as a C-string.
24118        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24119        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
24120     **/
24121     CImg<T>& min(const char *const expression) {
24122       return min((+*this)._fill(expression,true,true,0,0,"min",this));
24123     }
24124 
24125     //! Pointwise min operator between an image and an expression \newinstance.
24126     CImg<Tfloat> get_min(const char *const expression) const {
24127       return CImg<Tfloat>(*this,false).min(expression);
24128     }
24129 
24130     //! Pointwise max operator between instance image and a value.
24131     /**
24132        \param val Value used as the reference argument of the max operator.
24133        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24134        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
24135      **/
24136     CImg<T>& max(const T& val) {
24137       if (is_empty()) return *this;
24138       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
24139       cimg_rof(*this,ptrd,T) *ptrd = std::max(*ptrd,val);
24140       return *this;
24141     }
24142 
24143     //! Pointwise max operator between instance image and a value \newinstance.
24144     CImg<T> get_max(const T& val) const {
24145       return (+*this).max(val);
24146     }
24147 
24148     //! Pointwise max operator between two images.
24149     /**
24150        \param img Image used as the reference argument of the max operator.
24151        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24152        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
24153      **/
24154     template<typename t>
24155     CImg<T>& max(const CImg<t>& img) {
24156       const ulongT siz = size(), isiz = img.size();
24157       if (siz && isiz) {
24158         if (is_overlapped(img)) return max(+img);
24159         T *ptrd = _data, *const ptre = _data + siz;
24160         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24161           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24162             *ptrd = std::max((T)*(ptrs++),*ptrd);
24163         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
24164       }
24165       return *this;
24166     }
24167 
24168     //! Pointwise max operator between two images \newinstance.
24169     template<typename t>
24170     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
24171       return CImg<_cimg_Tt>(*this,false).max(img);
24172     }
24173 
24174     //! Pointwise max operator between an image and an expression.
24175     /**
24176        \param expression Math formula as a C-string.
24177        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24178        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
24179     **/
24180     CImg<T>& max(const char *const expression) {
24181       return max((+*this)._fill(expression,true,true,0,0,"max",this));
24182     }
24183 
24184     //! Pointwise max operator between an image and an expression \newinstance.
24185     CImg<Tfloat> get_max(const char *const expression) const {
24186       return CImg<Tfloat>(*this,false).max(expression);
24187     }
24188 
24189     //! Return a reference to the minimum pixel value.
24190     /**
24191      **/
24192     T& min() {
24193       if (is_empty())
24194         throw CImgInstanceException(_cimg_instance
24195                                     "min(): Empty instance.",
24196                                     cimg_instance);
24197       T *ptr_min = _data;
24198       T min_value = *ptr_min;
24199       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
24200       return *ptr_min;
24201     }
24202 
24203     //! Return a reference to the minimum pixel value \const.
24204     const T& min() const {
24205       if (is_empty())
24206         throw CImgInstanceException(_cimg_instance
24207                                     "min(): Empty instance.",
24208                                     cimg_instance);
24209       const 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 maximum pixel value.
24216     /**
24217      **/
24218     T& max() {
24219       if (is_empty())
24220         throw CImgInstanceException(_cimg_instance
24221                                     "max(): Empty instance.",
24222                                     cimg_instance);
24223       T *ptr_max = _data;
24224       T max_value = *ptr_max;
24225       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
24226       return *ptr_max;
24227     }
24228 
24229     //! Return a reference to the maximum pixel value \const.
24230     const T& max() const {
24231       if (is_empty())
24232         throw CImgInstanceException(_cimg_instance
24233                                     "max(): Empty instance.",
24234                                     cimg_instance);
24235       const 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 minimum pixel value as well as the maximum pixel value.
24242     /**
24243        \param[out] max_val Maximum pixel value.
24244     **/
24245     template<typename t>
24246     T& min_max(t& max_val) {
24247       if (is_empty())
24248         throw CImgInstanceException(_cimg_instance
24249                                     "min_max(): Empty instance.",
24250                                     cimg_instance);
24251       T *ptr_min = _data;
24252       T min_value = *ptr_min, max_value = min_value;
24253       cimg_for(*this,ptrs,T) {
24254         const T val = *ptrs;
24255         if (val<min_value) { min_value = val; ptr_min = ptrs; }
24256         if (val>max_value) max_value = val;
24257       }
24258       max_val = (t)max_value;
24259       return *ptr_min;
24260     }
24261 
24262     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
24263     template<typename t>
24264     const T& min_max(t& max_val) const {
24265       if (is_empty())
24266         throw CImgInstanceException(_cimg_instance
24267                                     "min_max(): Empty instance.",
24268                                     cimg_instance);
24269       const T *ptr_min = _data;
24270       T min_value = *ptr_min, max_value = min_value;
24271       cimg_for(*this,ptrs,T) {
24272         const T val = *ptrs;
24273         if (val<min_value) { min_value = val; ptr_min = ptrs; }
24274         if (val>max_value) max_value = val;
24275       }
24276       max_val = (t)max_value;
24277       return *ptr_min;
24278     }
24279 
24280     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
24281     /**
24282        \param[out] min_val Minimum pixel value.
24283     **/
24284     template<typename t>
24285     T& max_min(t& min_val) {
24286       if (is_empty())
24287         throw CImgInstanceException(_cimg_instance
24288                                     "max_min(): Empty instance.",
24289                                     cimg_instance);
24290       T *ptr_max = _data;
24291       T max_value = *ptr_max, min_value = max_value;
24292       cimg_for(*this,ptrs,T) {
24293         const T val = *ptrs;
24294         if (val>max_value) { max_value = val; ptr_max = ptrs; }
24295         if (val<min_value) min_value = val;
24296       }
24297       min_val = (t)min_value;
24298       return *ptr_max;
24299     }
24300 
24301     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
24302     template<typename t>
24303     const T& max_min(t& min_val) const {
24304       if (is_empty())
24305         throw CImgInstanceException(_cimg_instance
24306                                     "max_min(): Empty instance.",
24307                                     cimg_instance);
24308       const T *ptr_max = _data;
24309       T max_value = *ptr_max, min_value = max_value;
24310       cimg_for(*this,ptrs,T) {
24311         const T val = *ptrs;
24312         if (val>max_value) { max_value = val; ptr_max = ptrs; }
24313         if (val<min_value) min_value = val;
24314       }
24315       min_val = (t)min_value;
24316       return *ptr_max;
24317     }
24318 
24319     //! Return the kth smallest pixel value.
24320     /**
24321        \param k Rank of the search smallest element.
24322     **/
24323     T kth_smallest(const ulongT k) const {
24324       if (is_empty())
24325         throw CImgInstanceException(_cimg_instance
24326                                     "kth_smallest(): Empty instance.",
24327                                     cimg_instance);
24328       CImg<T> arr(*this,false);
24329       ulongT l = 0, ir = size() - 1;
24330       for ( ; ; ) {
24331         if (ir<=l + 1) {
24332           if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
24333           return arr[k];
24334         } else {
24335           const ulongT mid = (l + ir)>>1;
24336           cimg::swap(arr[mid],arr[l + 1]);
24337           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
24338           if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
24339           if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
24340           ulongT i = l + 1, j = ir;
24341           const T pivot = arr[l + 1];
24342           for ( ; ; ) {
24343             do ++i; while (arr[i]<pivot);
24344             do --j; while (arr[j]>pivot);
24345             if (j<i) break;
24346             cimg::swap(arr[i],arr[j]);
24347           }
24348           arr[l + 1] = arr[j];
24349           arr[j] = pivot;
24350           if (j>=k) ir = j - 1;
24351           if (j<=k) l = i;
24352         }
24353       }
24354     }
24355 
24356     //! Return the median pixel value.
24357     /**
24358      **/
24359     T median() const {
24360       if (is_empty())
24361         throw CImgInstanceException(_cimg_instance
24362                                     "median(): Empty instance.",
24363                                     cimg_instance);
24364       const ulongT s = size();
24365       switch (s) {
24366       case 1 : return _data[0];
24367       case 2 : return cimg::median(_data[0],_data[1]);
24368       case 3 : return cimg::median(_data[0],_data[1],_data[2]);
24369       case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
24370       case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
24371       case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
24372       case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
24373                                     _data[9],_data[10],_data[11],_data[12]);
24374       }
24375       const T res = kth_smallest(s>>1);
24376       return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
24377     }
24378 
24379     //! Return the product of all the pixel values.
24380     /**
24381      **/
24382     double product() const {
24383       if (is_empty()) return 0;
24384       double res = 1;
24385       cimg_for(*this,ptrs,T) res*=(double)*ptrs;
24386       return res;
24387     }
24388 
24389     //! Return the sum of all the pixel values.
24390     /**
24391      **/
24392     double sum() const {
24393       double res = 0;
24394       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
24395       return res;
24396     }
24397 
24398     //! Return the average pixel value.
24399     /**
24400      **/
24401     double mean() const {
24402       double res = 0;
24403       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
24404       return res/size();
24405     }
24406 
24407     //! Return the variance of the pixel values.
24408     /**
24409        \param variance_method Method used to estimate the variance. Can be:
24410        - \c 0: Second moment, computed as
24411        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
24412        1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
24413        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
24414        - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
24415        - \c 2: Least median of squares.
24416        - \c 3: Least trimmed of squares.
24417     **/
24418     double variance(const unsigned int variance_method=1) const {
24419       double foo;
24420       return variance_mean(variance_method,foo);
24421     }
24422 
24423     //! Return the variance as well as the average of the pixel values.
24424     /**
24425        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
24426        \param[out] mean Average pixel value.
24427     **/
24428     template<typename t>
24429     double variance_mean(const unsigned int variance_method, t& mean) const {
24430       if (is_empty())
24431         throw CImgInstanceException(_cimg_instance
24432                                     "variance_mean(): Empty instance.",
24433                                     cimg_instance);
24434 
24435       double variance = 0, average = 0;
24436       const ulongT siz = size();
24437       switch (variance_method) {
24438       case 0 : { // Least mean square (standard definition)
24439         double S = 0, S2 = 0;
24440         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
24441         variance = (S2 - S*S/siz)/siz;
24442         average = S;
24443       } break;
24444       case 1 : { // Least mean square (robust definition)
24445         double S = 0, S2 = 0;
24446         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
24447         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
24448         average = S;
24449       } break;
24450       case 2 : { // Least Median of Squares (MAD)
24451         CImg<Tfloat> buf(*this,false);
24452         buf.sort();
24453         const ulongT siz2 = siz>>1;
24454         const double med_i = (double)buf[siz2];
24455         cimg_for(buf,ptrs,Tfloat) {
24456           const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
24457         }
24458         buf.sort();
24459         const double sig = (double)(1.4828*buf[siz2]);
24460         variance = sig*sig;
24461       } break;
24462       default : { // Least trimmed of Squares
24463         CImg<Tfloat> buf(*this,false);
24464         const ulongT siz2 = siz>>1;
24465         cimg_for(buf,ptrs,Tfloat) {
24466           const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
24467         }
24468         buf.sort();
24469         double a = 0;
24470         const Tfloat *ptrs = buf._data;
24471         for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
24472         const double sig = (double)(2.6477*std::sqrt(a/siz2));
24473         variance = sig*sig;
24474       }
24475       }
24476       mean = (t)(average/siz);
24477       return variance>0?variance:0;
24478     }
24479 
24480     //! Return estimated variance of the noise.
24481     /**
24482        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
24483        \note Because of structures such as edges in images it is
24484        recommanded to use a robust variance estimation. The variance of the
24485        noise is estimated by computing the variance of the Laplacian \f$(\Delta
24486        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
24487        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
24488     **/
24489     double variance_noise(const unsigned int variance_method=2) const {
24490       if (is_empty())
24491         throw CImgInstanceException(_cimg_instance
24492                                     "variance_noise(): Empty instance.",
24493                                     cimg_instance);
24494 
24495       const ulongT siz = size();
24496       if (!siz || !_data) return 0;
24497       if (variance_method>1) { // Compute a scaled version of the Laplacian.
24498         CImg<Tdouble> tmp(*this,false);
24499         if (_depth==1) {
24500           const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
24501           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2))
24502           cimg_forC(*this,c) {
24503             CImg_3x3(I,T);
24504             cimg_for3x3(*this,x,y,0,c,I,T) {
24505               tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
24506                                  (double)Icp - 4*(double)Icc);
24507             }
24508           }
24509         } else {
24510           const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
24511           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=262144 && _spectrum>=2))
24512           cimg_forC(*this,c) {
24513             CImg_3x3x3(I,T);
24514             cimg_for3x3x3(*this,x,y,z,c,I,T) {
24515               tmp(x,y,z,c) = cste*(
24516                                    (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
24517                                    (double)Iccn + (double)Iccp - 6*(double)Iccc);
24518             }
24519           }
24520         }
24521         return tmp.variance(variance_method);
24522       }
24523 
24524       // Version that doesn't need intermediate images.
24525       double variance = 0, S = 0, S2 = 0;
24526       if (_depth==1) {
24527         const double cste = 1.0/std::sqrt(20.0);
24528         CImg_3x3(I,T);
24529         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
24530           const double val = cste*((double)Inc + (double)Ipc +
24531                                    (double)Icn + (double)Icp - 4*(double)Icc);
24532           S+=val; S2+=val*val;
24533         }
24534       } else {
24535         const double cste = 1.0/std::sqrt(42.0);
24536         CImg_3x3x3(I,T);
24537         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
24538           const double val = cste *
24539             ((double)Incc + (double)Ipcc + (double)Icnc +
24540              (double)Icpc +
24541              (double)Iccn + (double)Iccp - 6*(double)Iccc);
24542           S+=val; S2+=val*val;
24543         }
24544       }
24545       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
24546       else variance = (S2 - S*S/siz)/siz;
24547       return variance>0?variance:0;
24548     }
24549 
24550     //! Compute the MSE (Mean-Squared Error) between two images.
24551     /**
24552        \param img Image used as the second argument of the MSE operator.
24553     **/
24554     template<typename t>
24555     double MSE(const CImg<t>& img) const {
24556       if (img.size()!=size())
24557         throw CImgArgumentException(_cimg_instance
24558                                     "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
24559                                     cimg_instance,
24560                                     img._width,img._height,img._depth,img._spectrum,img._data);
24561       double vMSE = 0;
24562       const t* ptr2 = img._data;
24563       cimg_for(*this,ptr1,T) {
24564         const double diff = (double)*ptr1 - (double)*(ptr2++);
24565         vMSE+=diff*diff;
24566       }
24567       const ulongT siz = img.size();
24568       if (siz) vMSE/=siz;
24569       return vMSE;
24570     }
24571 
24572     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
24573     /**
24574        \param img Image used as the second argument of the PSNR operator.
24575        \param max_value Maximum theoretical value of the signal.
24576      **/
24577     template<typename t>
24578     double PSNR(const CImg<t>& img, const double max_value=255) const {
24579       const double vMSE = (double)std::sqrt(MSE(img));
24580       return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
24581     }
24582 
24583     //! Evaluate math formula.
24584     /**
24585        \param expression Math formula, as a C-string.
24586        \param x Value of the pre-defined variable \c x.
24587        \param y Value of the pre-defined variable \c y.
24588        \param z Value of the pre-defined variable \c z.
24589        \param c Value of the pre-defined variable \c c.
24590        \param list_inputs A list of input images attached to the specified math formula.
24591        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24592     **/
24593     double eval(const char *const expression,
24594                 const double x=0, const double y=0, const double z=0, const double c=0,
24595                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24596       return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
24597     }
24598 
24599     //! Evaluate math formula \const.
24600     double eval(const char *const expression,
24601                 const double x=0, const double y=0, const double z=0, const double c=0,
24602                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24603       return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
24604     }
24605 
24606     double _eval(CImg<T> *const img_output, const char *const expression,
24607                  const double x, const double y, const double z, const double c,
24608                  const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
24609       if (!expression || !*expression) return 0;
24610       if (!expression[1]) switch (*expression) { // Single-char optimization.
24611         case 'w' : return (double)_width;
24612         case 'h' : return (double)_height;
24613         case 'd' : return (double)_depth;
24614         case 's' : return (double)_spectrum;
24615         case 'r' : return (double)_is_shared;
24616         }
24617       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
24618                                          *expression=='*' || *expression==':'),"eval",
24619                            *this,img_output,list_inputs,list_outputs,false);
24620       const double val = mp(x,y,z,c);
24621       mp.end();
24622       return val;
24623     }
24624 
24625     //! Evaluate math formula.
24626     /**
24627        \param[out] output Contains values of output vector returned by the evaluated expression
24628          (or is empty if the returned type is scalar).
24629        \param expression Math formula, as a C-string.
24630        \param x Value of the pre-defined variable \c x.
24631        \param y Value of the pre-defined variable \c y.
24632        \param z Value of the pre-defined variable \c z.
24633        \param c Value of the pre-defined variable \c c.
24634        \param list_inputs A list of input images attached to the specified math formula.
24635        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24636     **/
24637     template<typename t>
24638     void eval(CImg<t> &output, const char *const expression,
24639               const double x=0, const double y=0, const double z=0, const double c=0,
24640               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24641       _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
24642     }
24643 
24644     //! Evaluate math formula \const.
24645     template<typename t>
24646     void eval(CImg<t>& output, const char *const expression,
24647               const double x=0, const double y=0, const double z=0, const double c=0,
24648               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24649       _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
24650     }
24651 
24652     template<typename t>
24653     void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
24654                const double x, const double y, const double z, const double c,
24655                const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
24656       if (!expression || !*expression) { output.assign(1); *output = 0; }
24657       if (!expression[1]) switch (*expression) { // Single-char optimization.
24658         case 'w' : output.assign(1); *output = (t)_width; break;
24659         case 'h' : output.assign(1); *output = (t)_height; break;
24660         case 'd' : output.assign(1); *output = (t)_depth; break;
24661         case 's' : output.assign(1); *output = (t)_spectrum; break;
24662         case 'r' : output.assign(1); *output = (t)_is_shared; break;
24663         }
24664       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
24665                                          *expression=='*' || *expression==':'),"eval",
24666                            *this,img_output,list_inputs,list_outputs,false);
24667       output.assign(1,std::max(1U,mp.result_dim));
24668       mp(x,y,z,c,output._data);
24669       mp.end();
24670     }
24671 
24672     //! Evaluate math formula on a set of variables.
24673     /**
24674        \param expression Math formula, as a C-string.
24675        \param xyzc Set of values (x,y,z,c) used for the evaluation.
24676        \param list_inputs A list of input images attached to the specified math formula.
24677        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24678     **/
24679     template<typename t>
24680     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
24681                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24682       return _eval(this,expression,xyzc,list_inputs,list_outputs);
24683     }
24684 
24685     //! Evaluate math formula on a set of variables \const.
24686     template<typename t>
24687     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
24688                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24689       return _eval(0,expression,xyzc,list_inputs,list_outputs);
24690     }
24691 
24692     template<typename t>
24693     CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
24694                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24695       CImg<doubleT> res(1,xyzc.size()/4);
24696       if (!expression || !*expression) return res.fill(0);
24697       _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
24698 #ifdef cimg_use_openmp
24699       cimg_pragma_openmp(parallel if (res._height>=512))
24700       {
24701         _cimg_math_parser
24702           _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
24703           &lmp = omp_get_thread_num()?_mp:mp;
24704         cimg_pragma_openmp(for)
24705           for (unsigned int i = 0; i<res._height; ++i) {
24706             const unsigned int i4 = 4*i;
24707             const double
24708               x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
24709               z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
24710             res[i] = lmp(x,y,z,c);
24711           }
24712         }
24713 #else
24714       const t *ps = xyzc._data;
24715       cimg_for(res,pd,double) {
24716         const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
24717         *pd = mp(x,y,z,c);
24718       }
24719 #endif
24720       mp.end();
24721       return res;
24722     }
24723 
24724     //! Compute statistics vector from the pixel values.
24725     /*
24726        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
24727        \return Statistics vector as
24728          <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
24729     **/
24730     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
24731       if (is_empty()) return CImg<doubleT>();
24732       const T *const p_end = end(), *pm = _data, *pM = _data;
24733       double S = 0, S2 = 0, P = 1;
24734       const ulongT siz = size();
24735       T m = *pm, M = *pM;
24736 
24737       cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if(siz>=131072)) {
24738         const T *lpm = _data, *lpM = _data;
24739         T lm = *lpm, lM = *lpM;
24740         cimg_pragma_openmp(for)
24741         for (const T *ptrs = _data; ptrs<p_end; ++ptrs) {
24742           const T val = *ptrs;
24743           const double _val = (double)val;
24744           if (val<lm) { lm = val; lpm = ptrs; }
24745           if (val>lM) { lM = val; lpM = ptrs; }
24746           S+=_val;
24747           S2+=_val*_val;
24748           P*=_val;
24749         }
24750         cimg_pragma_openmp(critical(get_stats)) {
24751           if (lm<m || (lm==m && lpm<pm)) { m = lm; pm = lpm; }
24752           if (lM>M || (lM==M && lpM<pM)) { M = lM; pM = lpM; }
24753         }
24754       }
24755 
24756       const double
24757         mean_value = S/siz,
24758         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
24759         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
24760          variance(variance_method)),
24761         variance_value = _variance_value>0?_variance_value:0;
24762       int
24763         xm = 0, ym = 0, zm = 0, cm = 0,
24764         xM = 0, yM = 0, zM = 0, cM = 0;
24765       contains(*pm,xm,ym,zm,cm);
24766       contains(*pM,xM,yM,zM,cM);
24767       return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
24768                                       (double)xm,(double)ym,(double)zm,(double)cm,
24769                                       (double)xM,(double)yM,(double)zM,(double)cM,
24770                                       S,P);
24771     }
24772 
24773     //! Compute statistics vector from the pixel values \inplace.
24774     CImg<T>& stats(const unsigned int variance_method=1) {
24775       return get_stats(variance_method).move_to(*this);
24776     }
24777 
24778     //@}
24779     //-------------------------------------
24780     //
24781     //! \name Vector / Matrix Operations
24782     //@{
24783     //-------------------------------------
24784 
24785     //! Compute norm of the image, viewed as a matrix.
24786     /**
24787        \param magnitude_type Norm type. Can be:
24788        - \c -1: Linf-norm
24789        - \c 0: L0-norm
24790        - \c 1: L1-norm
24791        - \c 2: L2-norm
24792     **/
24793     double magnitude(const int magnitude_type=2) const {
24794       if (is_empty())
24795         throw CImgInstanceException(_cimg_instance
24796                                     "magnitude(): Empty instance.",
24797                                     cimg_instance);
24798       double res = 0;
24799       switch (magnitude_type) {
24800       case -1 : {
24801         cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
24802       } break;
24803       case 1 : {
24804         cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs);
24805       } break;
24806       default : {
24807         cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs);
24808         res = (double)std::sqrt(res);
24809       }
24810       }
24811       return res;
24812     }
24813 
24814     //! Compute the trace of the image, viewed as a matrix.
24815     /**
24816      **/
24817     double trace() const {
24818       if (is_empty())
24819         throw CImgInstanceException(_cimg_instance
24820                                     "trace(): Empty instance.",
24821                                     cimg_instance);
24822       double res = 0;
24823       cimg_forX(*this,k) res+=(double)(*this)(k,k);
24824       return res;
24825     }
24826 
24827     //! Compute the determinant of the image, viewed as a matrix.
24828     /**
24829      **/
24830     double det() const {
24831       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
24832         throw CImgInstanceException(_cimg_instance
24833                                     "det(): Instance is not a square matrix.",
24834                                     cimg_instance);
24835 
24836       switch (_width) {
24837       case 1 : return (double)((*this)(0,0));
24838       case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
24839       case 3 : {
24840         const double
24841           a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
24842           b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
24843           c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
24844         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
24845       }
24846       default : {
24847         CImg<Tfloat> lu(*this,false);
24848         CImg<uintT> indx;
24849         bool d;
24850         lu._LU(indx,d);
24851         double res = d?(double)1:(double)-1;
24852         cimg_forX(lu,i) res*=lu(i,i);
24853         return res;
24854       }
24855       }
24856     }
24857 
24858     //! Compute the dot product between instance and argument, viewed as matrices.
24859     /**
24860        \param img Image used as a second argument of the dot product.
24861     **/
24862     template<typename t>
24863     double dot(const CImg<t>& img) const {
24864       if (is_empty())
24865         throw CImgInstanceException(_cimg_instance
24866                                     "dot(): Empty instance.",
24867                                     cimg_instance);
24868       if (!img)
24869         throw CImgArgumentException(_cimg_instance
24870                                     "dot(): Empty specified image.",
24871                                     cimg_instance);
24872 
24873       const ulongT nb = std::min(size(),img.size());
24874       double res = 0;
24875       for (ulongT off = 0; off<nb; ++off) res+=(double)_data[off]*(double)img[off];
24876       return res;
24877     }
24878 
24879     //! Get vector-valued pixel located at specified position.
24880     /**
24881        \param x X-coordinate of the pixel value.
24882        \param y Y-coordinate of the pixel value.
24883        \param z Z-coordinate of the pixel value.
24884     **/
24885     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
24886       CImg<T> res;
24887       if (res._height!=_spectrum) res.assign(1,_spectrum);
24888       const ulongT whd = (ulongT)_width*_height*_depth;
24889       const T *ptrs = data(x,y,z);
24890       T *ptrd = res._data;
24891       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24892       return res;
24893     }
24894 
24895     //! Get (square) matrix-valued pixel located at specified position.
24896     /**
24897        \param x X-coordinate of the pixel value.
24898        \param y Y-coordinate of the pixel value.
24899        \param z Z-coordinate of the pixel value.
24900        \note - The spectrum() of the image must be a square.
24901      **/
24902     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
24903       const int n = (int)std::sqrt((double)_spectrum);
24904       const T *ptrs = data(x,y,z,0);
24905       const ulongT whd = (ulongT)_width*_height*_depth;
24906       CImg<T> res(n,n);
24907       T *ptrd = res._data;
24908       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24909       return res;
24910     }
24911 
24912     //! Get tensor-valued pixel located at specified position.
24913     /**
24914        \param x X-coordinate of the pixel value.
24915        \param y Y-coordinate of the pixel value.
24916        \param z Z-coordinate of the pixel value.
24917     **/
24918     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
24919       const T *ptrs = data(x,y,z,0);
24920       const ulongT whd = (ulongT)_width*_height*_depth;
24921       if (_spectrum==6)
24922         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
24923       if (_spectrum==3)
24924         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
24925       return tensor(*ptrs);
24926     }
24927 
24928     //! Set vector-valued pixel at specified position.
24929     /**
24930        \param vec Vector to put on the instance image.
24931        \param x X-coordinate of the pixel value.
24932        \param y Y-coordinate of the pixel value.
24933        \param z Z-coordinate of the pixel value.
24934     **/
24935     template<typename t>
24936     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
24937       if (x<_width && y<_height && z<_depth) {
24938         const t *ptrs = vec._data;
24939         const ulongT whd = (ulongT)_width*_height*_depth;
24940         T *ptrd = data(x,y,z);
24941         for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
24942           *ptrd = (T)*(ptrs++); ptrd+=whd;
24943         }
24944       }
24945       return *this;
24946     }
24947 
24948     //! Set (square) matrix-valued pixel at specified position.
24949     /**
24950        \param mat Matrix to put on the instance image.
24951        \param x X-coordinate of the pixel value.
24952        \param y Y-coordinate of the pixel value.
24953        \param z Z-coordinate of the pixel value.
24954     **/
24955     template<typename t>
24956     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
24957       return set_vector_at(mat,x,y,z);
24958     }
24959 
24960     //! Set tensor-valued pixel at specified position.
24961     /**
24962        \param ten Tensor to put on the instance image.
24963        \param x X-coordinate of the pixel value.
24964        \param y Y-coordinate of the pixel value.
24965        \param z Z-coordinate of the pixel value.
24966     **/
24967     template<typename t>
24968     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
24969       T *ptrd = data(x,y,z,0);
24970       const ulongT siz = (ulongT)_width*_height*_depth;
24971       if (ten._height==2) {
24972         *ptrd = (T)ten[0]; ptrd+=siz;
24973         *ptrd = (T)ten[1]; ptrd+=siz;
24974         *ptrd = (T)ten[3];
24975       }
24976       else {
24977         *ptrd = (T)ten[0]; ptrd+=siz;
24978         *ptrd = (T)ten[1]; ptrd+=siz;
24979         *ptrd = (T)ten[2]; ptrd+=siz;
24980         *ptrd = (T)ten[4]; ptrd+=siz;
24981         *ptrd = (T)ten[5]; ptrd+=siz;
24982         *ptrd = (T)ten[8];
24983       }
24984       return *this;
24985     }
24986 
24987     //! Unroll pixel values along axis \c y.
24988     /**
24989        \note Equivalent to \code unroll('y'); \endcode.
24990     **/
24991     CImg<T>& vector() {
24992       return unroll('y');
24993     }
24994 
24995     //! Unroll pixel values along axis \c y \newinstance.
24996     CImg<T> get_vector() const {
24997       return get_unroll('y');
24998     }
24999 
25000     //! Resize image to become a scalar square matrix.
25001     /**
25002      **/
25003     CImg<T>& matrix() {
25004       const ulongT siz = size();
25005       switch (siz) {
25006       case 1 : break;
25007       case 4 : _width = _height = 2; break;
25008       case 9 : _width = _height = 3; break;
25009       case 16 : _width = _height = 4; break;
25010       case 25 : _width = _height = 5; break;
25011       case 36 : _width = _height = 6; break;
25012       case 49 : _width = _height = 7; break;
25013       case 64 : _width = _height = 8; break;
25014       case 81 : _width = _height = 9; break;
25015       case 100 : _width = _height = 10; break;
25016       default : {
25017         ulongT i = 11, i2 = i*i;
25018         while (i2<siz) { i2+=2*i + 1; ++i; }
25019         if (i2==siz) _width = _height = i;
25020         else throw CImgInstanceException(_cimg_instance
25021                                          "matrix(): Invalid instance size %u (should be a square integer).",
25022                                          cimg_instance,
25023                                          siz);
25024       }
25025       }
25026       return *this;
25027     }
25028 
25029     //! Resize image to become a scalar square matrix \newinstance.
25030     CImg<T> get_matrix() const {
25031       return (+*this).matrix();
25032     }
25033 
25034     //! Resize image to become a symmetric tensor.
25035     /**
25036      **/
25037     CImg<T>& tensor() {
25038       return get_tensor().move_to(*this);
25039     }
25040 
25041     //! Resize image to become a symmetric tensor \newinstance.
25042     CImg<T> get_tensor() const {
25043       CImg<T> res;
25044       const ulongT siz = size();
25045       switch (siz) {
25046       case 1 : break;
25047       case 3 :
25048         res.assign(2,2);
25049         res(0,0) = (*this)(0);
25050         res(1,0) = res(0,1) = (*this)(1);
25051         res(1,1) = (*this)(2);
25052         break;
25053       case 6 :
25054         res.assign(3,3);
25055         res(0,0) = (*this)(0);
25056         res(1,0) = res(0,1) = (*this)(1);
25057         res(2,0) = res(0,2) = (*this)(2);
25058         res(1,1) = (*this)(3);
25059         res(2,1) = res(1,2) = (*this)(4);
25060         res(2,2) = (*this)(5);
25061         break;
25062       default :
25063         throw CImgInstanceException(_cimg_instance
25064                                     "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
25065                                     cimg_instance);
25066       }
25067       return res;
25068     }
25069 
25070     //! Resize image to become a diagonal matrix.
25071     /**
25072        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
25073     **/
25074     CImg<T>& diagonal() {
25075       return get_diagonal().move_to(*this);
25076     }
25077 
25078     //! Resize image to become a diagonal matrix \newinstance.
25079     CImg<T> get_diagonal() const {
25080       if (is_empty()) return *this;
25081       const unsigned int siz = (unsigned int)size();
25082       CImg<T> res(siz,siz,1,1,0);
25083       cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
25084       return res;
25085     }
25086 
25087     //! Replace the image by an identity matrix.
25088     /**
25089        \note If the instance image is not square, it is resized to a square matrix using its maximum
25090        dimension as a reference.
25091     **/
25092     CImg<T>& identity_matrix() {
25093       return identity_matrix(std::max(_width,_height)).move_to(*this);
25094     }
25095 
25096     //! Replace the image by an identity matrix \newinstance.
25097     CImg<T> get_identity_matrix() const {
25098       return identity_matrix(std::max(_width,_height));
25099     }
25100 
25101     //! Fill image with a linear sequence of values.
25102     /**
25103        \param a0 Starting value of the sequence.
25104        \param a1 Ending value of the sequence.
25105     **/
25106     CImg<T>& sequence(const T& a0, const T& a1) {
25107       if (is_empty()) return *this;
25108       const ulongT siz = size() - 1;
25109       T* ptr = _data;
25110       if (siz) {
25111         const double delta = (double)a1 - (double)a0;
25112         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
25113       } else *ptr = a0;
25114       return *this;
25115     }
25116 
25117     //! Fill image with a linear sequence of values \newinstance.
25118     CImg<T> get_sequence(const T& a0, const T& a1) const {
25119       return (+*this).sequence(a0,a1);
25120     }
25121 
25122     //! Transpose the image, viewed as a matrix.
25123     /**
25124        \note Equivalent to \code permute_axes("yxzc"); \endcode
25125     **/
25126     CImg<T>& transpose() {
25127       if (_width==1) { _width = _height; _height = 1; return *this; }
25128       if (_height==1) { _height = _width; _width = 1; return *this; }
25129       if (_width==_height) {
25130         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));
25131         return *this;
25132       }
25133       return get_transpose().move_to(*this);
25134     }
25135 
25136     //! Transpose the image, viewed as a matrix \newinstance.
25137     CImg<T> get_transpose() const {
25138       return get_permute_axes("yxzc");
25139     }
25140 
25141     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors.
25142     /**
25143        \param img Image used as the second argument of the cross product.
25144        \note The first argument of the cross product is \c *this.
25145      **/
25146     template<typename t>
25147     CImg<T>& cross(const CImg<t>& img) {
25148       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
25149         throw CImgInstanceException(_cimg_instance
25150                                     "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
25151                                     cimg_instance,
25152                                     img._width,img._height,img._depth,img._spectrum,img._data);
25153 
25154       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
25155       (*this)[0] = (T)(y*img[2] - z*img[1]);
25156       (*this)[1] = (T)(z*img[0] - x*img[2]);
25157       (*this)[2] = (T)(x*img[1] - y*img[0]);
25158       return *this;
25159     }
25160 
25161     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance.
25162     template<typename t>
25163     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
25164       return CImg<_cimg_Tt>(*this).cross(img);
25165     }
25166 
25167     //! Invert the instance image, viewed as a matrix.
25168     /**
25169        \param use_LU Choose the inverting algorithm. Can be:
25170        - \c true: LU-based matrix inversion.
25171        - \c false: SVD-based matrix inversion.
25172     **/
25173     CImg<T>& invert(const bool use_LU=true) {
25174       if (_width!=_height || _depth!=1 || _spectrum!=1)
25175         throw CImgInstanceException(_cimg_instance
25176                                     "invert(): Instance is not a square matrix.",
25177                                     cimg_instance);
25178 #ifdef cimg_use_lapack
25179       int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
25180       Tfloat
25181         *const lapA = new Tfloat[N*N],
25182         *const WORK = new Tfloat[LWORK];
25183       cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
25184       cimg::getrf(N,lapA,IPIV,INFO);
25185       if (INFO)
25186         cimg::warn(_cimg_instance
25187                    "invert(): LAPACK function dgetrf_() returned error code %d.",
25188                    cimg_instance,
25189                    INFO);
25190       else {
25191         cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
25192         if (INFO)
25193           cimg::warn(_cimg_instance
25194                      "invert(): LAPACK function dgetri_() returned error code %d.",
25195                      cimg_instance,
25196                      INFO);
25197       }
25198       if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
25199       delete[] IPIV; delete[] lapA; delete[] WORK;
25200 #else
25201       const double dete = _width>3?-1.0:det();
25202       if (dete!=0.0 && _width==2) {
25203         const double
25204           a = _data[0], c = _data[1],
25205           b = _data[2], d = _data[3];
25206         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
25207         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
25208       } else if (dete!=0.0 && _width==3) {
25209         const double
25210           a = _data[0], d = _data[1], g = _data[2],
25211           b = _data[3], e = _data[4], h = _data[5],
25212           c = _data[6], f = _data[7], i = _data[8];
25213         _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
25214         _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
25215         _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
25216       } else {
25217         if (use_LU) { // LU-based inverse computation
25218           CImg<Tfloat> A(*this,false), indx, col(1,_width);
25219           bool d;
25220           A._LU(indx,d);
25221           cimg_forX(*this,j) {
25222             col.fill(0);
25223             col(j) = 1;
25224             col._solve(A,indx);
25225             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
25226           }
25227         } else { // SVD-based inverse computation
25228           CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
25229           SVD(U,S,V,false);
25230           U.transpose();
25231           cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
25232           S.diagonal();
25233           *this = V*S*U;
25234         }
25235       }
25236 #endif
25237       return *this;
25238     }
25239 
25240     //! Invert the instance image, viewed as a matrix \newinstance.
25241     CImg<Tfloat> get_invert(const bool use_LU=true) const {
25242       return CImg<Tfloat>(*this,false).invert(use_LU);
25243     }
25244 
25245     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
25246     /**
25247     **/
25248     CImg<T>& pseudoinvert() {
25249       return get_pseudoinvert().move_to(*this);
25250     }
25251 
25252     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
25253     CImg<Tfloat> get_pseudoinvert() const {
25254       CImg<Tfloat> U, S, V;
25255       SVD(U,S,V);
25256       const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
25257       cimg_forX(V,x) {
25258 	const Tfloat s = S(x), invs = s>tolerance?1/s:0;
25259 	cimg_forY(V,y) V(x,y)*=invs;
25260       }
25261       return V*U.transpose();
25262     }
25263 
25264     //! Solve a system of linear equations.
25265     /**
25266        \param A Matrix of the linear system.
25267        \note Solve \c AX=B where \c B=*this.
25268     **/
25269     template<typename t>
25270     CImg<T>& solve(const CImg<t>& A) {
25271       if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
25272         throw CImgArgumentException(_cimg_instance
25273                                     "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
25274                                     "incompatible dimensions.",
25275                                     cimg_instance,
25276                                     A._width,A._height,A._depth,A._spectrum,A._data);
25277       typedef _cimg_Ttfloat Ttfloat;
25278       if (A._width==A._height) { // Classical linear system
25279         if (_width!=1) {
25280           CImg<T> res(_width,A._width);
25281           cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
25282           return res.move_to(*this);
25283         }
25284 #ifdef cimg_use_lapack
25285         char TRANS = 'N';
25286         int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
25287         Ttfloat
25288           *const lapA = new Ttfloat[N*N],
25289           *const lapB = new Ttfloat[N],
25290           *const WORK = new Ttfloat[LWORK];
25291         cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
25292         cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
25293         cimg::getrf(N,lapA,IPIV,INFO);
25294         if (INFO)
25295           cimg::warn(_cimg_instance
25296                      "solve(): LAPACK library function dgetrf_() returned error code %d.",
25297                      cimg_instance,
25298                      INFO);
25299 
25300         if (!INFO) {
25301           cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
25302           if (INFO)
25303             cimg::warn(_cimg_instance
25304                        "solve(): LAPACK library function dgetrs_() returned error code %d.",
25305                        cimg_instance,
25306                        INFO);
25307         }
25308         if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
25309         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
25310 #else
25311         CImg<Ttfloat> lu(A,false);
25312         CImg<Ttfloat> indx;
25313         bool d;
25314         lu._LU(indx,d);
25315         _solve(lu,indx);
25316 #endif
25317       } else { // Least-square solution for non-square systems.
25318 #ifdef cimg_use_lapack
25319         if (_width!=1) {
25320           CImg<T> res(_width,A._width);
25321           cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
25322           return res.move_to(*this);
25323         }
25324 	char TRANS = 'N';
25325         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
25326 	Ttfloat WORK_QUERY;
25327 	Ttfloat
25328 	  * const lapA = new Ttfloat[M*N],
25329 	  * const lapB = new Ttfloat[M*NRHS];
25330 	cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
25331 	LWORK = (int) WORK_QUERY;
25332 	Ttfloat *const WORK = new Ttfloat[LWORK];
25333         cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
25334         cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
25335 	cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
25336         if (INFO != 0)
25337           cimg::warn(_cimg_instance
25338                      "solve(): LAPACK library function sgels() returned error code %d.",
25339                      cimg_instance,
25340                      INFO);
25341 	assign(NRHS, N);
25342         if (!INFO)
25343           cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
25344         else
25345           assign(A.get_pseudoinvert()*(*this));
25346         delete[] lapA; delete[] lapB; delete[] WORK;
25347 #else
25348 	assign(A.get_pseudoinvert()*(*this));
25349 #endif
25350       }
25351       return *this;
25352     }
25353 
25354     //! Solve a system of linear equations \newinstance.
25355     template<typename t>
25356     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
25357       return CImg<_cimg_Ttfloat>(*this,false).solve(A);
25358     }
25359 
25360     template<typename t, typename ti>
25361     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
25362       typedef _cimg_Ttfloat Ttfloat;
25363       const int N = (int)size();
25364       int ii = -1;
25365       Ttfloat sum;
25366       for (int i = 0; i<N; ++i) {
25367         const int ip = (int)indx[i];
25368         Ttfloat sum = (*this)(ip);
25369         (*this)(ip) = (*this)(i);
25370         if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
25371         else if (sum!=0) ii = i;
25372         (*this)(i) = (T)sum;
25373       }
25374       for (int i = N - 1; i>=0; --i) {
25375         sum = (*this)(i);
25376         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
25377         (*this)(i) = (T)(sum/A(i,i));
25378       }
25379       return *this;
25380     }
25381 
25382     //! Solve a tridiagonal system of linear equations.
25383     /**
25384        \param A Coefficients of the tridiagonal system.
25385        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
25386        stored as a 3 columns matrix
25387        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
25388     **/
25389     template<typename t>
25390     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
25391       const unsigned int siz = (unsigned int)size();
25392       if (A._width!=3 || A._height!=siz)
25393         throw CImgArgumentException(_cimg_instance
25394                                     "solve_tridiagonal(): Instance and tridiagonal matrix "
25395                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
25396                                     cimg_instance,
25397                                     A._width,A._height,A._depth,A._spectrum,A._data);
25398       typedef _cimg_Ttfloat Ttfloat;
25399       const Ttfloat epsilon = 1e-4f;
25400       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
25401       for (int i = 1; i<(int)siz; ++i) {
25402         const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
25403         B[i] -= m*A(2,i - 1);
25404         V[i] -= m*V[i - 1];
25405       }
25406       (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
25407       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));
25408       return *this;
25409     }
25410 
25411     //! Solve a tridiagonal system of linear equations \newinstance.
25412     template<typename t>
25413     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
25414       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
25415     }
25416 
25417     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
25418     /**
25419        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
25420        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
25421     **/
25422     template<typename t>
25423     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
25424       if (is_empty()) { val.assign(); vec.assign(); }
25425       else {
25426         if (_width!=_height || _depth>1 || _spectrum>1)
25427           throw CImgInstanceException(_cimg_instance
25428                                       "eigen(): Instance is not a square matrix.",
25429                                       cimg_instance);
25430 
25431         if (val.size()<(ulongT)_width) val.assign(1,_width);
25432         if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
25433         switch (_width) {
25434         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
25435         case 2 : {
25436           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
25437           double f = e*e - 4*(a*d - b*c);
25438           if (f<0)
25439             cimg::warn(_cimg_instance
25440                        "eigen(): Complex eigenvalues found.",
25441                        cimg_instance);
25442 
25443           f = std::sqrt(f);
25444           const double
25445             l1 = 0.5*(e - f),
25446             l2 = 0.5*(e + f),
25447             b2 = b*b,
25448             norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
25449             norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
25450           val[0] = (t)l2;
25451           val[1] = (t)l1;
25452           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; }
25453           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; }
25454         } break;
25455         default :
25456           throw CImgInstanceException(_cimg_instance
25457                                       "eigen(): Eigenvalues computation of general matrices is limited "
25458                                       "to 2x2 matrices.",
25459                                       cimg_instance);
25460         }
25461       }
25462       return *this;
25463     }
25464 
25465     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
25466     /**
25467        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
25468     **/
25469     CImgList<Tfloat> get_eigen() const {
25470       CImgList<Tfloat> res(2);
25471       eigen(res[0],res[1]);
25472       return res;
25473     }
25474 
25475     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
25476     /**
25477        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
25478        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
25479     **/
25480     template<typename t>
25481     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
25482       if (is_empty()) { val.assign(); vec.assign(); }
25483       else {
25484 #ifdef cimg_use_lapack
25485         char JOB = 'V', UPLO = 'U';
25486         int N = _width, LWORK = 4*N, INFO;
25487         Tfloat
25488           *const lapA = new Tfloat[N*N],
25489           *const lapW = new Tfloat[N],
25490           *const WORK = new Tfloat[LWORK];
25491         cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
25492         cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
25493         if (INFO)
25494           cimg::warn(_cimg_instance
25495                      "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
25496                      cimg_instance,
25497                      INFO);
25498 
25499         val.assign(1,N);
25500 	vec.assign(N,N);
25501         if (!INFO) {
25502           cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
25503           cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
25504         } else { val.fill(0); vec.fill(0); }
25505         delete[] lapA; delete[] lapW; delete[] WORK;
25506 #else
25507         if (_width!=_height || _depth>1 || _spectrum>1)
25508           throw CImgInstanceException(_cimg_instance
25509                                       "eigen(): Instance is not a square matrix.",
25510                                       cimg_instance);
25511 
25512 	val.assign(1,_width);
25513 	if (vec._data) vec.assign(_width,_width);
25514         if (_width<3) {
25515           eigen(val,vec);
25516           if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
25517           return *this;
25518         }
25519         CImg<t> V(_width,_width);
25520         Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
25521         (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
25522         if (maxabs!=1) val*=maxabs;
25523 
25524 	bool is_ambiguous = false;
25525 	float eig = 0;
25526 	cimg_forY(val,p) {       // check for ambiguous cases.
25527 	  if (val[p]>eig) eig = (float)val[p];
25528           t scal = 0;
25529           cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
25530           if (cimg::abs(scal)<0.9f) is_ambiguous = true;
25531           if (scal<0) val[p] = -val[p];
25532         }
25533 	if (is_ambiguous) {
25534 	  ++(eig*=2);
25535 	  SVD(vec,val,V,false,40,eig);
25536 	  val-=eig;
25537 	}
25538         CImg<intT> permutations;  // sort eigenvalues in decreasing order
25539         CImg<t> tmp(_width);
25540         val.sort(permutations,false);
25541         cimg_forY(vec,k) {
25542           cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
25543           std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
25544         }
25545 #endif
25546       }
25547       return *this;
25548     }
25549 
25550     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
25551     /**
25552        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
25553          symmetric_eigen(CImg<t>&,CImg<t>&) const.
25554     **/
25555     CImgList<Tfloat> get_symmetric_eigen() const {
25556       CImgList<Tfloat> res(2);
25557       symmetric_eigen(res[0],res[1]);
25558       return res;
25559     }
25560 
25561     //! Sort pixel values and get sorting permutations.
25562     /**
25563        \param[out] permutations Permutation map used for the sorting.
25564        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
25565     **/
25566     template<typename t>
25567     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
25568       permutations.assign(_width,_height,_depth,_spectrum);
25569       if (is_empty()) return *this;
25570       cimg_foroff(permutations,off) permutations[off] = (t)off;
25571       return _quicksort(0,size() - 1,permutations,is_increasing,true);
25572     }
25573 
25574     //! Sort pixel values and get sorting permutations \newinstance.
25575     template<typename t>
25576     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
25577       return (+*this).sort(permutations,is_increasing);
25578     }
25579 
25580     //! Sort pixel values.
25581     /**
25582        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
25583        \param axis Tells if the value sorting must be done along a specific axis. Can be:
25584        - \c 0: All pixel values are sorted, independently on their initial position.
25585        - \c 'x': Image columns are sorted, according to the first value in each column.
25586        - \c 'y': Image rows are sorted, according to the first value in each row.
25587        - \c 'z': Image slices are sorted, according to the first value in each slice.
25588        - \c 'c': Image channels are sorted, according to the first value in each channel.
25589     **/
25590     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
25591       if (is_empty()) return *this;
25592       CImg<uintT> perm;
25593       switch (cimg::lowercase(axis)) {
25594       case 0 :
25595         _quicksort(0,size() - 1,perm,is_increasing,false);
25596         break;
25597       case 'x' : {
25598         perm.assign(_width);
25599         get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
25600         CImg<T> img(*this,false);
25601         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
25602       } break;
25603       case 'y' : {
25604         perm.assign(_height);
25605         get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
25606         CImg<T> img(*this,false);
25607         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
25608       } break;
25609       case 'z' : {
25610         perm.assign(_depth);
25611         get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
25612         CImg<T> img(*this,false);
25613         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
25614       } break;
25615       case 'c' : {
25616         perm.assign(_spectrum);
25617         get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
25618         CImg<T> img(*this,false);
25619         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
25620       } break;
25621       default :
25622         throw CImgArgumentException(_cimg_instance
25623                                     "sort(): Invalid specified axis '%c' "
25624                                     "(should be { x | y | z | c }).",
25625                                     cimg_instance,axis);
25626       }
25627       return *this;
25628     }
25629 
25630     //! Sort pixel values \newinstance.
25631     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
25632       return (+*this).sort(is_increasing,axis);
25633     }
25634 
25635     template<typename t>
25636     CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
25637                         const bool is_increasing, const bool is_permutations) {
25638       if (indm<indM) {
25639         const long mid = (indm + indM)/2;
25640         if (is_increasing) {
25641           if ((*this)[indm]>(*this)[mid]) {
25642             cimg::swap((*this)[indm],(*this)[mid]);
25643             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25644           }
25645           if ((*this)[mid]>(*this)[indM]) {
25646             cimg::swap((*this)[indM],(*this)[mid]);
25647             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
25648           }
25649           if ((*this)[indm]>(*this)[mid]) {
25650             cimg::swap((*this)[indm],(*this)[mid]);
25651             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25652           }
25653         } else {
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         }
25667         if (indM - indm>=3) {
25668           const T pivot = (*this)[mid];
25669           long i = indm, j = indM;
25670           if (is_increasing) {
25671             do {
25672               while ((*this)[i]<pivot) ++i;
25673               while ((*this)[j]>pivot) --j;
25674               if (i<=j) {
25675                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
25676                 cimg::swap((*this)[i++],(*this)[j--]);
25677               }
25678             } while (i<=j);
25679           } else {
25680             do {
25681               while ((*this)[i]>pivot) ++i;
25682               while ((*this)[j]<pivot) --j;
25683               if (i<=j) {
25684                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
25685                 cimg::swap((*this)[i++],(*this)[j--]);
25686               }
25687             } while (i<=j);
25688           }
25689           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
25690           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
25691         }
25692       }
25693       return *this;
25694     }
25695 
25696     //! Compute the SVD of the instance image, viewed as a general matrix.
25697     /**
25698        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
25699        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
25700        \param[out] U First matrix of the SVD product.
25701        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
25702          These coefficients are stored as a vector.
25703        \param[out] V Third matrix of the SVD product.
25704        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
25705        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
25706        \param lambda Epsilon used for the algorithm convergence.
25707        \note The instance matrix can be computed from \c U,\c S and \c V by
25708        \code
25709        const CImg<> A;  // Input matrix (assumed to contain some values).
25710        CImg<> U,S,V;
25711        A.SVD(U,S,V)
25712        \endcode
25713     **/
25714     template<typename t>
25715     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
25716                        const unsigned int max_iteration=40, const float lambda=0) const {
25717       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
25718       else {
25719         U = *this;
25720 	if (lambda!=0) {
25721 	  const unsigned int delta = std::min(U._width,U._height);
25722 	  for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
25723 	}
25724         if (S.size()<_width) S.assign(1,_width);
25725         if (V._width<_width || V._height<_height) V.assign(_width,_width);
25726         CImg<t> rv1(_width);
25727         t anorm = 0, c, f, g = 0, h, s, scale = 0;
25728         int l = 0, nm = 0;
25729 
25730         cimg_forX(U,i) {
25731           l = i + 1; rv1[i] = scale*g; g = s = scale = 0;
25732           if (i<height()) {
25733             for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
25734             if (scale) {
25735               for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+=U(i,k)*U(i,k); }
25736               f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
25737               for (int j = l; j<width(); ++j) {
25738                 s = 0;
25739                 for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
25740                 f = s/h;
25741                 for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
25742               }
25743               for (int k = i; k<height(); ++k) U(i,k)*=scale;
25744             }
25745           }
25746           S[i]=scale*g;
25747 
25748           g = s = scale = 0;
25749           if (i<height() && i!=width() - 1) {
25750             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
25751             if (scale) {
25752               for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+=U(k,i)*U(k,i); }
25753               f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
25754               for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
25755               for (int j = l; j<height(); ++j) {
25756                 s = 0;
25757                 for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
25758                 for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
25759               }
25760               for (int k = l; k<width(); ++k) U(k,i)*=scale;
25761             }
25762           }
25763           anorm = (t)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
25764         }
25765 
25766         for (int i = width() - 1; i>=0; --i) {
25767           if (i<width()-1) {
25768             if (g) {
25769               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
25770               for (int j = l; j<width(); ++j) {
25771                 s = 0;
25772                 for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
25773                 for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
25774               }
25775             }
25776             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
25777           }
25778           V(i,i) = (t)1.0; g = rv1[i]; l = i;
25779         }
25780 
25781         for (int i = std::min(width(),height()) - 1; i>=0; --i) {
25782           l = i + 1; g = S[i];
25783           for (int j = l; j<width(); ++j) U(j,i) = 0;
25784           if (g) {
25785             g = 1/g;
25786             for (int j = l; j<width(); ++j) {
25787               s = 0; for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
25788               f = (s/U(i,i))*g;
25789               for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
25790             }
25791             for (int j = i; j<height(); ++j) U(i,j)*= g;
25792           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
25793           ++U(i,i);
25794         }
25795 
25796         for (int k = width() - 1; k>=0; --k) {
25797           for (unsigned int its = 0; its<max_iteration; ++its) {
25798             bool flag = true;
25799             for (l = k; l>=1; --l) {
25800               nm = l - 1;
25801               if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
25802               if ((cimg::abs(S[nm]) + anorm)==anorm) break;
25803             }
25804             if (flag) {
25805               c = 0; s = 1;
25806               for (int i = l; i<=k; ++i) {
25807                 f = s*rv1[i]; rv1[i] = c*rv1[i];
25808                 if ((cimg::abs(f) + anorm)==anorm) break;
25809                 g = S[i]; h = cimg::_hypot(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
25810                 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; }
25811               }
25812             }
25813 
25814             const t z = S[k];
25815             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
25816             nm = k - 1;
25817             t x = S[l], y = S[nm];
25818             g = rv1[nm]; h = rv1[k];
25819             f = ((y - z)*(y + z)+(g - h)*(g + h))/std::max((t)1e-25,2*h*y);
25820             g = cimg::_hypot(f,(t)1);
25821             f = ((x - z)*(x + z)+h*((y/(f + (f>=0?g:-g))) - h))/std::max((t)1e-25,x);
25822             c = s = 1;
25823             for (int j = l; j<=nm; ++j) {
25824               const int i = j + 1;
25825               g = rv1[i]; h = s*g; g = c*g;
25826               t y = S[i];
25827               t z = cimg::_hypot(f,h);
25828               rv1[j] = z; c = f/std::max((t)1e-25,z); s = h/std::max((t)1e-25,z);
25829               f = x*c + g*s; g = g*c - x*s; h = y*s; y*=c;
25830               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; }
25831               z = cimg::_hypot(f,h); S[j] = z;
25832               if (z) { z = 1/std::max((t)1e-25,z); c = f*z; s = h*z; }
25833               f = c*g + s*y; x = c*y - s*g;
25834               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; }
25835             }
25836             rv1[l] = 0; rv1[k]=f; S[k]=x;
25837           }
25838         }
25839 
25840         if (sorting) {
25841           CImg<intT> permutations;
25842           CImg<t> tmp(_width);
25843           S.sort(permutations,false);
25844           cimg_forY(U,k) {
25845             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
25846             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
25847           }
25848           cimg_forY(V,k) {
25849             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
25850             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
25851           }
25852         }
25853       }
25854       return *this;
25855     }
25856 
25857     //! Compute the SVD of the instance image, viewed as a general matrix.
25858     /**
25859        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
25860          SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
25861     **/
25862     CImgList<Tfloat> get_SVD(const bool sorting=true,
25863                              const unsigned int max_iteration=40, const float lambda=0) const {
25864       CImgList<Tfloat> res(3);
25865       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
25866       return res;
25867     }
25868 
25869     // [internal] Compute the LU decomposition of a permuted matrix.
25870     template<typename t>
25871     CImg<T>& _LU(CImg<t>& indx, bool& d) {
25872       const int N = width();
25873       int imax = 0;
25874       CImg<Tfloat> vv(N);
25875       indx.assign(N);
25876       d = true;
25877       cimg_forX(*this,i) {
25878         Tfloat vmax = 0;
25879         cimg_forX(*this,j) {
25880           const Tfloat tmp = cimg::abs((*this)(j,i));
25881           if (tmp>vmax) vmax = tmp;
25882         }
25883         if (vmax==0) { indx.fill(0); return fill(0); }
25884         vv[i] = 1/vmax;
25885       }
25886       cimg_forX(*this,j) {
25887         for (int i = 0; i<j; ++i) {
25888           Tfloat sum=(*this)(j,i);
25889           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
25890           (*this)(j,i) = (T)sum;
25891         }
25892         Tfloat vmax = 0;
25893         for (int i = j; i<width(); ++i) {
25894           Tfloat sum=(*this)(j,i);
25895           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
25896           (*this)(j,i) = (T)sum;
25897           const Tfloat tmp = vv[i]*cimg::abs(sum);
25898           if (tmp>=vmax) { vmax=tmp; imax=i; }
25899         }
25900         if (j!=imax) {
25901           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
25902           d =!d;
25903           vv[imax] = vv[j];
25904         }
25905         indx[j] = (t)imax;
25906         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
25907         if (j<N) {
25908           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
25909           for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
25910         }
25911       }
25912       return *this;
25913     }
25914 
25915     //! Compute minimal path in a graph, using the Dijkstra algorithm.
25916     /**
25917        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
25918          between two nodes (i,j).
25919        \param nb_nodes Number of graph nodes.
25920        \param starting_node Indice of the starting node.
25921        \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
25922        \param previous_node Array that gives the previous node indice in the path to the starting node
25923          (optional parameter).
25924        \return Array of distances of each node to the starting node.
25925     **/
25926     template<typename tf, typename t>
25927     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
25928                             const unsigned int starting_node, const unsigned int ending_node,
25929                             CImg<t>& previous_node) {
25930       if (starting_node>=nb_nodes)
25931         throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher "
25932                                     "than number of nodes %u.",
25933                                     pixel_type(),starting_node,nb_nodes);
25934       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
25935       dist(starting_node) = 0;
25936       previous_node.assign(1,nb_nodes,1,1,(t)-1);
25937       previous_node(starting_node) = (t)starting_node;
25938       CImg<uintT> Q(nb_nodes);
25939       cimg_forX(Q,u) Q(u) = (unsigned int)u;
25940       cimg::swap(Q(starting_node),Q(0));
25941       unsigned int sizeQ = nb_nodes;
25942       while (sizeQ) {
25943         // Update neighbors from minimal vertex
25944         const unsigned int umin = Q(0);
25945         if (umin==ending_node) sizeQ = 0;
25946         else {
25947           const T dmin = dist(umin);
25948           const T infty = cimg::type<T>::max();
25949           for (unsigned int q = 1; q<sizeQ; ++q) {
25950             const unsigned int v = Q(q);
25951             const T d = (T)distance(v,umin);
25952             if (d<infty) {
25953               const T alt = dmin + d;
25954               if (alt<dist(v)) {
25955                 dist(v) = alt;
25956                 previous_node(v) = (t)umin;
25957                 const T distpos = dist(Q(q));
25958                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
25959                   cimg::swap(Q(pos),Q(par));
25960               }
25961             }
25962           }
25963           // Remove minimal vertex from queue
25964           Q(0) = Q(--sizeQ);
25965           const T distpos = dist(Q(0));
25966           for (unsigned int pos = 0, left = 0, right = 0;
25967                ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
25968                  (right<sizeQ && distpos>dist(Q(right)));) {
25969             if (right<sizeQ) {
25970               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
25971               else { cimg::swap(Q(pos),Q(right)); pos = right; }
25972             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
25973           }
25974         }
25975       }
25976       return dist;
25977     }
25978 
25979     //! Return minimal path in a graph, using the Dijkstra algorithm.
25980     template<typename tf, typename t>
25981     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
25982                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
25983       CImg<uintT> foo;
25984       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
25985     }
25986 
25987     //! Return minimal path in a graph, using the Dijkstra algorithm.
25988     /**
25989        \param starting_node Indice of the starting node.
25990        \param ending_node Indice of the ending node.
25991        \param previous_node Array that gives the previous node indice in the path to the starting node
25992          (optional parameter).
25993        \return Array of distances of each node to the starting node.
25994        \note image instance corresponds to the adjacency matrix of the graph.
25995     **/
25996     template<typename t>
25997     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
25998                       CImg<t>& previous_node) {
25999       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
26000     }
26001 
26002     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
26003     template<typename t>
26004     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
26005                          CImg<t>& previous_node) const {
26006       if (_width!=_height || _depth!=1 || _spectrum!=1)
26007         throw CImgInstanceException(_cimg_instance
26008                                     "dijkstra(): Instance is not a graph adjacency matrix.",
26009                                     cimg_instance);
26010 
26011       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
26012     }
26013 
26014     //! Return minimal path in a graph, using the Dijkstra algorithm.
26015     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
26016       return get_dijkstra(starting_node,ending_node).move_to(*this);
26017     }
26018 
26019     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
26020     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
26021       CImg<uintT> foo;
26022       return get_dijkstra(starting_node,ending_node,foo);
26023     }
26024 
26025     //! Return an image containing the ascii codes of the specified  string.
26026     /**
26027        \param str input C-string to encode as an image.
26028        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
26029        \param is_shared Return result that shares its buffer with \p str.
26030     **/
26031     static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
26032       if (!str) return CImg<T>();
26033       return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
26034     }
26035 
26036     //! Return a \c 1x1 image containing specified value.
26037     /**
26038        \param a0 First vector value.
26039     **/
26040     static CImg<T> vector(const T& a0) {
26041       CImg<T> r(1,1);
26042       r[0] = a0;
26043       return r;
26044     }
26045 
26046     //! Return a \c 1x2 image containing specified values.
26047     /**
26048        \param a0 First vector value.
26049        \param a1 Second vector value.
26050     **/
26051     static CImg<T> vector(const T& a0, const T& a1) {
26052       CImg<T> r(1,2); T *ptr = r._data;
26053       *(ptr++) = a0; *(ptr++) = a1;
26054       return r;
26055     }
26056 
26057     //! Return a \c 1x3 image containing specified values.
26058     /**
26059        \param a0 First vector value.
26060        \param a1 Second vector value.
26061        \param a2 Third vector value.
26062     **/
26063     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
26064       CImg<T> r(1,3); T *ptr = r._data;
26065       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
26066       return r;
26067     }
26068 
26069     //! Return a \c 1x4 image containing specified values.
26070     /**
26071        \param a0 First vector value.
26072        \param a1 Second vector value.
26073        \param a2 Third vector value.
26074        \param a3 Fourth vector value.
26075     **/
26076     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
26077       CImg<T> r(1,4); T *ptr = r._data;
26078       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26079       return r;
26080     }
26081 
26082     //! Return a \c 1x5 image containing specified values.
26083     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
26084       CImg<T> r(1,5); T *ptr = r._data;
26085       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
26086       return r;
26087     }
26088 
26089     //! Return a \c 1x6 image containing specified values.
26090     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
26091       CImg<T> r(1,6); T *ptr = r._data;
26092       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
26093       return r;
26094     }
26095 
26096     //! Return a \c 1x7 image containing specified values.
26097     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26098 			  const T& a4, const T& a5, const T& a6) {
26099       CImg<T> r(1,7); T *ptr = r._data;
26100       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26101       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
26102       return r;
26103     }
26104 
26105     //! Return a \c 1x8 image containing specified values.
26106     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26107 			  const T& a4, const T& a5, const T& a6, const T& a7) {
26108       CImg<T> r(1,8); T *ptr = r._data;
26109       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26110       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26111       return r;
26112     }
26113 
26114     //! Return a \c 1x9 image containing specified values.
26115     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26116 			  const T& a4, const T& a5, const T& a6, const T& a7,
26117 			  const T& a8) {
26118       CImg<T> r(1,9); T *ptr = r._data;
26119       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26120       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26121       *(ptr++) = a8;
26122       return r;
26123     }
26124 
26125     //! Return a \c 1x10 image containing specified values.
26126     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26127                           const T& a4, const T& a5, const T& a6, const T& a7,
26128                           const T& a8, const T& a9) {
26129       CImg<T> r(1,10); T *ptr = r._data;
26130       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26131       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26132       *(ptr++) = a8; *(ptr++) = a9;
26133       return r;
26134     }
26135 
26136     //! Return a \c 1x11 image containing specified values.
26137     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26138                           const T& a4, const T& a5, const T& a6, const T& a7,
26139                           const T& a8, const T& a9, const T& a10) {
26140       CImg<T> r(1,11); T *ptr = r._data;
26141       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26142       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26143       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
26144       return r;
26145     }
26146 
26147     //! Return a \c 1x12 image containing specified values.
26148     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26149                           const T& a4, const T& a5, const T& a6, const T& a7,
26150                           const T& a8, const T& a9, const T& a10, const T& a11) {
26151       CImg<T> r(1,12); T *ptr = r._data;
26152       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26153       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26154       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26155       return r;
26156     }
26157 
26158     //! Return a \c 1x13 image containing specified values.
26159     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26160                           const T& a4, const T& a5, const T& a6, const T& a7,
26161                           const T& a8, const T& a9, const T& a10, const T& a11,
26162 			  const T& a12) {
26163       CImg<T> r(1,13); T *ptr = r._data;
26164       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26165       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26166       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26167       *(ptr++) = a12;
26168       return r;
26169     }
26170 
26171     //! Return a \c 1x14 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, const T& a13) {
26176       CImg<T> r(1,14); 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; *(ptr++) = a13;
26181       return r;
26182     }
26183 
26184     //! Return a \c 1x15 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, const T& a14) {
26189       CImg<T> r(1,15); 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; *(ptr++) = a14;
26194       return r;
26195     }
26196 
26197     //! Return a \c 1x16 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, const T& a15) {
26202       CImg<T> r(1,16); 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; *(ptr++) = a15;
26207       return r;
26208     }
26209 
26210     //! Return a 1x1 matrix containing specified coefficients.
26211     /**
26212        \param a0 First matrix value.
26213        \note Equivalent to vector(const T&).
26214     **/
26215     static CImg<T> matrix(const T& a0) {
26216       return vector(a0);
26217     }
26218 
26219     //! Return a 2x2 matrix containing specified coefficients.
26220     /**
26221        \param a0 First matrix value.
26222        \param a1 Second matrix value.
26223        \param a2 Third matrix value.
26224        \param a3 Fourth matrix value.
26225     **/
26226     static CImg<T> matrix(const T& a0, const T& a1,
26227 			  const T& a2, const T& a3) {
26228       CImg<T> r(2,2); T *ptr = r._data;
26229       *(ptr++) = a0; *(ptr++) = a1;
26230       *(ptr++) = a2; *(ptr++) = a3;
26231       return r;
26232     }
26233 
26234     //! Return a 3x3 matrix containing specified coefficients.
26235     /**
26236        \param a0 First matrix value.
26237        \param a1 Second matrix value.
26238        \param a2 Third matrix value.
26239        \param a3 Fourth matrix value.
26240        \param a4 Fifth matrix value.
26241        \param a5 Sixth matrix value.
26242        \param a6 Seventh matrix value.
26243        \param a7 Eighth matrix value.
26244        \param a8 Nineth matrix value.
26245     **/
26246     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
26247 			  const T& a3, const T& a4, const T& a5,
26248 			  const T& a6, const T& a7, const T& a8) {
26249       CImg<T> r(3,3); T *ptr = r._data;
26250       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
26251       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
26252       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
26253       return r;
26254     }
26255 
26256     //! Return a 4x4 matrix containing specified coefficients.
26257     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
26258 			  const T& a4, const T& a5, const T& a6, const T& a7,
26259 			  const T& a8, const T& a9, const T& a10, const T& a11,
26260 			  const T& a12, const T& a13, const T& a14, const T& a15) {
26261       CImg<T> r(4,4); T *ptr = r._data;
26262       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26263       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26264       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26265       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
26266       return r;
26267     }
26268 
26269     //! Return a 5x5 matrix containing specified coefficients.
26270     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
26271                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
26272                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
26273                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
26274                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
26275       CImg<T> r(5,5); T *ptr = r._data;
26276       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
26277       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
26278       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
26279       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
26280       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
26281       return r;
26282     }
26283 
26284     //! Return a 1x1 symmetric matrix containing specified coefficients.
26285     /**
26286        \param a0 First matrix value.
26287        \note Equivalent to vector(const T&).
26288     **/
26289     static CImg<T> tensor(const T& a0) {
26290       return matrix(a0);
26291     }
26292 
26293     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
26294     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
26295       return matrix(a0,a1,a1,a2);
26296     }
26297 
26298     //! Return a 3x3 symmetric matrix containing specified coefficients.
26299     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
26300       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
26301     }
26302 
26303     //! Return a 1x1 diagonal matrix containing specified coefficients.
26304     static CImg<T> diagonal(const T& a0) {
26305       return matrix(a0);
26306     }
26307 
26308     //! Return a 2x2 diagonal matrix containing specified coefficients.
26309     static CImg<T> diagonal(const T& a0, const T& a1) {
26310       return matrix(a0,0,0,a1);
26311     }
26312 
26313     //! Return a 3x3 diagonal matrix containing specified coefficients.
26314     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
26315       return matrix(a0,0,0,0,a1,0,0,0,a2);
26316     }
26317 
26318     //! Return a 4x4 diagonal matrix containing specified coefficients.
26319     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
26320       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
26321     }
26322 
26323     //! Return a 5x5 diagonal matrix containing specified coefficients.
26324     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
26325       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);
26326     }
26327 
26328     //! Return a NxN identity matrix.
26329     /**
26330        \param N Dimension of the matrix.
26331     **/
26332     static CImg<T> identity_matrix(const unsigned int N) {
26333       CImg<T> res(N,N,1,1,0);
26334       cimg_forX(res,x) res(x,x) = 1;
26335       return res;
26336     }
26337 
26338     //! Return a N-numbered sequence vector from \p a0 to \p a1.
26339     /**
26340        \param N Size of the resulting vector.
26341        \param a0 Starting value of the sequence.
26342        \param a1 Ending value of the sequence.
26343      **/
26344     static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
26345       if (N) return CImg<T>(1,N).sequence(a0,a1);
26346       return CImg<T>();
26347     }
26348 
26349     //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
26350     /**
26351        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
26352        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
26353        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
26354        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
26355        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
26356      **/
26357     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
26358                                    const bool is_quaternion=false) {
26359       double X, Y, Z, W, N;
26360       if (is_quaternion) {
26361         N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
26362         if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
26363         else { X = Y = Z = 0; W = 1; }
26364         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),
26365                                (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
26366                                (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
26367       }
26368       N = cimg::hypot((double)x,(double)y,(double)z);
26369       if (N>0) { X = x/N; Y = y/N; Z = z/N; }
26370       else { X = Y = 0; Z = 1; }
26371       const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
26372       return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
26373                              (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
26374                              (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
26375     }
26376 
26377     //@}
26378     //-----------------------------------
26379     //
26380     //! \name Value Manipulation
26381     //@{
26382     //-----------------------------------
26383 
26384     //! Fill all pixel values with specified value.
26385     /**
26386        \param val Fill value.
26387     **/
26388     CImg<T>& fill(const T& val) {
26389       if (is_empty()) return *this;
26390       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
26391       else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
26392       return *this;
26393     }
26394 
26395     //! Fill all pixel values with specified value \newinstance.
26396     CImg<T> get_fill(const T& val) const {
26397       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
26398     }
26399 
26400     //! Fill sequentially all pixel values with specified values.
26401     /**
26402        \param val0 First fill value.
26403        \param val1 Second fill value.
26404     **/
26405     CImg<T>& fill(const T& val0, const T& val1) {
26406       if (is_empty()) return *this;
26407       T *ptrd, *ptre = end() - 1;
26408       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
26409       if (ptrd!=ptre + 1) *(ptrd++) = val0;
26410       return *this;
26411     }
26412 
26413     //! Fill sequentially all pixel values with specified values \newinstance.
26414     CImg<T> get_fill(const T& val0, const T& val1) const {
26415       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
26416     }
26417 
26418     //! Fill sequentially all pixel values with specified values \overloading.
26419     CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
26420       if (is_empty()) return *this;
26421       T *ptrd, *ptre = end() - 2;
26422       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
26423       ptre+=2;
26424       switch (ptre - ptrd) {
26425       case 2 : *(--ptre) = val1; // fallthrough
26426       case 1 : *(--ptre) = val0; // fallthrough
26427       }
26428       return *this;
26429     }
26430 
26431     //! Fill sequentially all pixel values with specified values \newinstance.
26432     CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
26433       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
26434     }
26435 
26436     //! Fill sequentially all pixel values with specified values \overloading.
26437     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
26438       if (is_empty()) return *this;
26439       T *ptrd, *ptre = end() - 3;
26440       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
26441       ptre+=3;
26442       switch (ptre - ptrd) {
26443       case 3 : *(--ptre) = val2; // fallthrough
26444       case 2 : *(--ptre) = val1; // fallthrough
26445       case 1 : *(--ptre) = val0; // fallthrough
26446       }
26447       return *this;
26448     }
26449 
26450     //! Fill sequentially all pixel values with specified values \newinstance.
26451     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
26452       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
26453     }
26454 
26455     //! Fill sequentially all pixel values with specified values \overloading.
26456     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
26457       if (is_empty()) return *this;
26458       T *ptrd, *ptre = end() - 4;
26459       for (ptrd = _data; ptrd<ptre; ) {
26460         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26461       }
26462       ptre+=4;
26463       switch (ptre - ptrd) {
26464       case 4 : *(--ptre) = val3; // fallthrough
26465       case 3 : *(--ptre) = val2; // fallthrough
26466       case 2 : *(--ptre) = val1; // fallthrough
26467       case 1 : *(--ptre) = val0; // fallthrough
26468       }
26469       return *this;
26470     }
26471 
26472     //! Fill sequentially all pixel values with specified values \newinstance.
26473     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
26474       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
26475     }
26476 
26477     //! Fill sequentially all pixel values with specified values \overloading.
26478     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
26479       if (is_empty()) return *this;
26480       T *ptrd, *ptre = end() - 5;
26481       for (ptrd = _data; ptrd<ptre; ) {
26482         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26483       }
26484       ptre+=5;
26485       switch (ptre - ptrd) {
26486       case 5 : *(--ptre) = val4; // fallthrough
26487       case 4 : *(--ptre) = val3; // fallthrough
26488       case 3 : *(--ptre) = val2; // fallthrough
26489       case 2 : *(--ptre) = val1; // fallthrough
26490       case 1 : *(--ptre) = val0; // fallthrough
26491       }
26492       return *this;
26493     }
26494 
26495     //! Fill sequentially all pixel values with specified values \newinstance.
26496     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
26497       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
26498     }
26499 
26500     //! Fill sequentially all pixel values with specified values \overloading.
26501     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26502                   const T& val6) {
26503       if (is_empty()) return *this;
26504       T *ptrd, *ptre = end() - 6;
26505       for (ptrd = _data; ptrd<ptre; ) {
26506         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26507         *(ptrd++) = val6;
26508       }
26509       ptre+=6;
26510       switch (ptre - ptrd) {
26511       case 6 : *(--ptre) = val5; // fallthrough
26512       case 5 : *(--ptre) = val4; // fallthrough
26513       case 4 : *(--ptre) = val3; // fallthrough
26514       case 3 : *(--ptre) = val2; // fallthrough
26515       case 2 : *(--ptre) = val1; // fallthrough
26516       case 1 : *(--ptre) = val0; // fallthrough
26517       }
26518       return *this;
26519     }
26520 
26521     //! Fill sequentially all pixel values with specified values \newinstance.
26522     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26523                      const T& val6) const {
26524       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
26525     }
26526 
26527     //! Fill sequentially all pixel values with specified values \overloading.
26528     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26529                   const T& val6, const T& val7) {
26530       if (is_empty()) return *this;
26531       T *ptrd, *ptre = end() - 7;
26532       for (ptrd = _data; ptrd<ptre; ) {
26533         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
26534         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
26535       }
26536       ptre+=7;
26537       switch (ptre - ptrd) {
26538       case 7 : *(--ptre) = val6; // fallthrough
26539       case 6 : *(--ptre) = val5; // fallthrough
26540       case 5 : *(--ptre) = val4; // fallthrough
26541       case 4 : *(--ptre) = val3; // fallthrough
26542       case 3 : *(--ptre) = val2; // fallthrough
26543       case 2 : *(--ptre) = val1; // fallthrough
26544       case 1 : *(--ptre) = val0; // fallthrough
26545       }
26546       return *this;
26547     }
26548 
26549     //! Fill sequentially all pixel values with specified values \newinstance.
26550     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26551                      const T& val6, const T& val7) const {
26552       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
26553     }
26554 
26555     //! Fill sequentially all pixel values with specified values \overloading.
26556     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26557                   const T& val6, const T& val7, const T& val8) {
26558       if (is_empty()) return *this;
26559       T *ptrd, *ptre = end() - 8;
26560       for (ptrd = _data; ptrd<ptre; ) {
26561         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
26562         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26563         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
26564       }
26565       ptre+=8;
26566       switch (ptre - ptrd) {
26567       case 8 : *(--ptre) = val7; // fallthrough
26568       case 7 : *(--ptre) = val6; // fallthrough
26569       case 6 : *(--ptre) = val5; // fallthrough
26570       case 5 : *(--ptre) = val4; // fallthrough
26571       case 4 : *(--ptre) = val3; // fallthrough
26572       case 3 : *(--ptre) = val2; // fallthrough
26573       case 2 : *(--ptre) = val1; // fallthrough
26574       case 1 : *(--ptre) = val0; // fallthrough
26575       }
26576       return *this;
26577     }
26578 
26579     //! Fill sequentially all pixel values with specified values \newinstance.
26580     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26581                      const T& val6, const T& val7, const T& val8) const {
26582       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
26583     }
26584 
26585     //! Fill sequentially all pixel values with specified values \overloading.
26586     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26587                   const T& val6, const T& val7, const T& val8, const T& val9) {
26588       if (is_empty()) return *this;
26589       T *ptrd, *ptre = end() - 9;
26590       for (ptrd = _data; ptrd<ptre; ) {
26591         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26592         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
26593       }
26594       ptre+=9;
26595       switch (ptre - ptrd) {
26596       case 9 : *(--ptre) = val8; // fallthrough
26597       case 8 : *(--ptre) = val7; // fallthrough
26598       case 7 : *(--ptre) = val6; // fallthrough
26599       case 6 : *(--ptre) = val5; // fallthrough
26600       case 5 : *(--ptre) = val4; // fallthrough
26601       case 4 : *(--ptre) = val3; // fallthrough
26602       case 3 : *(--ptre) = val2; // fallthrough
26603       case 2 : *(--ptre) = val1; // fallthrough
26604       case 1 : *(--ptre) = val0; // fallthrough
26605       }
26606       return *this;
26607     }
26608 
26609     //! Fill sequentially all pixel values with specified values \newinstance.
26610     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26611                      const T& val6, const T& val7, const T& val8, const T& val9) const {
26612       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
26613     }
26614 
26615     //! Fill sequentially all pixel values with specified values \overloading.
26616     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26617                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
26618       if (is_empty()) return *this;
26619       T *ptrd, *ptre = end() - 10;
26620       for (ptrd = _data; ptrd<ptre; ) {
26621         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26622         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
26623         *(ptrd++) = val10;
26624       }
26625       ptre+=10;
26626       switch (ptre - ptrd) {
26627       case 10 : *(--ptre) = val9; // fallthrough
26628       case 9 : *(--ptre) = val8; // fallthrough
26629       case 8 : *(--ptre) = val7; // fallthrough
26630       case 7 : *(--ptre) = val6; // fallthrough
26631       case 6 : *(--ptre) = val5; // fallthrough
26632       case 5 : *(--ptre) = val4; // fallthrough
26633       case 4 : *(--ptre) = val3; // fallthrough
26634       case 3 : *(--ptre) = val2; // fallthrough
26635       case 2 : *(--ptre) = val1; // fallthrough
26636       case 1 : *(--ptre) = val0; // fallthrough
26637       }
26638       return *this;
26639     }
26640 
26641     //! Fill sequentially all pixel values with specified values \newinstance.
26642     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26643                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
26644       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
26645     }
26646 
26647     //! Fill sequentially all pixel values with specified values \overloading.
26648     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26649                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
26650       if (is_empty()) return *this;
26651       T *ptrd, *ptre = end() - 11;
26652       for (ptrd = _data; ptrd<ptre; ) {
26653         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26654         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26655       }
26656       ptre+=11;
26657       switch (ptre - ptrd) {
26658       case 11 : *(--ptre) = val10; // fallthrough
26659       case 10 : *(--ptre) = val9; // fallthrough
26660       case 9 : *(--ptre) = val8; // fallthrough
26661       case 8 : *(--ptre) = val7; // fallthrough
26662       case 7 : *(--ptre) = val6; // fallthrough
26663       case 6 : *(--ptre) = val5; // fallthrough
26664       case 5 : *(--ptre) = val4; // fallthrough
26665       case 4 : *(--ptre) = val3; // fallthrough
26666       case 3 : *(--ptre) = val2; // fallthrough
26667       case 2 : *(--ptre) = val1; // fallthrough
26668       case 1 : *(--ptre) = val0; // fallthrough
26669       }
26670       return *this;
26671     }
26672 
26673     //! Fill sequentially all pixel values with specified values \newinstance.
26674     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26675                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
26676       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26677                                                            val11);
26678     }
26679 
26680     //! Fill sequentially all pixel values with specified values \overloading.
26681     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26682                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26683                   const T& val12) {
26684       if (is_empty()) return *this;
26685       T *ptrd, *ptre = end() - 12;
26686       for (ptrd = _data; ptrd<ptre; ) {
26687         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26688         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26689         *(ptrd++) = val12;
26690       }
26691       ptre+=12;
26692       switch (ptre - ptrd) {
26693       case 12 : *(--ptre) = val11; // fallthrough
26694       case 11 : *(--ptre) = val10; // fallthrough
26695       case 10 : *(--ptre) = val9; // fallthrough
26696       case 9 : *(--ptre) = val8; // fallthrough
26697       case 8 : *(--ptre) = val7; // fallthrough
26698       case 7 : *(--ptre) = val6; // fallthrough
26699       case 6 : *(--ptre) = val5; // fallthrough
26700       case 5 : *(--ptre) = val4; // fallthrough
26701       case 4 : *(--ptre) = val3; // fallthrough
26702       case 3 : *(--ptre) = val2; // fallthrough
26703       case 2 : *(--ptre) = val1; // fallthrough
26704       case 1 : *(--ptre) = val0; // fallthrough
26705       }
26706       return *this;
26707     }
26708 
26709     //! Fill sequentially all pixel values with specified values \newinstance.
26710     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26711                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26712                      const T& val12) const {
26713       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26714                                                            val11,val12);
26715     }
26716 
26717     //! Fill sequentially all pixel values with specified values \overloading.
26718     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26719                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26720                   const T& val12, const T& val13) {
26721       if (is_empty()) return *this;
26722       T *ptrd, *ptre = end() - 13;
26723       for (ptrd = _data; ptrd<ptre; ) {
26724         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26725         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26726         *(ptrd++) = val12; *(ptrd++) = val13;
26727       }
26728       ptre+=13;
26729       switch (ptre - ptrd) {
26730       case 13 : *(--ptre) = val12; // fallthrough
26731       case 12 : *(--ptre) = val11; // fallthrough
26732       case 11 : *(--ptre) = val10; // fallthrough
26733       case 10 : *(--ptre) = val9; // fallthrough
26734       case 9 : *(--ptre) = val8; // fallthrough
26735       case 8 : *(--ptre) = val7; // fallthrough
26736       case 7 : *(--ptre) = val6; // fallthrough
26737       case 6 : *(--ptre) = val5; // fallthrough
26738       case 5 : *(--ptre) = val4; // fallthrough
26739       case 4 : *(--ptre) = val3; // fallthrough
26740       case 3 : *(--ptre) = val2; // fallthrough
26741       case 2 : *(--ptre) = val1; // fallthrough
26742       case 1 : *(--ptre) = val0; // fallthrough
26743       }
26744       return *this;
26745     }
26746 
26747     //! Fill sequentially all pixel values with specified values \newinstance.
26748     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26749                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26750                      const T& val12, const T& val13) const {
26751       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26752                                                            val11,val12,val13);
26753     }
26754 
26755     //! Fill sequentially all pixel values with specified values \overloading.
26756     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26757                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26758                   const T& val12, const T& val13, const T& val14) {
26759       if (is_empty()) return *this;
26760       T *ptrd, *ptre = end() - 14;
26761       for (ptrd = _data; ptrd<ptre; ) {
26762         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26763         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26764         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
26765       }
26766       ptre+=14;
26767       switch (ptre - ptrd) {
26768       case 14 : *(--ptre) = val13; // fallthrough
26769       case 13 : *(--ptre) = val12; // fallthrough
26770       case 12 : *(--ptre) = val11; // fallthrough
26771       case 11 : *(--ptre) = val10; // fallthrough
26772       case 10 : *(--ptre) = val9; // fallthrough
26773       case 9 : *(--ptre) = val8; // fallthrough
26774       case 8 : *(--ptre) = val7; // fallthrough
26775       case 7 : *(--ptre) = val6; // fallthrough
26776       case 6 : *(--ptre) = val5; // fallthrough
26777       case 5 : *(--ptre) = val4; // fallthrough
26778       case 4 : *(--ptre) = val3; // fallthrough
26779       case 3 : *(--ptre) = val2; // fallthrough
26780       case 2 : *(--ptre) = val1; // fallthrough
26781       case 1 : *(--ptre) = val0; // fallthrough
26782       }
26783       return *this;
26784     }
26785 
26786     //! Fill sequentially all pixel values with specified values \newinstance.
26787     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26788                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26789                      const T& val12, const T& val13, const T& val14) const {
26790       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26791                                                            val11,val12,val13,val14);
26792     }
26793 
26794     //! Fill sequentially all pixel values with specified values \overloading.
26795     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26796                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26797                   const T& val12, const T& val13, const T& val14, const T& val15) {
26798       if (is_empty()) return *this;
26799       T *ptrd, *ptre = end() - 15;
26800       for (ptrd = _data; ptrd<ptre; ) {
26801         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26802         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26803         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
26804       }
26805       ptre+=15;
26806       switch (ptre - ptrd) {
26807       case 15 : *(--ptre) = val14; // fallthrough
26808       case 14 : *(--ptre) = val13; // fallthrough
26809       case 13 : *(--ptre) = val12; // fallthrough
26810       case 12 : *(--ptre) = val11; // fallthrough
26811       case 11 : *(--ptre) = val10; // fallthrough
26812       case 10 : *(--ptre) = val9; // fallthrough
26813       case 9 : *(--ptre) = val8; // fallthrough
26814       case 8 : *(--ptre) = val7; // fallthrough
26815       case 7 : *(--ptre) = val6; // fallthrough
26816       case 6 : *(--ptre) = val5; // fallthrough
26817       case 5 : *(--ptre) = val4; // fallthrough
26818       case 4 : *(--ptre) = val3; // fallthrough
26819       case 3 : *(--ptre) = val2; // fallthrough
26820       case 2 : *(--ptre) = val1; // fallthrough
26821       case 1 : *(--ptre) = val0; // fallthrough
26822       }
26823       return *this;
26824     }
26825 
26826     //! Fill sequentially all pixel values with specified values \newinstance.
26827     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26828                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26829                      const T& val12, const T& val13, const T& val14, const T& val15) const {
26830       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26831                                                            val11,val12,val13,val14,val15);
26832     }
26833 
26834     //! Fill sequentially pixel values according to a given expression.
26835     /**
26836        \param expression C-string describing a math formula, or a sequence of values.
26837        \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
26838        \param allow_formula Tells that mathematical formulas are authorized for the filling.
26839        \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
26840        \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
26841     **/
26842     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
26843                   const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
26844       return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0);
26845     }
26846 
26847     CImg<T>& _fill(const char *const expression, const bool repeat_values, bool allow_formula,
26848                    const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
26849                    const char *const calling_function, const CImg<T> *provides_copy) {
26850       if (is_empty() || !expression || !*expression) return *this;
26851       const unsigned int omode = cimg::exception_mode();
26852       cimg::exception_mode(0);
26853       CImg<charT> is_error;
26854       bool is_value_sequence = false;
26855       cimg_abort_init;
26856 
26857       if (allow_formula) {
26858 
26859         // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
26860         double value;
26861         char sep;
26862         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
26863         if (err==1 || (err==2 && sep==',')) {
26864           if (err==1) return fill((T)value);
26865           else is_value_sequence = true;
26866         }
26867 
26868         // Try to fill values according to a formula.
26869         _cimg_abort_init_omp;
26870         if (!is_value_sequence) try {
26871             CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
26872             _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
26873                                                *expression=='*' || *expression==':'),
26874                                  calling_function,base,this,list_inputs,list_outputs,true);
26875             if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
26876                 mp.need_input_copy)
26877               base.assign().assign(*this,false); // Needs input copy
26878 
26879             bool do_in_parallel = false;
26880 #ifdef cimg_use_openmp
26881             cimg_openmp_if(*expression=='*' || *expression==':' ||
26882                            (mp.is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2))
26883               do_in_parallel = true;
26884 #endif
26885             if (mp.result_dim) { // Vector-valued expression
26886               const unsigned int N = std::min(mp.result_dim,_spectrum);
26887               const ulongT whd = (ulongT)_width*_height*_depth;
26888               T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
26889               if (*expression=='<') {
26890                 CImg<doubleT> res(1,mp.result_dim);
26891                 cimg_rofYZ(*this,y,z) {
26892                   cimg_abort_test;
26893                   cimg_rofX(*this,x) {
26894                     mp(x,y,z,0,res._data);
26895                     const double *ptrs = res._data;
26896                     T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26897                   }
26898                 }
26899               } else if (*expression=='>' || !do_in_parallel) {
26900                 CImg<doubleT> res(1,mp.result_dim);
26901                 cimg_forYZ(*this,y,z) {
26902                   cimg_abort_test;
26903                   cimg_forX(*this,x) {
26904                     mp(x,y,z,0,res._data);
26905                     const double *ptrs = res._data;
26906                     T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26907                   }
26908                 }
26909              } else {
26910 #ifdef cimg_use_openmp
26911                 cimg_pragma_openmp(parallel)
26912                 {
26913                   _cimg_math_parser
26914                     _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
26915                     &lmp = omp_get_thread_num()?_mp:mp;
26916                   lmp.is_fill = true;
26917                   cimg_pragma_openmp(for collapse(2))
26918                     cimg_forYZ(*this,y,z) _cimg_abort_try_omp {
26919                     cimg_abort_test;
26920                     CImg<doubleT> res(1,lmp.result_dim);
26921                     T *ptrd = data(0,y,z,0);
26922                     cimg_forX(*this,x) {
26923                       lmp(x,y,z,0,res._data);
26924                       const double *ptrs = res._data;
26925                       T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26926                     }
26927                   } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
26928                 }
26929 #endif
26930               }
26931 
26932             } else { // Scalar-valued expression
26933               T *ptrd = *expression=='<'?end() - 1:_data;
26934               if (*expression=='<')
26935                 cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
26936               else if (*expression=='>' || !do_in_parallel)
26937                 cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
26938               else {
26939 #ifdef cimg_use_openmp
26940                 cimg_pragma_openmp(parallel)
26941                 {
26942                   _cimg_math_parser
26943                     _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
26944                     &lmp = omp_get_thread_num()?_mp:mp;
26945                   lmp.is_fill = true;
26946                   cimg_pragma_openmp(for collapse(3))
26947                     cimg_forYZC(*this,y,z,c) _cimg_abort_try_omp {
26948                     cimg_abort_test;
26949                     T *ptrd = data(0,y,z,c);
26950                     cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c);
26951                   } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
26952                 }
26953 #endif
26954               }
26955             }
26956             mp.end();
26957           } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
26958       }
26959 
26960       // Try to fill values according to a value sequence.
26961       if (!allow_formula || is_value_sequence || is_error) {
26962         CImg<charT> item(256);
26963         char sep = 0;
26964         const char *nexpression = expression;
26965         ulongT nb = 0;
26966         const ulongT siz = size();
26967         T *ptrd = _data;
26968         for (double val = 0; *nexpression && nb<siz; ++nb) {
26969           sep = 0;
26970           const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
26971           if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
26972             nexpression+=std::strlen(item) + (err>1);
26973             *(ptrd++) = (T)val;
26974           } else break;
26975         }
26976         cimg::exception_mode(omode);
26977         if (nb<siz && (sep || *nexpression)) {
26978           if (is_error) throw CImgArgumentException("%s",is_error._data);
26979           else throw CImgArgumentException(_cimg_instance
26980                                            "%s(): Invalid sequence of filling values '%s'.",
26981                                            cimg_instance,calling_function,expression);
26982         }
26983         if (repeat_values && nb && nb<siz)
26984           for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
26985       }
26986 
26987       cimg::exception_mode(omode);
26988       cimg_abort_test;
26989       return *this;
26990     }
26991 
26992     //! Fill sequentially pixel values according to a given expression \newinstance.
26993     CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
26994                      const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
26995       return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs);
26996     }
26997 
26998     //! Fill sequentially pixel values according to the values found in another image.
26999     /**
27000        \param values Image containing the values used for the filling.
27001        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
27002          repeated for the filling.
27003     **/
27004     template<typename t>
27005     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
27006       if (is_empty() || !values) return *this;
27007       T *ptrd = _data, *ptre = ptrd + size();
27008       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
27009         *(ptrd++) = (T)*ptrs;
27010       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
27011       return *this;
27012     }
27013 
27014     //! Fill sequentially pixel values according to the values found in another image \newinstance.
27015     template<typename t>
27016     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
27017       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
27018         (+*this).fill(values,repeat_values);
27019     }
27020 
27021     //! Fill pixel values along the X-axis at a specified pixel position.
27022     /**
27023        \param y Y-coordinate of the filled column.
27024        \param z Z-coordinate of the filled column.
27025        \param c C-coordinate of the filled column.
27026        \param a0 First fill value.
27027     **/
27028     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
27029 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
27030     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
27031     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
27032     va_end(ap); }
27033       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
27034       return *this;
27035     }
27036 
27037     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
27038     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
27039       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
27040       return *this;
27041     }
27042 
27043     //! Fill pixel values along the Y-axis at a specified pixel position.
27044     /**
27045        \param x X-coordinate of the filled row.
27046        \param z Z-coordinate of the filled row.
27047        \param c C-coordinate of the filled row.
27048        \param a0 First fill value.
27049     **/
27050     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
27051       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
27052       return *this;
27053     }
27054 
27055     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
27056     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
27057       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
27058       return *this;
27059     }
27060 
27061     //! Fill pixel values along the Z-axis at a specified pixel position.
27062     /**
27063        \param x X-coordinate of the filled slice.
27064        \param y Y-coordinate of the filled slice.
27065        \param c C-coordinate of the filled slice.
27066        \param a0 First fill value.
27067     **/
27068     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
27069       const ulongT wh = (ulongT)_width*_height;
27070       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
27071       return *this;
27072     }
27073 
27074     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
27075     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
27076       const ulongT wh = (ulongT)_width*_height;
27077       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
27078       return *this;
27079     }
27080 
27081     //! Fill pixel values along the C-axis at a specified pixel position.
27082     /**
27083        \param x X-coordinate of the filled channel.
27084        \param y Y-coordinate of the filled channel.
27085        \param z Z-coordinate of the filled channel.
27086        \param a0 First filling value.
27087     **/
27088     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
27089       const ulongT whd = (ulongT)_width*_height*_depth;
27090       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
27091       return *this;
27092     }
27093 
27094     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
27095     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
27096       const ulongT whd = (ulongT)_width*_height*_depth;
27097       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
27098       return *this;
27099     }
27100 
27101     //! Discard specified sequence of values in the image buffer, along a specific axis.
27102     /**
27103        \param values Sequence of values to discard.
27104        \param axis Axis along which the values are discarded. If set to \c 0 (default value)
27105          the method does it for all the buffer values and returns a one-column vector.
27106        \note Discarded values will change the image geometry, so the resulting image
27107        is returned as a one-column vector.
27108     **/
27109     template<typename t>
27110     CImg<T>& discard(const CImg<t>& values, const char axis=0) {
27111       if (is_empty() || !values) return *this;
27112       return get_discard(values,axis).move_to(*this);
27113     }
27114 
27115     template<typename t>
27116     CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
27117       CImg<T> res;
27118       if (!values) return +*this;
27119       if (is_empty()) return res;
27120       const ulongT vsiz = values.size();
27121       const char _axis = cimg::lowercase(axis);
27122       ulongT j = 0;
27123       unsigned int k = 0;
27124       int i0 = 0;
27125       res.assign(width(),height(),depth(),spectrum());
27126       switch (_axis) {
27127       case 'x' : {
27128         cimg_forX(*this,i) {
27129           if ((*this)(i)!=(T)values[j]) {
27130             if (j) --i;
27131             res.draw_image(k,get_columns(i0,i));
27132             k+=i - i0 + 1; i0 = i + 1; j = 0;
27133           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27134         }
27135         if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
27136         res.resize(k,-100,-100,-100,0);
27137       } break;
27138       case 'y' : {
27139         cimg_forY(*this,i) {
27140           if ((*this)(0,i)!=(T)values[j]) {
27141             if (j) --i;
27142             res.draw_image(0,k,get_rows(i0,i));
27143             k+=i - i0 + 1; i0 = i + 1; j = 0;
27144           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27145         }
27146         if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
27147         res.resize(-100,k,-100,-100,0);
27148       } break;
27149       case 'z' : {
27150         cimg_forZ(*this,i) {
27151           if ((*this)(0,0,i)!=(T)values[j]) {
27152             if (j) --i;
27153             res.draw_image(0,0,k,get_slices(i0,i));
27154             k+=i - i0 + 1; i0 = i + 1; j = 0;
27155           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27156         }
27157         if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
27158         res.resize(-100,-100,k,-100,0);
27159       } break;
27160       case 'c' : {
27161         cimg_forC(*this,i) {
27162           if ((*this)(0,0,0,i)!=(T)values[j]) {
27163             if (j) --i;
27164             res.draw_image(0,0,0,k,get_channels(i0,i));
27165             k+=i - i0 + 1; i0 = i + 1; j = 0;
27166           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27167         }
27168         if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
27169         res.resize(-100,-100,-100,k,0);
27170       } break;
27171       default : {
27172         res.unroll('y');
27173         cimg_foroff(*this,i) {
27174           if ((*this)[i]!=(T)values[j]) {
27175             if (j) --i;
27176             std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
27177             k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
27178           } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
27179         }
27180         const ulongT siz = size();
27181         if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
27182         res.resize(1,k,1,1,0);
27183       }
27184       }
27185       return res;
27186     }
27187 
27188     //! Discard neighboring duplicates in the image buffer, along the specified axis.
27189     CImg<T>& discard(const char axis=0) {
27190       return get_discard(axis).move_to(*this);
27191     }
27192 
27193     //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
27194     CImg<T> get_discard(const char axis=0) const {
27195       CImg<T> res;
27196       if (is_empty()) return res;
27197       const char _axis = cimg::lowercase(axis);
27198       T current = *_data?(T)0:(T)1;
27199       int j = 0;
27200       res.assign(width(),height(),depth(),spectrum());
27201       switch (_axis) {
27202       case 'x' : {
27203         cimg_forX(*this,i)
27204           if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
27205         res.resize(j,-100,-100,-100,0);
27206       } break;
27207       case 'y' : {
27208         cimg_forY(*this,i)
27209           if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
27210         res.resize(-100,j,-100,-100,0);
27211       } break;
27212       case 'z' : {
27213         cimg_forZ(*this,i)
27214           if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
27215         res.resize(-100,-100,j,-100,0);
27216       } break;
27217       case 'c' : {
27218         cimg_forC(*this,i)
27219           if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
27220         res.resize(-100,-100,-100,j,0);
27221       } break;
27222       default : {
27223         res.unroll('y');
27224         cimg_foroff(*this,i)
27225           if ((*this)[i]!=current) res[j++] = current = (*this)[i];
27226         res.resize(-100,j,-100,-100,0);
27227       }
27228       }
27229       return res;
27230     }
27231 
27232     //! Invert endianness of all pixel values.
27233     /**
27234      **/
27235     CImg<T>& invert_endianness() {
27236       cimg::invert_endianness(_data,size());
27237       return *this;
27238     }
27239 
27240     //! Invert endianness of all pixel values \newinstance.
27241     CImg<T> get_invert_endianness() const {
27242       return (+*this).invert_endianness();
27243     }
27244 
27245     //! Fill image with random values in specified range.
27246     /**
27247        \param val_min Minimal authorized random value.
27248        \param val_max Maximal authorized random value.
27249        \note Random variables are uniformely distributed in [val_min,val_max].
27250      **/
27251     CImg<T>& rand(const T& val_min, const T& val_max) {
27252       const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
27253       if (cimg::type<T>::is_float()) cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
27254       else cimg_for(*this,ptrd,T) *ptrd = std::min(val_max,(T)(val_min + cimg::rand()*delta));
27255       return *this;
27256     }
27257 
27258     //! Fill image with random values in specified range \newinstance.
27259     CImg<T> get_rand(const T& val_min, const T& val_max) const {
27260       return (+*this).rand(val_min,val_max);
27261     }
27262 
27263     //! Round pixel values.
27264     /**
27265        \param y Rounding precision.
27266        \param rounding_type Rounding type. Can be:
27267        - \c -1: Backward.
27268        - \c 0: Nearest.
27269        - \c 1: Forward.
27270     **/
27271     CImg<T>& round(const double y=1, const int rounding_type=0) {
27272       if (y>0)
27273         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
27274         cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
27275       return *this;
27276     }
27277 
27278     //! Round pixel values \newinstance.
27279     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
27280       return (+*this).round(y,rounding_type);
27281     }
27282 
27283     //! Add random noise to pixel values.
27284     /**
27285        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
27286          global value range.
27287        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
27288          \p 3=Poisson or \p 4=Rician).
27289        \return A reference to the modified image instance.
27290        \note
27291        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
27292          the image value itself.
27293        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
27294        \par Example
27295        \code
27296        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
27297        (img,res.normalize(0,255)).display();
27298        \endcode
27299        \image html ref_noise.jpg
27300     **/
27301     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
27302       if (is_empty()) return *this;
27303       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
27304       Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
27305       if (nsigma==0 && noise_type!=3) return *this;
27306       if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
27307       if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
27308       switch (noise_type) {
27309       case 0 : { // Gaussian noise
27310         cimg_rof(*this,ptrd,T) {
27311           Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
27312           if (val>vmax) val = vmax;
27313           if (val<vmin) val = vmin;
27314           *ptrd = (T)val;
27315         }
27316       } break;
27317       case 1 : { // Uniform noise
27318         cimg_rof(*this,ptrd,T) {
27319           Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::rand(-1,1));
27320           if (val>vmax) val = vmax;
27321           if (val<vmin) val = vmin;
27322           *ptrd = (T)val;
27323         }
27324       } break;
27325       case 2 : { // Salt & Pepper noise
27326         if (nsigma<0) nsigma = -nsigma;
27327         if (M==m) { m = 0; M = cimg::type<T>::is_float()?(Tfloat)1:(Tfloat)cimg::type<T>::max(); }
27328         cimg_rof(*this,ptrd,T) if (cimg::rand(100)<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
27329       } break;
27330       case 3 : { // Poisson Noise
27331         cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
27332       } break;
27333       case 4 : { // Rice noise
27334         const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
27335         cimg_rof(*this,ptrd,T) {
27336           const Tfloat
27337             val0 = (Tfloat)*ptrd/sqrt2,
27338             re = (Tfloat)(val0 + nsigma*cimg::grand()),
27339             im = (Tfloat)(val0 + nsigma*cimg::grand());
27340           Tfloat val = cimg::hypot(re,im);
27341           if (val>vmax) val = vmax;
27342           if (val<vmin) val = vmin;
27343           *ptrd = (T)val;
27344         }
27345       } break;
27346       default :
27347         throw CImgArgumentException(_cimg_instance
27348                                     "noise(): Invalid specified noise type %d "
27349                                     "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
27350                                     cimg_instance,
27351                                     noise_type);
27352       }
27353       return *this;
27354     }
27355 
27356     //! Add random noise to pixel values \newinstance.
27357     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
27358       return (+*this).noise(sigma,noise_type);
27359     }
27360 
27361     //! Linearly normalize pixel values.
27362     /**
27363        \param min_value Minimum desired value of the resulting image.
27364        \param max_value Maximum desired value of the resulting image.
27365        \par Example
27366        \code
27367        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
27368        (img,res).display();
27369        \endcode
27370        \image html ref_normalize2.jpg
27371     **/
27372     CImg<T>& normalize(const T& min_value, const T& max_value) {
27373       if (is_empty()) return *this;
27374       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
27375       T m, M = max_min(m);
27376       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
27377       if (m==M) return fill(min_value);
27378       if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
27379       return *this;
27380     }
27381 
27382     //! Linearly normalize pixel values \newinstance.
27383     CImg<Tfloat> get_normalize(const T& min_value, const T& max_value) const {
27384       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
27385     }
27386 
27387     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
27388     /**
27389        \par Example
27390        \code
27391        const CImg<float> img("reference.jpg"), res = img.get_normalize();
27392        (img,res.normalize(0,255)).display();
27393        \endcode
27394        \image html ref_normalize.jpg
27395     **/
27396     CImg<T>& normalize() {
27397       const ulongT whd = (ulongT)_width*_height*_depth;
27398       cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27399       cimg_forYZ(*this,y,z) {
27400         T *ptrd = data(0,y,z,0);
27401         cimg_forX(*this,x) {
27402           const T *ptrs = ptrd;
27403           float n = 0;
27404           cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
27405           n = (float)std::sqrt(n);
27406           T *_ptrd = ptrd++;
27407           if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
27408           else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
27409         }
27410       }
27411       return *this;
27412     }
27413 
27414     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
27415     CImg<Tfloat> get_normalize() const {
27416       return CImg<Tfloat>(*this,false).normalize();
27417     }
27418 
27419     //! Compute Lp-norm of each multi-valued pixel of the image instance.
27420     /**
27421        \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
27422        \par Example
27423        \code
27424        const CImg<float> img("reference.jpg"), res = img.get_norm();
27425        (img,res.normalize(0,255)).display();
27426        \endcode
27427        \image html ref_norm.jpg
27428     **/
27429     CImg<T>& norm(const int norm_type=2) {
27430       if (_spectrum==1 && norm_type) return abs();
27431       return get_norm(norm_type).move_to(*this);
27432     }
27433 
27434     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
27435     CImg<Tfloat> get_norm(const int norm_type=2) const {
27436       if (is_empty()) return *this;
27437       if (_spectrum==1 && norm_type) return get_abs();
27438       const ulongT whd = (ulongT)_width*_height*_depth;
27439       CImg<Tfloat> res(_width,_height,_depth);
27440       switch (norm_type) {
27441       case -1 : { // Linf-norm.
27442         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27443         cimg_forYZ(*this,y,z) {
27444           const ulongT off = (ulongT)offset(0,y,z);
27445           const T *ptrs = _data + off;
27446           Tfloat *ptrd = res._data + off;
27447           cimg_forX(*this,x) {
27448             Tfloat n = 0;
27449             const T *_ptrs = ptrs++;
27450             cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
27451             *(ptrd++) = n;
27452           }
27453         }
27454       } break;
27455       case 0 : { // L0-norm.
27456         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27457         cimg_forYZ(*this,y,z) {
27458           const ulongT off = (ulongT)offset(0,y,z);
27459           const T *ptrs = _data + off;
27460           Tfloat *ptrd = res._data + off;
27461           cimg_forX(*this,x) {
27462             unsigned int n = 0;
27463             const T *_ptrs = ptrs++;
27464             cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
27465             *(ptrd++) = (Tfloat)n;
27466           }
27467         }
27468       } break;
27469       case 1 : { // L1-norm.
27470         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27471         cimg_forYZ(*this,y,z) {
27472           const ulongT off = (ulongT)offset(0,y,z);
27473           const T *ptrs = _data + off;
27474           Tfloat *ptrd = res._data + off;
27475           cimg_forX(*this,x) {
27476             Tfloat n = 0;
27477             const T *_ptrs = ptrs++;
27478             cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
27479             *(ptrd++) = n;
27480           }
27481         }
27482       } break;
27483       case 2 : { // L2-norm.
27484         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27485         cimg_forYZ(*this,y,z) {
27486           const ulongT off = (ulongT)offset(0,y,z);
27487           const T *ptrs = _data + off;
27488           Tfloat *ptrd = res._data + off;
27489           cimg_forX(*this,x) {
27490             Tfloat n = 0;
27491             const T *_ptrs = ptrs++;
27492             cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
27493             *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
27494           }
27495         }
27496       } break;
27497       default : { // Linf-norm.
27498         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27499         cimg_forYZ(*this,y,z) {
27500           const ulongT off = (ulongT)offset(0,y,z);
27501           const T *ptrs = _data + off;
27502           Tfloat *ptrd = res._data + off;
27503           cimg_forX(*this,x) {
27504             Tfloat n = 0;
27505             const T *_ptrs = ptrs++;
27506             cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
27507             *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
27508           }
27509         }
27510       }
27511       }
27512       return res;
27513     }
27514 
27515     //! Cut pixel values in specified range.
27516     /**
27517        \param min_value Minimum desired value of the resulting image.
27518        \param max_value Maximum desired value of the resulting image.
27519        \par Example
27520        \code
27521        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
27522        (img,res).display();
27523        \endcode
27524        \image html ref_cut.jpg
27525     **/
27526     CImg<T>& cut(const T& min_value, const T& max_value) {
27527       if (is_empty()) return *this;
27528       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
27529       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27530       cimg_rof(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
27531       return *this;
27532     }
27533 
27534     //! Cut pixel values in specified range \newinstance.
27535     CImg<T> get_cut(const T& min_value, const T& max_value) const {
27536       return (+*this).cut(min_value,max_value);
27537     }
27538 
27539     //! Uniformly quantize pixel values.
27540     /**
27541        \param nb_levels Number of quantization levels.
27542        \param keep_range Tells if resulting values keep the same range as the original ones.
27543        \par Example
27544        \code
27545        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
27546        (img,res).display();
27547        \endcode
27548        \image html ref_quantize.jpg
27549     **/
27550     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
27551       if (!nb_levels)
27552         throw CImgArgumentException(_cimg_instance
27553                                     "quantize(): Invalid quantization request with 0 values.",
27554                                     cimg_instance);
27555 
27556       if (is_empty()) return *this;
27557       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
27558       if (range>0) {
27559         if (keep_range)
27560           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27561           cimg_rof(*this,ptrd,T) {
27562             const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
27563             *ptrd = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
27564           } else
27565           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27566           cimg_rof(*this,ptrd,T) {
27567             const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
27568             *ptrd = (T)std::min(val,nb_levels - 1);
27569           }
27570       }
27571       return *this;
27572     }
27573 
27574     //! Uniformly quantize pixel values \newinstance.
27575     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
27576       return (+*this).quantize(n,keep_range);
27577     }
27578 
27579     //! Threshold pixel values.
27580     /**
27581        \param value Threshold value
27582        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
27583        \param strict_threshold Tells if threshold value is strict.
27584        \par Example
27585        \code
27586        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
27587        (img,res.normalize(0,255)).display();
27588        \endcode
27589        \image html ref_threshold.jpg
27590     **/
27591     CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
27592       if (is_empty()) return *this;
27593       if (strict_threshold) {
27594         if (soft_threshold)
27595           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27596           cimg_rof(*this,ptrd,T) {
27597             const T v = *ptrd;
27598             *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
27599           }
27600         else
27601           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
27602           cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
27603       } else {
27604         if (soft_threshold)
27605           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27606           cimg_rof(*this,ptrd,T) {
27607             const T v = *ptrd;
27608             *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
27609           }
27610         else
27611           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
27612           cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
27613       }
27614       return *this;
27615     }
27616 
27617     //! Threshold pixel values \newinstance.
27618     CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
27619       return (+*this).threshold(value,soft_threshold,strict_threshold);
27620     }
27621 
27622     //! Compute the histogram of pixel values.
27623     /**
27624        \param nb_levels Number of desired histogram levels.
27625        \param min_value Minimum pixel value considered for the histogram computation.
27626          All pixel values lower than \p min_value will not be counted.
27627        \param max_value Maximum pixel value considered for the histogram computation.
27628          All pixel values higher than \p max_value will not be counted.
27629        \note
27630        - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x
27631          in the image I.
27632        - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
27633        \par Example
27634        \code
27635        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
27636        img.display_graph(0,3);
27637        \endcode
27638        \image html ref_histogram.jpg
27639     **/
27640     CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
27641       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
27642     }
27643 
27644     //! Compute the histogram of pixel values \overloading.
27645     CImg<T>& histogram(const unsigned int nb_levels) {
27646       return get_histogram(nb_levels).move_to(*this);
27647     }
27648 
27649     //! Compute the histogram of pixel values \newinstance.
27650     CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
27651       if (!nb_levels || is_empty()) return CImg<ulongT>();
27652       const double
27653         vmin = (double)(min_value<max_value?min_value:max_value),
27654         vmax = (double)(min_value<max_value?max_value:min_value);
27655       CImg<ulongT> res(nb_levels,1,1,1,0);
27656       cimg_rof(*this,ptrs,T) {
27657         const T val = *ptrs;
27658         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
27659       }
27660       return res;
27661     }
27662 
27663     //! Compute the histogram of pixel values \newinstance.
27664     CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
27665       if (!nb_levels || is_empty()) return CImg<ulongT>();
27666       T vmax = 0, vmin = min_max(vmax);
27667       return get_histogram(nb_levels,vmin,vmax);
27668     }
27669 
27670     //! Equalize histogram of pixel values.
27671     /**
27672        \param nb_levels Number of histogram levels used for the equalization.
27673        \param min_value Minimum pixel value considered for the histogram computation.
27674          All pixel values lower than \p min_value will not be counted.
27675        \param max_value Maximum pixel value considered for the histogram computation.
27676          All pixel values higher than \p max_value will not be counted.
27677        \par Example
27678        \code
27679        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
27680        (img,res).display();
27681        \endcode
27682        \image html ref_equalize.jpg
27683     **/
27684     CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
27685       if (!nb_levels || is_empty()) return *this;
27686       const T
27687         vmin = min_value<max_value?min_value:max_value,
27688         vmax = min_value<max_value?max_value:min_value;
27689       CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
27690       ulongT cumul = 0;
27691       cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
27692       if (!cumul) cumul = 1;
27693       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1048576))
27694       cimg_rof(*this,ptrd,T) {
27695         const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin));
27696         if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/cumul);
27697       }
27698       return *this;
27699     }
27700 
27701     //! Equalize histogram of pixel values \overloading.
27702     CImg<T>& equalize(const unsigned int nb_levels) {
27703       if (!nb_levels || is_empty()) return *this;
27704       T vmax = 0, vmin = min_max(vmax);
27705       return equalize(nb_levels,vmin,vmax);
27706     }
27707 
27708     //! Equalize histogram of pixel values \newinstance.
27709     CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
27710       return (+*this).equalize(nblevels,val_min,val_max);
27711     }
27712 
27713     //! Equalize histogram of pixel values \newinstance.
27714     CImg<T> get_equalize(const unsigned int nblevels) const {
27715       return (+*this).equalize(nblevels);
27716     }
27717 
27718     //! Index multi-valued pixels regarding to a specified colormap.
27719     /**
27720        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
27721        \param dithering Level of dithering (0=disable, 1=standard level).
27722        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
27723        \note
27724        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
27725        \par Example
27726        \code
27727        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
27728        const CImg<float> res = img.get_index(colormap,1,true);
27729        (img,res).display();
27730        \endcode
27731        \image html ref_index.jpg
27732     **/
27733     template<typename t>
27734     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
27735       return get_index(colormap,dithering,map_indexes).move_to(*this);
27736     }
27737 
27738     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
27739     template<typename t>
27740     CImg<typename CImg<t>::Tuint>
27741     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
27742       if (colormap._spectrum!=_spectrum)
27743         throw CImgArgumentException(_cimg_instance
27744                                     "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
27745                                     "have incompatible dimensions.",
27746                                     cimg_instance,
27747                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
27748 
27749       typedef typename CImg<t>::Tuint tuint;
27750       if (is_empty()) return CImg<tuint>();
27751       const ulongT
27752         whd = (ulongT)_width*_height*_depth,
27753         pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
27754       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
27755       tuint *ptrd = res._data;
27756       if (dithering>0) { // Dithered versions.
27757         const float ndithering = cimg::cut(dithering,0,1)/16;
27758         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
27759         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
27760         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
27761         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
27762         const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
27763         switch (_spectrum) {
27764         case 1 : { // Optimized for scalars.
27765           cimg_forYZ(*this,y,z) {
27766             if (y<height() - 2) {
27767               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
27768               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
27769             }
27770             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
27771             cimg_forX(*this,x) {
27772               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
27773               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27774               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
27775                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
27776                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27777               }
27778               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
27779               *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
27780               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27781             }
27782             cimg::swap(cache_current,cache_next);
27783           }
27784         } break;
27785         case 2 : { // Optimized for 2d vectors.
27786           tuint *ptrd1 = ptrd + whd;
27787           cimg_forYZ(*this,y,z) {
27788             if (y<height() - 2) {
27789               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
27790               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
27791               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
27792             }
27793             Tfloat
27794               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
27795               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
27796             cimg_forX(*this,x) {
27797               const Tfloat
27798                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
27799                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
27800               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27801               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27802                 const Tfloat
27803                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
27804                   dist = pval0*pval0 + pval1*pval1;
27805                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27806               }
27807               const t *const ptrmin1 = ptrmin0 + pwhd;
27808               const Tfloat
27809                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
27810                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
27811               *ptrs0+=7*err0; *ptrs1+=7*err1;
27812               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
27813               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
27814               *ptrsn0+=err0; *ptrsn1+=err1;
27815               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
27816               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27817             }
27818             cimg::swap(cache_current,cache_next);
27819           }
27820         } break;
27821         case 3 : { // Optimized for 3d vectors (colors).
27822           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
27823           cimg_forYZ(*this,y,z) {
27824             if (y<height() - 2) {
27825               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
27826               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
27827               cimg_forX(*this,x) {
27828                 *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
27829               }
27830             }
27831             Tfloat
27832               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
27833               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
27834             cimg_forX(*this,x) {
27835               const Tfloat
27836                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
27837                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
27838                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
27839               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27840               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
27841                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27842                 const Tfloat
27843                   pval0 = (Tfloat)*(ptrp0++) - val0,
27844                   pval1 = (Tfloat)*(ptrp1++) - val1,
27845                   pval2 = (Tfloat)*(ptrp2++) - val2,
27846                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
27847                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27848               }
27849               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
27850               const Tfloat
27851                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
27852                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
27853                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
27854 
27855               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
27856               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
27857               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
27858               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
27859 
27860               if (map_indexes) {
27861                 *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
27862               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27863             }
27864             cimg::swap(cache_current,cache_next);
27865           }
27866         } break;
27867         default : // Generic version
27868           cimg_forYZ(*this,y,z) {
27869             if (y<height() - 2) {
27870               Tfloat *ptrc = cache_next;
27871               cimg_forC(*this,c) {
27872                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
27873                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
27874                 ptrc+=cwhd;
27875               }
27876             }
27877             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
27878             cimg_forX(*this,x) {
27879               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
27880               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
27881                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
27882                 cimg_forC(*this,c) {
27883                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
27884                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
27885                 }
27886                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
27887               }
27888               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
27889               cimg_forC(*this,c) {
27890                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
27891                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
27892                 _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
27893               }
27894               if (map_indexes) {
27895                 tuint *_ptrd = ptrd++;
27896                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
27897               }
27898               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
27899             }
27900             cimg::swap(cache_current,cache_next);
27901           }
27902         }
27903       } else { // Non-dithered versions
27904         switch (_spectrum) {
27905         case 1 : { // Optimized for scalars.
27906           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27907           cimg_forYZ(*this,y,z) {
27908             tuint *ptrd = res.data(0,y,z);
27909             for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27910               const Tfloat val0 = (Tfloat)*(ptrs0++);
27911               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27912               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
27913                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
27914                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27915               }
27916               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27917             }
27918           }
27919         } break;
27920         case 2 : { // Optimized for 2d vectors.
27921           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27922           cimg_forYZ(*this,y,z) {
27923             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
27924             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27925               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
27926               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27927               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27928                 const Tfloat
27929                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
27930                   dist = pval0*pval0 + pval1*pval1;
27931                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27932               }
27933               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
27934               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27935             }
27936           }
27937         } break;
27938         case 3 : { // Optimized for 3d vectors (colors).
27939           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27940           cimg_forYZ(*this,y,z) {
27941             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
27942             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
27943                    *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27944               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
27945               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27946               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
27947                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27948                 const Tfloat
27949                   pval0 = (Tfloat)*(ptrp0++) - val0,
27950                   pval1 = (Tfloat)*(ptrp1++) - val1,
27951                   pval2 = (Tfloat)*(ptrp2++) - val2,
27952                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
27953                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27954               }
27955               if (map_indexes) {
27956                 *(ptrd++) = (tuint)*ptrmin0;
27957                 *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
27958                 *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
27959               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27960             }
27961           }
27962         } break;
27963         default : // Generic version.
27964           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27965           cimg_forYZ(*this,y,z) {
27966             tuint *ptrd = res.data(0,y,z);
27967             for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
27968               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
27969               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
27970                 Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
27971                 cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
27972                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
27973               }
27974               if (map_indexes) {
27975                 tuint *_ptrd = ptrd++;
27976                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
27977               }
27978               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
27979             }
27980           }
27981         }
27982       }
27983       return res;
27984     }
27985 
27986     //! Map predefined colormap on the scalar (indexed) image instance.
27987     /**
27988        \param colormap Multi-valued colormap used for mapping the indexes.
27989        \param boundary_conditions The border condition type { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
27990        \par Example
27991        \code
27992        const CImg<float> img("reference.jpg"),
27993                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
27994                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
27995                          res = img.get_index(colormap1,0).map(colormap2);
27996        (img,res).display();
27997        \endcode
27998        \image html ref_map.jpg
27999     **/
28000     template<typename t>
28001     CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
28002       return get_map(colormap,boundary_conditions).move_to(*this);
28003     }
28004 
28005     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
28006     template<typename t>
28007     CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
28008       if (_spectrum!=1 && colormap._spectrum!=1)
28009         throw CImgArgumentException(_cimg_instance
28010                                     "map(): Instance and specified colormap (%u,%u,%u,%u,%p) "
28011                                     "have incompatible dimensions.",
28012                                     cimg_instance,
28013                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
28014 
28015       const ulongT
28016         whd = (ulongT)_width*_height*_depth,
28017         cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
28018         cwhd2 = 2*cwhd;
28019       CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
28020       switch (colormap._spectrum) {
28021 
28022       case 1 : { // Optimized for scalars
28023         const T *ptrs = _data;
28024         switch (boundary_conditions) {
28025         case 3 : // Mirror
28026           cimg_for(res,ptrd,t) {
28027             const ulongT ind = ((ulongT)*(ptrs++))%cwhd2;
28028             *ptrd = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
28029           }
28030           break;
28031         case 2 : // Periodic
28032           cimg_for(res,ptrd,t) {
28033             const ulongT ind = (ulongT)*(ptrs++);
28034             *ptrd = colormap[ind%cwhd];
28035           } break;
28036         case 1 : // Neumann
28037           cimg_for(res,ptrd,t) {
28038             const longT ind = (longT)*(ptrs++);
28039             *ptrd = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
28040           } break;
28041         default : // Dirichlet
28042           cimg_for(res,ptrd,t) {
28043             const ulongT ind = (ulongT)*(ptrs++);
28044             *ptrd = ind<cwhd?colormap[ind]:(t)0;
28045           }
28046         }
28047       } break;
28048 
28049       case 2 : { // Optimized for 2d vectors.
28050         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd;
28051         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
28052         switch (boundary_conditions) {
28053         case 3 : // Mirror
28054           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28055             const ulongT
28056               _ind = ((ulongT)*(ptrs++))%cwhd2,
28057               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28058             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28059           }
28060           break;
28061         case 2 : // Periodic
28062           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28063             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28064             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28065           }
28066           break;
28067         case 1 : // Neumann
28068           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28069             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28070             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28071           }
28072           break;
28073         default : // Dirichlet
28074           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28075             const ulongT ind = (ulongT)*(ptrs++);
28076             const bool is_in = ind<cwhd;
28077             *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0;
28078           }
28079         }
28080       } break;
28081 
28082       case 3 : { // Optimized for 3d vectors (colors).
28083         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp1 + cwhd;
28084         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
28085         switch (boundary_conditions) {
28086         case 3 : // Mirror
28087           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28088             const ulongT
28089               _ind = ((ulongT)*(ptrs++))%cwhd2,
28090               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28091             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28092           } break;
28093         case 2 : // Periodic
28094           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28095             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28096             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28097           } break;
28098         case 1 : // Neumann
28099           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28100             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28101             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28102           } break;
28103         default : // Dirichlet
28104           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28105             const ulongT ind = (ulongT)*(ptrs++);
28106             const bool is_in = ind<cwhd;
28107             *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; *(ptrd2++) = is_in?ptrp2[ind]:(t)0;
28108           }
28109         }
28110       } break;
28111 
28112       default : { // Generic version.
28113         t *ptrd = res._data;
28114         switch (boundary_conditions) {
28115         case 3 : // Mirror
28116           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28117             const ulongT
28118               _ind = ((ulongT)*(ptrs++))%cwhd,
28119               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28120             const t *ptrp = colormap._data + ind;
28121             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28122           } break;
28123         case 2 : // Periodic
28124           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28125             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28126             const t *ptrp = colormap._data + ind;
28127             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28128           } break;
28129         case 1 : // Neumann
28130           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28131             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28132             const t *ptrp = colormap._data + ind;
28133             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28134           } break;
28135         default : // Dirichlet
28136           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28137             const ulongT ind = (ulongT)*(ptrs++);
28138             const bool is_in = ind<cwhd;
28139             if (is_in) {
28140               const t *ptrp = colormap._data + ind;
28141               t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28142             } else {
28143               t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = (t)0; _ptrd+=whd; }
28144             }
28145           }
28146         }
28147       }
28148       }
28149       return res;
28150     }
28151 
28152     //! Label connected components.
28153     /**
28154        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
28155        in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
28156        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
28157        \note The algorithm of connected components computation has been primarily done
28158        by A. Meijster, according to the publication:
28159        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
28160        In: Science of Computer Programming 41 (2001), pp. 173--194'.
28161        The submitted code has then been modified to fit CImg coding style and constraints.
28162     **/
28163     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
28164       return get_label(is_high_connectivity,tolerance).move_to(*this);
28165     }
28166 
28167     //! Label connected components \newinstance.
28168     CImg<ulongT> get_label(const bool is_high_connectivity=false,
28169                            const Tfloat tolerance=0) const {
28170       if (is_empty()) return CImg<ulongT>();
28171 
28172       // Create neighborhood tables.
28173       int dx[13], dy[13], dz[13], nb = 0;
28174       dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
28175       dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
28176       if (is_high_connectivity) {
28177         dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
28178         dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
28179       }
28180       if (_depth>1) { // 3d version.
28181         dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
28182         if (is_high_connectivity) {
28183           dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
28184           dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
28185           dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
28186           dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
28187 
28188           dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
28189           dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
28190           dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
28191           dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
28192         }
28193       }
28194       return _label(nb,dx,dy,dz,tolerance);
28195     }
28196 
28197     //! Label connected components \overloading.
28198     /**
28199        \param connectivity_mask Mask of the neighboring pixels.
28200        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
28201     **/
28202     template<typename t>
28203     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
28204       return get_label(connectivity_mask,tolerance).move_to(*this);
28205     }
28206 
28207     //! Label connected components \newinstance.
28208     template<typename t>
28209     CImg<ulongT> get_label(const CImg<t>& connectivity_mask,
28210                            const Tfloat tolerance=0) const {
28211       int nb = 0;
28212       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
28213       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
28214       nb = 0;
28215       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
28216                                                connectivity_mask(x,y,z)) {
28217         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
28218       }
28219       return _label(nb,dx,dy,dz,tolerance);
28220     }
28221 
28222     CImg<ulongT> _label(const unsigned int nb, const int *const dx,
28223                         const int *const dy, const int *const dz,
28224                         const Tfloat tolerance) const {
28225       CImg<ulongT> res(_width,_height,_depth,_spectrum);
28226       cimg_forC(*this,c) {
28227         CImg<ulongT> _res = res.get_shared_channel(c);
28228 
28229         // Init label numbers.
28230         ulongT *ptr = _res.data();
28231         cimg_foroff(_res,p) *(ptr++) = p;
28232 
28233         // For each neighbour-direction, label.
28234         for (unsigned int n = 0; n<nb; ++n) {
28235           const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
28236           if (_dx || _dy || _dz) {
28237             const int
28238               x0 = _dx<0?-_dx:0,
28239               x1 = _dx<0?width():width() - _dx,
28240               y0 = _dy<0?-_dy:0,
28241               y1 = _dy<0?height():height() - _dy,
28242               z0 = _dz<0?-_dz:0,
28243               z1 = _dz<0?depth():depth() - _dz;
28244             const longT
28245               wh = (longT)width()*height(),
28246               whd = (longT)width()*height()*depth(),
28247               offset = _dz*wh + _dy*width() + _dx;
28248             for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
28249               for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
28250                 for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
28251                   if (cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd))<=tolerance) {
28252                     const longT q = p + offset;
28253                     ulongT x, y;
28254                     for (x = (ulongT)(p<q?q:p), y = (ulongT)(p<q?p:q); x!=y && _res[x]!=x; ) {
28255                       x = _res[x]; if (x<y) cimg::swap(x,y);
28256                     }
28257                     if (x!=y) _res[x] = (ulongT)y;
28258                     for (ulongT _p = (ulongT)p; _p!=y; ) {
28259                       const ulongT h = _res[_p];
28260                       _res[_p] = (ulongT)y;
28261                       _p = h;
28262                     }
28263                     for (ulongT _q = (ulongT)q; _q!=y; ) {
28264                       const ulongT h = _res[_q];
28265                       _res[_q] = (ulongT)y;
28266                       _q = h;
28267                     }
28268                   }
28269                 }
28270               }
28271             }
28272           }
28273         }
28274 
28275         // Resolve equivalences.
28276         ulongT counter = 0;
28277         ptr = _res.data();
28278         cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
28279       }
28280       return res;
28281     }
28282 
28283     // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
28284     CImg<T>& _system_strescape() {
28285 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
28286       move_to(list); \
28287       CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
28288       CImgList<T> list;
28289       const T *ptrs = _data;
28290       cimg_for(*this,p,T) switch ((int)*p) {
28291         cimg_system_strescape('\\',"\\\\");
28292         cimg_system_strescape('\"',"\\\"");
28293         cimg_system_strescape('!',"\"\\!\"");
28294         cimg_system_strescape('`',"\\`");
28295         cimg_system_strescape('$',"\\$");
28296       }
28297       if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
28298       return (list>'x').move_to(*this);
28299     }
28300 
28301     //@}
28302     //---------------------------------
28303     //
28304     //! \name Color Base Management
28305     //@{
28306     //---------------------------------
28307 
28308     //! Return colormap \e "default", containing 256 colors entries in RGB.
28309     /**
28310        \return The following \c 256x1x1x3 colormap is returned:
28311        \image html ref_colormap_default.jpg
28312     **/
28313     static const CImg<Tuchar>& default_LUT256() {
28314       static CImg<Tuchar> colormap;
28315       cimg::mutex(8);
28316       if (!colormap) {
28317         colormap.assign(1,256,1,3);
28318         for (unsigned int index = 0, r = 16; r<256; r+=32)
28319           for (unsigned int g = 16; g<256; g+=32)
28320             for (unsigned int b = 32; b<256; b+=64) {
28321               colormap(0,index,0) = (Tuchar)r;
28322               colormap(0,index,1) = (Tuchar)g;
28323               colormap(0,index++,2) = (Tuchar)b;
28324             }
28325       }
28326       cimg::mutex(8,0);
28327       return colormap;
28328     }
28329 
28330     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
28331     /**
28332        \return The following \c 256x1x1x3 colormap is returned:
28333        \image html ref_colormap_hsv.jpg
28334     **/
28335     static const CImg<Tuchar>& HSV_LUT256() {
28336       static CImg<Tuchar> colormap;
28337       cimg::mutex(8);
28338       if (!colormap) {
28339         CImg<Tint> tmp(1,256,1,3,1);
28340         tmp.get_shared_channel(0).sequence(0,359);
28341         colormap = tmp.HSVtoRGB();
28342       }
28343       cimg::mutex(8,0);
28344       return colormap;
28345     }
28346 
28347     //! Return colormap \e "lines", containing 256 colors entries in RGB.
28348     /**
28349        \return The following \c 256x1x1x3 colormap is returned:
28350        \image html ref_colormap_lines.jpg
28351     **/
28352     static const CImg<Tuchar>& lines_LUT256() {
28353       static const unsigned char pal[] = {
28354         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,
28355         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,
28356         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,
28357         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,
28358         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,
28359         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,
28360         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,
28361         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,
28362         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,
28363         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,
28364         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,
28365         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,
28366         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,
28367         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,
28368         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,
28369         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,
28370         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,
28371         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,
28372         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,
28373         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,
28374         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,
28375         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,
28376         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,
28377         23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
28378       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
28379       return colormap;
28380     }
28381 
28382     //! Return colormap \e "hot", containing 256 colors entries in RGB.
28383     /**
28384        \return The following \c 256x1x1x3 colormap is returned:
28385        \image html ref_colormap_hot.jpg
28386     **/
28387     static const CImg<Tuchar>& hot_LUT256() {
28388       static CImg<Tuchar> colormap;
28389       cimg::mutex(8);
28390       if (!colormap) {
28391         colormap.assign(1,4,1,3,(T)0);
28392         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
28393         colormap.resize(1,256,1,3,3);
28394       }
28395       cimg::mutex(8,0);
28396       return colormap;
28397     }
28398 
28399     //! Return colormap \e "cool", containing 256 colors entries in RGB.
28400     /**
28401        \return The following \c 256x1x1x3 colormap is returned:
28402        \image html ref_colormap_cool.jpg
28403     **/
28404     static const CImg<Tuchar>& cool_LUT256() {
28405       static CImg<Tuchar> colormap;
28406       cimg::mutex(8);
28407       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);
28408       cimg::mutex(8,0);
28409       return colormap;
28410     }
28411 
28412     //! Return colormap \e "jet", containing 256 colors entries in RGB.
28413     /**
28414        \return The following \c 256x1x1x3 colormap is returned:
28415        \image html ref_colormap_jet.jpg
28416     **/
28417     static const CImg<Tuchar>& jet_LUT256() {
28418       static CImg<Tuchar> colormap;
28419       cimg::mutex(8);
28420       if (!colormap) {
28421         colormap.assign(1,4,1,3,(T)0);
28422         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
28423         colormap.resize(1,256,1,3,3);
28424       }
28425       cimg::mutex(8,0);
28426       return colormap;
28427     }
28428 
28429     //! Return colormap \e "flag", containing 256 colors entries in RGB.
28430     /**
28431        \return The following \c 256x1x1x3 colormap is returned:
28432        \image html ref_colormap_flag.jpg
28433     **/
28434     static const CImg<Tuchar>& flag_LUT256() {
28435       static CImg<Tuchar> colormap;
28436       cimg::mutex(8);
28437       if (!colormap) {
28438         colormap.assign(1,4,1,3,(T)0);
28439         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
28440         colormap.resize(1,256,1,3,0,2);
28441       }
28442       cimg::mutex(8,0);
28443       return colormap;
28444     }
28445 
28446     //! Return colormap \e "cube", containing 256 colors entries in RGB.
28447     /**
28448        \return The following \c 256x1x1x3 colormap is returned:
28449        \image html ref_colormap_cube.jpg
28450     **/
28451     static const CImg<Tuchar>& cube_LUT256() {
28452       static CImg<Tuchar> colormap;
28453       cimg::mutex(8);
28454       if (!colormap) {
28455         colormap.assign(1,8,1,3,(T)0);
28456         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
28457           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
28458           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
28459         colormap.resize(1,256,1,3,3);
28460       }
28461       cimg::mutex(8,0);
28462       return colormap;
28463     }
28464 
28465     //! Convert pixel values from sRGB to RGB color spaces.
28466     CImg<T>& sRGBtoRGB() {
28467       if (is_empty()) return *this;
28468       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
28469       cimg_rof(*this,ptr,T) {
28470         const Tfloat
28471           sval = (Tfloat)*ptr/255,
28472           val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
28473         *ptr = (T)cimg::cut(val*255,0,255);
28474       }
28475       return *this;
28476     }
28477 
28478     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
28479     CImg<Tfloat> get_sRGBtoRGB() const {
28480       return CImg<Tfloat>(*this,false).sRGBtoRGB();
28481     }
28482 
28483     //! Convert pixel values from RGB to sRGB color spaces.
28484     CImg<T>& RGBtosRGB() {
28485       if (is_empty()) return *this;
28486       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
28487       cimg_rof(*this,ptr,T) {
28488         const Tfloat
28489           val = (Tfloat)*ptr/255,
28490           sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
28491         *ptr = (T)cimg::cut(sval*255,0,255);
28492       }
28493       return *this;
28494     }
28495 
28496     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
28497     CImg<Tfloat> get_RGBtosRGB() const {
28498       return CImg<Tfloat>(*this,false).RGBtosRGB();
28499     }
28500 
28501     //! Convert pixel values from RGB to HSI color spaces.
28502     CImg<T>& RGBtoHSI() {
28503       if (_spectrum!=3)
28504         throw CImgInstanceException(_cimg_instance
28505                                     "RGBtoHSI(): Instance is not a RGB image.",
28506                                     cimg_instance);
28507 
28508       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28509       const ulongT whd = (ulongT)_width*_height*_depth;
28510       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28511       for (ulongT N = 0; N<whd; ++N) {
28512         const Tfloat
28513           R = (Tfloat)p1[N],
28514           G = (Tfloat)p2[N],
28515           B = (Tfloat)p3[N],
28516           theta = (Tfloat)(std::acos(0.5f*((R - G) + (R - B))/
28517                                      std::sqrt(cimg::sqr(R - G) + (R - B)*(G - B)))*180/cimg::PI),
28518           m = cimg::min(R,G,B),
28519           sum = R + G + B;
28520         Tfloat H = 0, S = 0, I = 0;
28521         if (theta>0) H = B<=G?theta:360 - theta;
28522         if (sum>0) S = 1 - 3*m/sum;
28523         I = sum/(3*255);
28524         p1[N] = (T)cimg::cut(H,0,360);
28525         p2[N] = (T)cimg::cut(S,0,1);
28526         p3[N] = (T)cimg::cut(I,0,1);
28527       }
28528       return *this;
28529     }
28530 
28531     //! Convert pixel values from RGB to HSI color spaces \newinstance.
28532     CImg<Tfloat> get_RGBtoHSI() const {
28533       return CImg<Tfloat>(*this,false).RGBtoHSI();
28534     }
28535 
28536     //! Convert pixel values from HSI to RGB color spaces.
28537     CImg<T>& HSItoRGB() {
28538       if (_spectrum!=3)
28539         throw CImgInstanceException(_cimg_instance
28540                                     "HSItoRGB(): Instance is not a HSI image.",
28541                                     cimg_instance);
28542 
28543       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28544       const ulongT whd = (ulongT)_width*_height*_depth;
28545       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28546       for (ulongT N = 0; N<whd; ++N) {
28547         Tfloat
28548           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28549           S = (Tfloat)p2[N],
28550           I = (Tfloat)p3[N],
28551           a = I*(1 - S),
28552           R = 0, G = 0, B = 0;
28553         if (H<120) {
28554           B = a;
28555           R = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28556           G = 3*I - (R + B);
28557         } else if (H<240) {
28558           H-=120;
28559           R = a;
28560           G = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28561           B = 3*I - (R + G);
28562         } else {
28563           H-=240;
28564           G = a;
28565           B = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28566           R = 3*I - (G + B);
28567         }
28568         p1[N] = (T)cimg::cut(R*255,0,255);
28569         p2[N] = (T)cimg::cut(G*255,0,255);
28570         p3[N] = (T)cimg::cut(B*255,0,255);
28571       }
28572       return *this;
28573     }
28574 
28575     //! Convert pixel values from HSI to RGB color spaces \newinstance.
28576     CImg<Tfloat> get_HSItoRGB() const {
28577       return CImg< Tuchar>(*this,false).HSItoRGB();
28578     }
28579 
28580     //! Convert pixel values from RGB to HSL color spaces.
28581     CImg<T>& RGBtoHSL() {
28582       if (_spectrum!=3)
28583         throw CImgInstanceException(_cimg_instance
28584                                     "RGBtoHSL(): Instance is not a RGB image.",
28585                                     cimg_instance);
28586 
28587       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28588       const ulongT whd = (ulongT)_width*_height*_depth;
28589       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28590       for (ulongT N = 0; N<whd; ++N) {
28591         const Tfloat
28592           R = (Tfloat)p1[N],
28593           G = (Tfloat)p2[N],
28594           B = (Tfloat)p3[N],
28595           m = cimg::min(R,G,B),
28596           M = cimg::max(R,G,B),
28597           L = (m + M)/(2*255);
28598         Tfloat H = 0, S = 0;
28599         if (M!=m) {
28600           const Tfloat
28601             f = R==m?G - B:G==m?B - R:R - G,
28602             i = R==m?3:G==m?5:1;
28603           H = i - f/(M - m);
28604           if (H>=6) H-=6;
28605           H*=60;
28606           S = 2*L<=1?(M - m)/(M + m):(M - m)/(2*255 - M - m);
28607         }
28608         p1[N] = (T)cimg::cut(H,0,360);
28609         p2[N] = (T)cimg::cut(S,0,1);
28610         p3[N] = (T)cimg::cut(L,0,1);
28611       }
28612       return *this;
28613     }
28614 
28615     //! Convert pixel values from RGB to HSL color spaces \newinstance.
28616     CImg<Tfloat> get_RGBtoHSL() const {
28617       return CImg<Tfloat>(*this,false).RGBtoHSL();
28618     }
28619 
28620     //! Convert pixel values from HSL to RGB color spaces.
28621     CImg<T>& HSLtoRGB() {
28622       if (_spectrum!=3)
28623         throw CImgInstanceException(_cimg_instance
28624                                     "HSLtoRGB(): Instance is not a HSL image.",
28625                                     cimg_instance);
28626 
28627       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28628       const ulongT whd = (ulongT)_width*_height*_depth;
28629       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28630       for (ulongT N = 0; N<whd; ++N) {
28631         const Tfloat
28632           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28633           S = (Tfloat)p2[N],
28634           L = (Tfloat)p3[N],
28635           q = 2*L<1?L*(1 + S):L + S - L*S,
28636           p = 2*L - q,
28637           h = H/360,
28638           tr = h + (Tfloat)1/3,
28639           tg = h,
28640           tb = h - (Tfloat)1/3,
28641           ntr = tr<0?tr + 1:tr>1?tr - 1:(Tfloat)tr,
28642           ntg = tg<0?tg + 1:tg>1?tg - 1:(Tfloat)tg,
28643           ntb = tb<0?tb + 1:tb>1?tb - 1:(Tfloat)tb,
28644           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,
28645           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,
28646           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;
28647         p1[N] = (T)cimg::cut(255*R,0,255);
28648         p2[N] = (T)cimg::cut(255*G,0,255);
28649         p3[N] = (T)cimg::cut(255*B,0,255);
28650       }
28651       return *this;
28652     }
28653 
28654     //! Convert pixel values from HSL to RGB color spaces \newinstance.
28655     CImg<Tuchar> get_HSLtoRGB() const {
28656       return CImg<Tuchar>(*this,false).HSLtoRGB();
28657     }
28658 
28659     //! Convert pixel values from RGB to HSV color spaces.
28660     CImg<T>& RGBtoHSV() {
28661       if (_spectrum!=3)
28662         throw CImgInstanceException(_cimg_instance
28663                                     "RGBtoHSV(): Instance is not a RGB image.",
28664                                     cimg_instance);
28665 
28666       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28667       const ulongT whd = (ulongT)_width*_height*_depth;
28668       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28669       for (ulongT N = 0; N<whd; ++N) {
28670         const Tfloat
28671           R = (Tfloat)p1[N],
28672           G = (Tfloat)p2[N],
28673           B = (Tfloat)p3[N],
28674           m = cimg::min(R,G,B),
28675           M = cimg::max(R,G,B);
28676         Tfloat H = 0, S = 0;
28677         if (M!=m) {
28678           const Tfloat
28679             f = R==m?G - B:G==m?B - R:R - G,
28680             i = R==m?3:G==m?5:1;
28681           H = i - f/(M - m);
28682           if (H>=6) H-=6;
28683           H*=60;
28684           S = (M - m)/M;
28685         }
28686         p1[N] = (T)cimg::cut(H,0,360);
28687         p2[N] = (T)cimg::cut(S,0,1);
28688         p3[N] = (T)cimg::cut(M/255,0,1);
28689       }
28690       return *this;
28691     }
28692 
28693     //! Convert pixel values from RGB to HSV color spaces \newinstance.
28694     CImg<Tfloat> get_RGBtoHSV() const {
28695       return CImg<Tfloat>(*this,false).RGBtoHSV();
28696     }
28697 
28698     //! Convert pixel values from HSV to RGB color spaces.
28699     CImg<T>& HSVtoRGB() {
28700       if (_spectrum!=3)
28701         throw CImgInstanceException(_cimg_instance
28702                                     "HSVtoRGB(): Instance is not a HSV image.",
28703                                     cimg_instance);
28704 
28705       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28706       const ulongT whd = (ulongT)_width*_height*_depth;
28707       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28708       for (ulongT N = 0; N<whd; ++N) {
28709         Tfloat
28710           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28711           S = (Tfloat)p2[N],
28712           V = (Tfloat)p3[N],
28713           R = 0, G = 0, B = 0;
28714         if (H==0 && S==0) R = G = B = V;
28715         else {
28716           H/=60;
28717           const int i = (int)std::floor(H);
28718           const Tfloat
28719             f = (i&1)?H - i:1 - H + i,
28720             m = V*(1 - S),
28721             n = V*(1 - S*f);
28722           switch (i) {
28723           case 6 :
28724           case 0 : R = V; G = n; B = m; break;
28725           case 1 : R = n; G = V; B = m; break;
28726           case 2 : R = m; G = V; B = n; break;
28727           case 3 : R = m; G = n; B = V; break;
28728           case 4 : R = n; G = m; B = V; break;
28729           case 5 : R = V; G = m; B = n; break;
28730           }
28731         }
28732         p1[N] = (T)cimg::cut(R*255,0,255);
28733         p2[N] = (T)cimg::cut(G*255,0,255);
28734         p3[N] = (T)cimg::cut(B*255,0,255);
28735       }
28736       return *this;
28737     }
28738 
28739     //! Convert pixel values from HSV to RGB color spaces \newinstance.
28740     CImg<Tuchar> get_HSVtoRGB() const {
28741       return CImg<Tuchar>(*this,false).HSVtoRGB();
28742     }
28743 
28744     //! Convert pixel values from RGB to YCbCr color spaces.
28745     CImg<T>& RGBtoYCbCr() {
28746       if (_spectrum!=3)
28747         throw CImgInstanceException(_cimg_instance
28748                                     "RGBtoYCbCr(): Instance is not a RGB image.",
28749                                     cimg_instance);
28750 
28751       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28752       const ulongT whd = (ulongT)_width*_height*_depth;
28753       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
28754       for (ulongT N = 0; N<whd; ++N) {
28755         const Tfloat
28756           R = (Tfloat)p1[N],
28757           G = (Tfloat)p2[N],
28758           B = (Tfloat)p3[N],
28759           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
28760           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
28761           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
28762         p1[N] = (T)cimg::cut(Y,0,255),
28763         p2[N] = (T)cimg::cut(Cb,0,255),
28764         p3[N] = (T)cimg::cut(Cr,0,255);
28765       }
28766       return *this;
28767     }
28768 
28769     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
28770     CImg<Tuchar> get_RGBtoYCbCr() const {
28771       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
28772     }
28773 
28774     //! Convert pixel values from RGB to YCbCr color spaces.
28775     CImg<T>& YCbCrtoRGB() {
28776       if (_spectrum!=3)
28777         throw CImgInstanceException(_cimg_instance
28778                                     "YCbCrtoRGB(): Instance is not a YCbCr image.",
28779                                     cimg_instance);
28780 
28781       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28782       const ulongT whd = (ulongT)_width*_height*_depth;
28783       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
28784       for (ulongT N = 0; N<whd; ++N) {
28785         const Tfloat
28786           Y = (Tfloat)p1[N] - 16,
28787           Cb = (Tfloat)p2[N] - 128,
28788           Cr = (Tfloat)p3[N] - 128,
28789           R = (298*Y + 409*Cr + 128)/256,
28790           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
28791           B = (298*Y + 516*Cb + 128)/256;
28792         p1[N] = (T)cimg::cut(R,0,255),
28793         p2[N] = (T)cimg::cut(G,0,255),
28794         p3[N] = (T)cimg::cut(B,0,255);
28795       }
28796       return *this;
28797     }
28798 
28799     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
28800     CImg<Tuchar> get_YCbCrtoRGB() const {
28801       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
28802     }
28803 
28804     //! Convert pixel values from RGB to YUV color spaces.
28805     CImg<T>& RGBtoYUV() {
28806       if (_spectrum!=3)
28807         throw CImgInstanceException(_cimg_instance
28808                                     "RGBtoYUV(): Instance is not a RGB image.",
28809                                     cimg_instance);
28810 
28811       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28812       const ulongT whd = (ulongT)_width*_height*_depth;
28813       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
28814       for (ulongT N = 0; N<whd; ++N) {
28815         const Tfloat
28816           R = (Tfloat)p1[N]/255,
28817           G = (Tfloat)p2[N]/255,
28818           B = (Tfloat)p3[N]/255,
28819           Y = 0.299f*R + 0.587f*G + 0.114f*B;
28820         p1[N] = (T)Y;
28821         p2[N] = (T)(0.492f*(B - Y));
28822         p3[N] = (T)(0.877*(R - Y));
28823       }
28824       return *this;
28825     }
28826 
28827     //! Convert pixel values from RGB to YUV color spaces \newinstance.
28828     CImg<Tfloat> get_RGBtoYUV() const {
28829       return CImg<Tfloat>(*this,false).RGBtoYUV();
28830     }
28831 
28832     //! Convert pixel values from YUV to RGB color spaces.
28833     CImg<T>& YUVtoRGB() {
28834       if (_spectrum!=3)
28835         throw CImgInstanceException(_cimg_instance
28836                                     "YUVtoRGB(): Instance is not a YUV image.",
28837                                     cimg_instance);
28838 
28839       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28840       const ulongT whd = (ulongT)_width*_height*_depth;
28841       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
28842       for (ulongT N = 0; N<whd; ++N) {
28843         const Tfloat
28844           Y = (Tfloat)p1[N],
28845           U = (Tfloat)p2[N],
28846           V = (Tfloat)p3[N],
28847           R = (Y + 1.140f*V)*255,
28848           G = (Y - 0.395f*U - 0.581f*V)*255,
28849           B = (Y + 2.032f*U)*255;
28850         p1[N] = (T)cimg::cut(R,0,255),
28851         p2[N] = (T)cimg::cut(G,0,255),
28852         p3[N] = (T)cimg::cut(B,0,255);
28853       }
28854       return *this;
28855     }
28856 
28857     //! Convert pixel values from YUV to RGB color spaces \newinstance.
28858     CImg<Tuchar> get_YUVtoRGB() const {
28859       return CImg< Tuchar>(*this,false).YUVtoRGB();
28860     }
28861 
28862     //! Convert pixel values from RGB to CMY color spaces.
28863     CImg<T>& RGBtoCMY() {
28864       if (_spectrum!=3)
28865         throw CImgInstanceException(_cimg_instance
28866                                     "RGBtoCMY(): Instance is not a RGB image.",
28867                                     cimg_instance);
28868 
28869       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28870       const ulongT whd = (ulongT)_width*_height*_depth;
28871       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
28872       for (ulongT N = 0; N<whd; ++N) {
28873         const Tfloat
28874           R = (Tfloat)p1[N],
28875           G = (Tfloat)p2[N],
28876           B = (Tfloat)p3[N],
28877           C = 255 - R,
28878           M = 255 - G,
28879           Y = 255 - B;
28880         p1[N] = (T)cimg::cut(C,0,255),
28881         p2[N] = (T)cimg::cut(M,0,255),
28882         p3[N] = (T)cimg::cut(Y,0,255);
28883       }
28884       return *this;
28885     }
28886 
28887     //! Convert pixel values from RGB to CMY color spaces \newinstance.
28888     CImg<Tuchar> get_RGBtoCMY() const {
28889       return CImg<Tfloat>(*this,false).RGBtoCMY();
28890     }
28891 
28892     //! Convert pixel values from CMY to RGB color spaces.
28893     CImg<T>& CMYtoRGB() {
28894       if (_spectrum!=3)
28895         throw CImgInstanceException(_cimg_instance
28896                                     "CMYtoRGB(): Instance is not a CMY image.",
28897                                     cimg_instance);
28898 
28899       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28900       const ulongT whd = (ulongT)_width*_height*_depth;
28901       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
28902       for (ulongT N = 0; N<whd; ++N) {
28903         const Tfloat
28904           C = (Tfloat)p1[N],
28905           M = (Tfloat)p2[N],
28906           Y = (Tfloat)p3[N],
28907           R = 255 - C,
28908           G = 255 - M,
28909           B = 255 - Y;
28910         p1[N] = (T)cimg::cut(R,0,255),
28911         p2[N] = (T)cimg::cut(G,0,255),
28912         p3[N] = (T)cimg::cut(B,0,255);
28913       }
28914       return *this;
28915     }
28916 
28917     //! Convert pixel values from CMY to RGB color spaces \newinstance.
28918     CImg<Tuchar> get_CMYtoRGB() const {
28919       return CImg<Tuchar>(*this,false).CMYtoRGB();
28920     }
28921 
28922     //! Convert pixel values from CMY to CMYK color spaces.
28923     CImg<T>& CMYtoCMYK() {
28924       return get_CMYtoCMYK().move_to(*this);
28925     }
28926 
28927     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
28928     CImg<Tuchar> get_CMYtoCMYK() const {
28929       if (_spectrum!=3)
28930         throw CImgInstanceException(_cimg_instance
28931                                     "CMYtoCMYK(): Instance is not a CMY image.",
28932                                     cimg_instance);
28933 
28934       CImg<Tfloat> res(_width,_height,_depth,4);
28935       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
28936       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);
28937       const ulongT whd = (ulongT)_width*_height*_depth;
28938       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
28939       for (ulongT N = 0; N<whd; ++N) {
28940         Tfloat
28941 	  C = (Tfloat)ps1[N],
28942 	  M = (Tfloat)ps2[N],
28943 	  Y = (Tfloat)ps3[N],
28944 	  K = cimg::min(C,M,Y);
28945 	if (K>=255) C = M = Y = 0;
28946 	else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
28947         pd1[N] = (Tfloat)cimg::cut(C,0,255),
28948         pd2[N] = (Tfloat)cimg::cut(M,0,255),
28949         pd3[N] = (Tfloat)cimg::cut(Y,0,255),
28950         pd4[N] = (Tfloat)cimg::cut(K,0,255);
28951       }
28952       return res;
28953     }
28954 
28955     //! Convert pixel values from CMYK to CMY color spaces.
28956     CImg<T>& CMYKtoCMY() {
28957       return get_CMYKtoCMY().move_to(*this);
28958     }
28959 
28960     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
28961     CImg<Tfloat> get_CMYKtoCMY() const {
28962       if (_spectrum!=4)
28963         throw CImgInstanceException(_cimg_instance
28964                                     "CMYKtoCMY(): Instance is not a CMYK image.",
28965                                     cimg_instance);
28966 
28967       CImg<Tfloat> res(_width,_height,_depth,3);
28968       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);
28969       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
28970       const ulongT whd = (ulongT)_width*_height*_depth;
28971       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
28972       for (ulongT N = 0; N<whd; ++N) {
28973         const Tfloat
28974 	  C = (Tfloat)ps1[N],
28975 	  M = (Tfloat)ps2[N],
28976 	  Y = (Tfloat)ps3[N],
28977 	  K = (Tfloat)ps4[N],
28978 	  K1 = 1 - K/255,
28979           nC = C*K1 + K,
28980           nM = M*K1 + K,
28981           nY = Y*K1 + K;
28982         pd1[N] = (Tfloat)cimg::cut(nC,0,255),
28983         pd2[N] = (Tfloat)cimg::cut(nM,0,255),
28984         pd3[N] = (Tfloat)cimg::cut(nY,0,255);
28985       }
28986       return res;
28987     }
28988 
28989     //! Convert pixel values from RGB to XYZ color spaces.
28990     /**
28991        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
28992     **/
28993     CImg<T>& RGBtoXYZ(const bool use_D65=true) {
28994       if (_spectrum!=3)
28995         throw CImgInstanceException(_cimg_instance
28996                                     "RGBtoXYZ(): Instance is not a RGB image.",
28997                                     cimg_instance);
28998 
28999       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29000       const ulongT whd = (ulongT)_width*_height*_depth;
29001       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
29002       for (ulongT N = 0; N<whd; ++N) {
29003         const Tfloat
29004           R = (Tfloat)p1[N]/255,
29005           G = (Tfloat)p2[N]/255,
29006           B = (Tfloat)p3[N]/255;
29007         if (use_D65) { // D65
29008           p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
29009           p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
29010           p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
29011         } else { // D50
29012           p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
29013           p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
29014           p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
29015         }
29016       }
29017       return *this;
29018     }
29019 
29020     //! Convert pixel values from RGB to XYZ color spaces \newinstance.
29021     CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
29022       return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
29023     }
29024 
29025     //! Convert pixel values from XYZ to RGB color spaces.
29026     /**
29027        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
29028     **/
29029     CImg<T>& XYZtoRGB(const bool use_D65=true) {
29030       if (_spectrum!=3)
29031         throw CImgInstanceException(_cimg_instance
29032                                     "XYZtoRGB(): Instance is not a XYZ image.",
29033                                     cimg_instance);
29034 
29035       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29036       const ulongT whd = (ulongT)_width*_height*_depth;
29037       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
29038       for (ulongT N = 0; N<whd; ++N) {
29039         const Tfloat
29040           X = (Tfloat)p1[N]*255,
29041           Y = (Tfloat)p2[N]*255,
29042           Z = (Tfloat)p3[N]*255;
29043         if (use_D65) {
29044           p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
29045           p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
29046           p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
29047         } else {
29048           p1[N] = (T)cimg::cut(3.134274799724*X  - 1.617275708956*Y - 0.490724283042*Z,0,255);
29049           p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
29050           p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
29051         }
29052       }
29053       return *this;
29054     }
29055 
29056     //! Convert pixel values from XYZ to RGB color spaces \newinstance.
29057     CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
29058       return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
29059     }
29060 
29061     //! Convert pixel values from XYZ to Lab color spaces.
29062     CImg<T>& XYZtoLab(const bool use_D65=true) {
29063 #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
29064 
29065       if (_spectrum!=3)
29066         throw CImgInstanceException(_cimg_instance
29067                                     "XYZtoLab(): Instance is not a XYZ image.",
29068                                     cimg_instance);
29069       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
29070       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29071       const ulongT whd = (ulongT)_width*_height*_depth;
29072       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
29073       for (ulongT N = 0; N<whd; ++N) {
29074         const Tfloat
29075           X = (Tfloat)(p1[N]/white[0]),
29076           Y = (Tfloat)(p2[N]/white[1]),
29077           Z = (Tfloat)(p3[N]/white[2]),
29078           fX = (Tfloat)_cimg_Labf(X),
29079           fY = (Tfloat)_cimg_Labf(Y),
29080           fZ = (Tfloat)_cimg_Labf(Z);
29081         p1[N] = (T)cimg::cut(116*fY - 16,0,100);
29082         p2[N] = (T)(500*(fX - fY));
29083         p3[N] = (T)(200*(fY - fZ));
29084       }
29085       return *this;
29086     }
29087 
29088     //! Convert pixel values from XYZ to Lab color spaces \newinstance.
29089     CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
29090       return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
29091     }
29092 
29093     //! Convert pixel values from Lab to XYZ color spaces.
29094     CImg<T>& LabtoXYZ(const bool use_D65=true) {
29095       if (_spectrum!=3)
29096         throw CImgInstanceException(_cimg_instance
29097                                     "LabtoXYZ(): Instance is not a Lab image.",
29098                                     cimg_instance);
29099       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
29100       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29101       const ulongT whd = (ulongT)_width*_height*_depth;
29102       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
29103       for (ulongT N = 0; N<whd; ++N) {
29104         const Tfloat
29105           L = (Tfloat)p1[N],
29106           a = (Tfloat)p2[N],
29107           b = (Tfloat)p3[N],
29108           cY = (L + 16)/116,
29109           cZ = cY - b/200,
29110           cX = a/500 + cY,
29111           X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
29112           Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
29113           Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
29114         p1[N] = (T)(X*white[0]);
29115         p2[N] = (T)(Y*white[1]);
29116         p3[N] = (T)(Z*white[2]);
29117       }
29118       return *this;
29119     }
29120 
29121     //! Convert pixel values from Lab to XYZ color spaces \newinstance.
29122     CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
29123       return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
29124     }
29125 
29126     //! Convert pixel values from XYZ to xyY color spaces.
29127     CImg<T>& XYZtoxyY() {
29128       if (_spectrum!=3)
29129         throw CImgInstanceException(_cimg_instance
29130                                     "XYZtoxyY(): Instance is not a XYZ image.",
29131                                     cimg_instance);
29132 
29133       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29134       const ulongT whd = (ulongT)_width*_height*_depth;
29135       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
29136       for (ulongT N = 0; N<whd; ++N) {
29137         const Tfloat
29138           X = (Tfloat)p1[N],
29139           Y = (Tfloat)p2[N],
29140           Z = (Tfloat)p3[N],
29141           sum = X + Y + Z,
29142           nsum = sum>0?sum:1;
29143         p1[N] = (T)(X/nsum);
29144         p2[N] = (T)(Y/nsum);
29145         p3[N] = (T)Y;
29146       }
29147       return *this;
29148     }
29149 
29150     //! Convert pixel values from XYZ to xyY color spaces \newinstance.
29151     CImg<Tfloat> get_XYZtoxyY() const {
29152       return CImg<Tfloat>(*this,false).XYZtoxyY();
29153     }
29154 
29155     //! Convert pixel values from xyY pixels to XYZ color spaces.
29156     CImg<T>& xyYtoXYZ() {
29157       if (_spectrum!=3)
29158         throw CImgInstanceException(_cimg_instance
29159                                     "xyYtoXYZ(): Instance is not a xyY image.",
29160                                     cimg_instance);
29161 
29162       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29163       const ulongT whd = (ulongT)_width*_height*_depth;
29164       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
29165       for (ulongT N = 0; N<whd; ++N) {
29166         const Tfloat
29167          px = (Tfloat)p1[N],
29168          py = (Tfloat)p2[N],
29169          Y = (Tfloat)p3[N],
29170          ny = py>0?py:1;
29171         p1[N] = (T)(px*Y/ny);
29172         p2[N] = (T)Y;
29173         p3[N] = (T)((1 - px - py)*Y/ny);
29174       }
29175       return *this;
29176     }
29177 
29178     //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
29179     CImg<Tfloat> get_xyYtoXYZ() const {
29180       return CImg<Tfloat>(*this,false).xyYtoXYZ();
29181     }
29182 
29183     //! Convert pixel values from RGB to Lab color spaces.
29184     CImg<T>& RGBtoLab(const bool use_D65=true) {
29185       return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
29186     }
29187 
29188     //! Convert pixel values from RGB to Lab color spaces \newinstance.
29189     CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
29190       return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
29191     }
29192 
29193     //! Convert pixel values from Lab to RGB color spaces.
29194     CImg<T>& LabtoRGB(const bool use_D65=true) {
29195       return LabtoXYZ().XYZtoRGB(use_D65);
29196     }
29197 
29198     //! Convert pixel values from Lab to RGB color spaces \newinstance.
29199     CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
29200       return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
29201     }
29202 
29203     //! Convert pixel values from RGB to xyY color spaces.
29204     CImg<T>& RGBtoxyY(const bool use_D65=true) {
29205       return RGBtoXYZ(use_D65).XYZtoxyY();
29206     }
29207 
29208     //! Convert pixel values from RGB to xyY color spaces \newinstance.
29209     CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
29210       return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
29211     }
29212 
29213     //! Convert pixel values from xyY to RGB color spaces.
29214     CImg<T>& xyYtoRGB(const bool use_D65=true) {
29215       return xyYtoXYZ().XYZtoRGB(use_D65);
29216     }
29217 
29218     //! Convert pixel values from xyY to RGB color spaces \newinstance.
29219     CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
29220       return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
29221     }
29222 
29223     //! Convert pixel values from RGB to CMYK color spaces.
29224     CImg<T>& RGBtoCMYK() {
29225       return RGBtoCMY().CMYtoCMYK();
29226     }
29227 
29228     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
29229     CImg<Tfloat> get_RGBtoCMYK() const {
29230       return CImg<Tfloat>(*this,false).RGBtoCMYK();
29231     }
29232 
29233     //! Convert pixel values from CMYK to RGB color spaces.
29234     CImg<T>& CMYKtoRGB() {
29235       return CMYKtoCMY().CMYtoRGB();
29236     }
29237 
29238     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
29239     CImg<Tuchar> get_CMYKtoRGB() const {
29240       return CImg<Tuchar>(*this,false).CMYKtoRGB();
29241     }
29242 
29243     //@}
29244     //------------------------------------------
29245     //
29246     //! \name Geometric / Spatial Manipulation
29247     //@{
29248     //------------------------------------------
29249 
29250     static float _cimg_lanczos(const float x) {
29251       if (x<=-2 || x>=2) return 0;
29252       const float a = (float)cimg::PI*x, b = 0.5f*a;
29253       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
29254     }
29255 
29256     //! Resize image to new dimensions.
29257     /**
29258        \param size_x Number of columns (new size along the X-axis).
29259        \param size_y Number of rows (new size along the Y-axis).
29260        \param size_z Number of slices (new size along the Z-axis).
29261        \param size_c Number of vector-channels (new size along the C-axis).
29262        \param interpolation_type Method of interpolation:
29263        - -1 = no interpolation: raw memory resizing.
29264        - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
29265        - 1 = nearest-neighbor interpolation.
29266        - 2 = moving average interpolation.
29267        - 3 = linear interpolation.
29268        - 4 = grid interpolation.
29269        - 5 = cubic interpolation.
29270        - 6 = lanczos interpolation.
29271        \param boundary_conditions Type of boundary conditions used if necessary.
29272        \param centering_x Set centering type (only if \p interpolation_type=0).
29273        \param centering_y Set centering type (only if \p interpolation_type=0).
29274        \param centering_z Set centering type (only if \p interpolation_type=0).
29275        \param centering_c Set centering type (only if \p interpolation_type=0).
29276        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
29277     **/
29278     CImg<T>& resize(const int size_x, const int size_y=-100,
29279                     const int size_z=-100, const int size_c=-100,
29280                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
29281                     const float centering_x = 0, const float centering_y = 0,
29282                     const float centering_z = 0, const float centering_c = 0) {
29283       if (!size_x || !size_y || !size_z || !size_c) return assign();
29284       const unsigned int
29285         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
29286         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
29287         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
29288         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
29289         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
29290       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
29291       if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
29292       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
29293 	_width = sx; _height = sy; _depth = sz; _spectrum = sc;
29294 	return *this;
29295       }
29296       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
29297                         centering_x,centering_y,centering_z,centering_c).move_to(*this);
29298     }
29299 
29300     //! Resize image to new dimensions \newinstance.
29301     CImg<T> get_resize(const int size_x, const int size_y = -100,
29302                        const int size_z = -100, const int size_c = -100,
29303                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
29304                        const float centering_x = 0, const float centering_y = 0,
29305                        const float centering_z = 0, const float centering_c = 0) const {
29306       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
29307           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
29308         throw CImgArgumentException(_cimg_instance
29309                                     "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
29310                                     cimg_instance,
29311                                     centering_x,centering_y,centering_z,centering_c);
29312 
29313       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
29314       const unsigned int
29315         sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
29316         sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
29317         sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
29318         sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
29319       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
29320       if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
29321       CImg<T> res;
29322       switch (interpolation_type) {
29323 
29324         // Raw resizing.
29325         //
29326       case -1 :
29327         std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
29328         break;
29329 
29330         // No interpolation.
29331         //
29332       case 0 : {
29333         const int
29334           xc = (int)(centering_x*((int)sx - width())),
29335           yc = (int)(centering_y*((int)sy - height())),
29336           zc = (int)(centering_z*((int)sz - depth())),
29337           cc = (int)(centering_c*((int)sc - spectrum()));
29338 
29339         switch (boundary_conditions) {
29340         case 3 : { // Mirror
29341           res.assign(sx,sy,sz,sc);
29342           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
29343           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
29344           cimg_forXYZC(res,x,y,z,c) {
29345             const int
29346               mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
29347               mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
29348             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
29349                                    my<height()?my:h2 - my - 1,
29350                                    mz<depth()?mz:d2 - mz - 1,
29351                                    mc<spectrum()?mc:s2 - mc - 1);
29352           }
29353         } break;
29354         case 2 : { // Periodic
29355           res.assign(sx,sy,sz,sc);
29356           const int
29357             x0 = ((int)xc%width()) - width(),
29358             y0 = ((int)yc%height()) - height(),
29359             z0 = ((int)zc%depth()) - depth(),
29360             c0 = ((int)cc%spectrum()) - spectrum(),
29361             dx = width(), dy = height(), dz = depth(), dc = spectrum();
29362           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
29363           for (int c = c0; c<(int)sc; c+=dc)
29364             for (int z = z0; z<(int)sz; z+=dz)
29365               for (int y = y0; y<(int)sy; y+=dy)
29366                 for (int x = x0; x<(int)sx; x+=dx)
29367                   res.draw_image(x,y,z,c,*this);
29368         } break;
29369         case 1 : { // Neumann
29370           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
29371           CImg<T> sprite;
29372           if (xc>0) {  // X-backward
29373             res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29374             for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
29375           }
29376           if (xc + width()<(int)sx) { // X-forward
29377             res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
29378                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29379             for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
29380           }
29381           if (yc>0) {  // Y-backward
29382             res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29383             for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
29384           }
29385           if (yc + height()<(int)sy) { // Y-forward
29386             res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
29387                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29388             for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
29389           }
29390           if (zc>0) {  // Z-backward
29391             res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
29392             for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
29393           }
29394           if (zc + depth()<(int)sz) { // Z-forward
29395             res.get_crop(0,0,zc  +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29396             for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
29397           }
29398           if (cc>0) {  // C-backward
29399             res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
29400             for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
29401           }
29402           if (cc + spectrum()<(int)sc) { // C-forward
29403             res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
29404             for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
29405           }
29406         } break;
29407         default : // Dirichlet
29408           res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
29409         }
29410         break;
29411       } break;
29412 
29413         // Nearest neighbor interpolation.
29414         //
29415       case 1 : {
29416         res.assign(sx,sy,sz,sc);
29417         CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
29418         const ulongT
29419           wh = (ulongT)_width*_height,
29420           whd = (ulongT)_width*_height*_depth,
29421           sxy = (ulongT)sx*sy,
29422           sxyz = (ulongT)sx*sy*sz;
29423         if (sx==_width) off_x.fill(1);
29424         else {
29425           ulongT *poff_x = off_x._data, curr = 0;
29426           cimg_forX(res,x) {
29427             const ulongT old = curr;
29428             curr = (ulongT)((x + 1.0)*_width/sx);
29429             *(poff_x++) = curr - old;
29430           }
29431         }
29432         if (sy==_height) off_y.fill(_width);
29433         else {
29434           ulongT *poff_y = off_y._data, curr = 0;
29435           cimg_forY(res,y) {
29436             const ulongT old = curr;
29437             curr = (ulongT)((y + 1.0)*_height/sy);
29438             *(poff_y++) = _width*(curr - old);
29439           }
29440           *poff_y = 0;
29441         }
29442         if (sz==_depth) off_z.fill(wh);
29443         else {
29444           ulongT *poff_z = off_z._data, curr = 0;
29445           cimg_forZ(res,z) {
29446             const ulongT old = curr;
29447             curr = (ulongT)((z + 1.0)*_depth/sz);
29448             *(poff_z++) = wh*(curr - old);
29449           }
29450           *poff_z = 0;
29451         }
29452         if (sc==_spectrum) off_c.fill(whd);
29453         else {
29454           ulongT *poff_c = off_c._data, curr = 0;
29455           cimg_forC(res,c) {
29456             const ulongT old = curr;
29457             curr = (ulongT)((c + 1.0)*_spectrum/sc);
29458             *(poff_c++) = whd*(curr - old);
29459           }
29460           *poff_c = 0;
29461         }
29462 
29463         T *ptrd = res._data;
29464         const T* ptrc = _data;
29465         const ulongT *poff_c = off_c._data;
29466         for (unsigned int c = 0; c<sc; ) {
29467           const T *ptrz = ptrc;
29468           const ulongT *poff_z = off_z._data;
29469           for (unsigned int z = 0; z<sz; ) {
29470             const T *ptry = ptrz;
29471             const ulongT *poff_y = off_y._data;
29472             for (unsigned int y = 0; y<sy; ) {
29473               const T *ptrx = ptry;
29474               const ulongT *poff_x = off_x._data;
29475               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
29476               ++y;
29477               ulongT dy = *(poff_y++);
29478               for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
29479               ptry+=dy;
29480             }
29481             ++z;
29482             ulongT dz = *(poff_z++);
29483             for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
29484             ptrz+=dz;
29485           }
29486           ++c;
29487           ulongT dc = *(poff_c++);
29488           for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
29489           ptrc+=dc;
29490         }
29491       } break;
29492 
29493         // Moving average.
29494         //
29495       case 2 : {
29496         bool instance_first = true;
29497         if (sx!=_width) {
29498           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
29499           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
29500             const unsigned int d = std::min(b,c);
29501             a-=d; b-=d; c-=d;
29502             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
29503             if (!b) {
29504               cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width;
29505               ++t;
29506               b = _width;
29507             }
29508             if (!c) { ++s; c = sx; }
29509           }
29510           tmp.move_to(res);
29511           instance_first = false;
29512         }
29513         if (sy!=_height) {
29514           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
29515           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
29516             const unsigned int d = std::min(b,c);
29517             a-=d; b-=d; c-=d;
29518             if (instance_first)
29519               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
29520             else
29521               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
29522             if (!b) {
29523               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height;
29524               ++t;
29525               b = _height;
29526             }
29527             if (!c) { ++s; c = sy; }
29528           }
29529           tmp.move_to(res);
29530           instance_first = false;
29531         }
29532         if (sz!=_depth) {
29533           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
29534           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
29535             const unsigned int d = std::min(b,c);
29536             a-=d; b-=d; c-=d;
29537             if (instance_first)
29538               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
29539             else
29540               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
29541             if (!b) {
29542               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth;
29543               ++t;
29544               b = _depth;
29545             }
29546             if (!c) { ++s; c = sz; }
29547           }
29548           tmp.move_to(res);
29549           instance_first = false;
29550         }
29551         if (sc!=_spectrum) {
29552           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
29553           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
29554             const unsigned int d = std::min(b,c);
29555             a-=d; b-=d; c-=d;
29556             if (instance_first)
29557               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
29558             else
29559               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
29560             if (!b) {
29561               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum;
29562               ++t;
29563               b = _spectrum;
29564             }
29565             if (!c) { ++s; c = sc; }
29566           }
29567           tmp.move_to(res);
29568           instance_first = false;
29569         }
29570       } break;
29571 
29572         // Linear interpolation.
29573         //
29574       case 3 : {
29575         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29576         CImg<doubleT> foff(off._width);
29577         CImg<T> resx, resy, resz, resc;
29578         double curr, old;
29579 
29580         if (sx!=_width) {
29581           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29582           else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29583           else {
29584             const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
29585               (double)_width/sx;
29586             resx.assign(sx,_height,_depth,_spectrum);
29587             curr = old = 0;
29588             unsigned int *poff = off._data;
29589             double *pfoff = foff._data;
29590             cimg_forX(resx,x) {
29591               *(pfoff++) = curr - (unsigned int)curr;
29592               old = curr;
29593               curr = std::min(width() - 1.0,curr + fx);
29594               *(poff++) = (unsigned int)curr - (unsigned int)old;
29595             }
29596             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
29597               cimg_forYZC(resx,y,z,c) {
29598               const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
29599               T *ptrd = resx.data(0,y,z,c);
29600               const unsigned int *poff = off._data;
29601               const double *pfoff = foff._data;
29602               cimg_forX(resx,x) {
29603                 const double alpha = *(pfoff++);
29604                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
29605                 *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
29606                 ptrs+=*(poff++);
29607               }
29608             }
29609           }
29610         } else resx.assign(*this,true);
29611 
29612         if (sy!=_height) {
29613           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29614           else {
29615             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
29616             else {
29617               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
29618                 (double)_height/sy;
29619               resy.assign(sx,sy,_depth,_spectrum);
29620               curr = old = 0;
29621               unsigned int *poff = off._data;
29622               double *pfoff = foff._data;
29623               cimg_forY(resy,y) {
29624                 *(pfoff++) = curr - (unsigned int)curr;
29625                 old = curr;
29626                 curr = std::min(height() - 1.0,curr + fy);
29627                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
29628               }
29629               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
29630               cimg_forXZC(resy,x,z,c) {
29631                 const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
29632                 T *ptrd = resy.data(x,0,z,c);
29633                 const unsigned int *poff = off._data;
29634                 const double *pfoff = foff._data;
29635                 cimg_forY(resy,y) {
29636                   const double alpha = *(pfoff++);
29637                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
29638                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29639                   ptrd+=sx;
29640                   ptrs+=*(poff++);
29641                 }
29642               }
29643             }
29644           }
29645           resx.assign();
29646         } else resy.assign(resx,true);
29647 
29648         if (sz!=_depth) {
29649           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29650           else {
29651             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
29652             else {
29653               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
29654                 (double)_depth/sz;
29655               const unsigned int sxy = sx*sy;
29656               resz.assign(sx,sy,sz,_spectrum);
29657               curr = old = 0;
29658               unsigned int *poff = off._data;
29659               double *pfoff = foff._data;
29660               cimg_forZ(resz,z) {
29661                 *(pfoff++) = curr - (unsigned int)curr;
29662                 old = curr;
29663                 curr = std::min(depth() - 1.0,curr + fz);
29664                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
29665               }
29666               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
29667               cimg_forXYC(resz,x,y,c) {
29668                 const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
29669                 T *ptrd = resz.data(x,y,0,c);
29670                 const unsigned int *poff = off._data;
29671                 const double *pfoff = foff._data;
29672                 cimg_forZ(resz,z) {
29673                   const double alpha = *(pfoff++);
29674                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
29675                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29676                   ptrd+=sxy;
29677                   ptrs+=*(poff++);
29678                 }
29679               }
29680             }
29681           }
29682           resy.assign();
29683         } else resz.assign(resy,true);
29684 
29685         if (sc!=_spectrum) {
29686           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29687           else {
29688             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
29689             else {
29690               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
29691                 (double)_spectrum/sc;
29692               const unsigned int sxyz = sx*sy*sz;
29693               resc.assign(sx,sy,sz,sc);
29694               curr = old = 0;
29695               unsigned int *poff = off._data;
29696               double *pfoff = foff._data;
29697               cimg_forC(resc,c) {
29698                 *(pfoff++) = curr - (unsigned int)curr;
29699                 old = curr;
29700                 curr = std::min(spectrum() - 1.0,curr + fc);
29701                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
29702               }
29703               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
29704               cimg_forXYZ(resc,x,y,z) {
29705                 const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
29706                 T *ptrd = resc.data(x,y,z,0);
29707                 const unsigned int *poff = off._data;
29708                 const double *pfoff = foff._data;
29709                 cimg_forC(resc,c) {
29710                   const double alpha = *(pfoff++);
29711                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
29712                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29713                   ptrd+=sxyz;
29714                   ptrs+=*(poff++);
29715                 }
29716               }
29717             }
29718           }
29719           resz.assign();
29720         } else resc.assign(resz,true);
29721         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29722       } break;
29723 
29724         // Grid interpolation.
29725         //
29726       case 4 : {
29727         CImg<T> resx, resy, resz, resc;
29728         if (sx!=_width) {
29729           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29730           else {
29731             resx.assign(sx,_height,_depth,_spectrum,(T)0);
29732             const int dx = (int)(2*sx), dy = 2*width();
29733             int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
29734             cimg_forX(resx,x) if ((err-=dy)<=0) {
29735               cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
29736               ++xs;
29737               err+=dx;
29738             }
29739           }
29740         } else resx.assign(*this,true);
29741 
29742         if (sy!=_height) {
29743           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29744           else {
29745             resy.assign(sx,sy,_depth,_spectrum,(T)0);
29746             const int dx = (int)(2*sy), dy = 2*height();
29747             int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
29748             cimg_forY(resy,y) if ((err-=dy)<=0) {
29749               cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
29750               ++ys;
29751               err+=dx;
29752             }
29753           }
29754           resx.assign();
29755         } else resy.assign(resx,true);
29756 
29757         if (sz!=_depth) {
29758           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29759           else {
29760             resz.assign(sx,sy,sz,_spectrum,(T)0);
29761             const int dx = (int)(2*sz), dy = 2*depth();
29762             int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
29763             cimg_forZ(resz,z) if ((err-=dy)<=0) {
29764               cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
29765               ++zs;
29766               err+=dx;
29767             }
29768           }
29769           resy.assign();
29770         } else resz.assign(resy,true);
29771 
29772         if (sc!=_spectrum) {
29773           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29774           else {
29775             resc.assign(sx,sy,sz,sc,(T)0);
29776             const int dx = (int)(2*sc), dy = 2*spectrum();
29777             int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
29778             cimg_forC(resc,c) if ((err-=dy)<=0) {
29779               cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
29780               ++cs;
29781               err+=dx;
29782             }
29783           }
29784           resz.assign();
29785         } else resc.assign(resz,true);
29786 
29787         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29788       } break;
29789 
29790         // Cubic interpolation.
29791         //
29792       case 5 : {
29793         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
29794         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29795         CImg<doubleT> foff(off._width);
29796         CImg<T> resx, resy, resz, resc;
29797         double curr, old;
29798 
29799         if (sx!=_width) {
29800           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29801           else {
29802             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29803             else {
29804               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
29805                 (double)_width/sx;
29806               resx.assign(sx,_height,_depth,_spectrum);
29807               curr = old = 0;
29808               unsigned int *poff = off._data;
29809               double *pfoff = foff._data;
29810               cimg_forX(resx,x) {
29811                 *(pfoff++) = curr - (unsigned int)curr;
29812                 old = curr;
29813                 curr = std::min(width() - 1.0,curr + fx);
29814                 *(poff++) = (unsigned int)curr - (unsigned int)old;
29815               }
29816               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
29817               cimg_forYZC(resx,y,z,c) {
29818                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
29819                 T *ptrd = resx.data(0,y,z,c);
29820                 const unsigned int *poff = off._data;
29821                 const double *pfoff = foff._data;
29822                 cimg_forX(resx,x) {
29823                   const double
29824                     t = *(pfoff++),
29825                     val1 = (double)*ptrs,
29826                     val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
29827                     val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
29828                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
29829                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29830                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29831                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
29832                   ptrs+=*(poff++);
29833                 }
29834               }
29835             }
29836           }
29837         } else resx.assign(*this,true);
29838 
29839         if (sy!=_height) {
29840           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29841           else {
29842             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
29843             else {
29844               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
29845                 (double)_height/sy;
29846               resy.assign(sx,sy,_depth,_spectrum);
29847               curr = old = 0;
29848               unsigned int *poff = off._data;
29849               double *pfoff = foff._data;
29850               cimg_forY(resy,y) {
29851                 *(pfoff++) = curr - (unsigned int)curr;
29852                 old = curr;
29853                 curr = std::min(height() - 1.0,curr + fy);
29854                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
29855               }
29856               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
29857               cimg_forXZC(resy,x,z,c) {
29858                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
29859                 T *ptrd = resy.data(x,0,z,c);
29860                 const unsigned int *poff = off._data;
29861                 const double *pfoff = foff._data;
29862                 cimg_forY(resy,y) {
29863                   const double
29864                     t = *(pfoff++),
29865                     val1 = (double)*ptrs,
29866                     val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
29867                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
29868                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
29869                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29870                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29871                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29872                   ptrd+=sx;
29873                   ptrs+=*(poff++);
29874                 }
29875               }
29876             }
29877           }
29878           resx.assign();
29879         } else resy.assign(resx,true);
29880 
29881         if (sz!=_depth) {
29882           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29883           else {
29884             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
29885             else {
29886               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
29887                 (double)_depth/sz;
29888               const unsigned int sxy = sx*sy;
29889               resz.assign(sx,sy,sz,_spectrum);
29890               curr = old = 0;
29891               unsigned int *poff = off._data;
29892               double *pfoff = foff._data;
29893               cimg_forZ(resz,z) {
29894                 *(pfoff++) = curr - (unsigned int)curr;
29895                 old = curr;
29896                 curr = std::min(depth() - 1.0,curr + fz);
29897                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
29898               }
29899               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
29900               cimg_forXYC(resz,x,y,c) {
29901                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
29902                 T *ptrd = resz.data(x,y,0,c);
29903                 const unsigned int *poff = off._data;
29904                 const double *pfoff = foff._data;
29905                 cimg_forZ(resz,z) {
29906                   const double
29907                     t = *(pfoff++),
29908                     val1 = (double)*ptrs,
29909                     val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
29910                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
29911                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
29912                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29913                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29914                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29915                   ptrd+=sxy;
29916                   ptrs+=*(poff++);
29917                 }
29918               }
29919             }
29920           }
29921           resy.assign();
29922         } else resz.assign(resy,true);
29923 
29924         if (sc!=_spectrum) {
29925           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29926           else {
29927             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
29928             else {
29929               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
29930                 (double)_spectrum/sc;
29931               const unsigned int sxyz = sx*sy*sz;
29932               resc.assign(sx,sy,sz,sc);
29933               curr = old = 0;
29934               unsigned int *poff = off._data;
29935               double *pfoff = foff._data;
29936               cimg_forC(resc,c) {
29937                 *(pfoff++) = curr - (unsigned int)curr;
29938                 old = curr;
29939                 curr = std::min(spectrum() - 1.0,curr + fc);
29940                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
29941               }
29942               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
29943               cimg_forXYZ(resc,x,y,z) {
29944                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
29945                 T *ptrd = resc.data(x,y,z,0);
29946                 const unsigned int *poff = off._data;
29947                 const double *pfoff = foff._data;
29948                 cimg_forC(resc,c) {
29949                   const double
29950                     t = *(pfoff++),
29951                     val1 = (double)*ptrs,
29952                     val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
29953                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
29954                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
29955                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29956                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29957                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29958                   ptrd+=sxyz;
29959                   ptrs+=*(poff++);
29960                 }
29961               }
29962             }
29963           }
29964           resz.assign();
29965         } else resc.assign(resz,true);
29966 
29967         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29968       } break;
29969 
29970         // Lanczos interpolation.
29971         //
29972       case 6 : {
29973         const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
29974         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29975         CImg<doubleT> foff(off._width);
29976         CImg<T> resx, resy, resz, resc;
29977         double curr, old;
29978 
29979         if (sx!=_width) {
29980           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29981           else {
29982             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29983             else {
29984               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
29985                 (double)_width/sx;
29986               resx.assign(sx,_height,_depth,_spectrum);
29987               curr = old = 0;
29988               unsigned int *poff = off._data;
29989               double *pfoff = foff._data;
29990               cimg_forX(resx,x) {
29991                 *(pfoff++) = curr - (unsigned int)curr;
29992                 old = curr;
29993                 curr = std::min(width() - 1.0,curr + fx);
29994                 *(poff++) = (unsigned int)curr - (unsigned int)old;
29995               }
29996               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
29997               cimg_forYZC(resx,y,z,c) {
29998                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
29999                   *const ptrsmax = ptrs0 + (_width - 2);
30000                 T *ptrd = resx.data(0,y,z,c);
30001                 const unsigned int *poff = off._data;
30002                 const double *pfoff = foff._data;
30003                 cimg_forX(resx,x) {
30004                   const double
30005                     t = *(pfoff++),
30006                     w0 = _cimg_lanczos(t + 2),
30007                     w1 = _cimg_lanczos(t + 1),
30008                     w2 = _cimg_lanczos(t),
30009                     w3 = _cimg_lanczos(t - 1),
30010                     w4 = _cimg_lanczos(t - 2),
30011                     val2 = (double)*ptrs,
30012                     val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
30013                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
30014                     val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
30015                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
30016                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30017                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
30018                   ptrs+=*(poff++);
30019                 }
30020               }
30021             }
30022           }
30023         } else resx.assign(*this,true);
30024 
30025         if (sy!=_height) {
30026           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
30027           else {
30028             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
30029             else {
30030               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
30031                 (double)_height/sy;
30032               resy.assign(sx,sy,_depth,_spectrum);
30033               curr = old = 0;
30034               unsigned int *poff = off._data;
30035               double *pfoff = foff._data;
30036               cimg_forY(resy,y) {
30037                 *(pfoff++) = curr - (unsigned int)curr;
30038                 old = curr;
30039                 curr = std::min(height() - 1.0,curr + fy);
30040                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
30041               }
30042               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
30043               cimg_forXZC(resy,x,z,c) {
30044                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
30045                   *const ptrsmax = ptrs0 + (_height - 2)*sx;
30046                 T *ptrd = resy.data(x,0,z,c);
30047                 const unsigned int *poff = off._data;
30048                 const double *pfoff = foff._data;
30049                 cimg_forY(resy,y) {
30050                   const double
30051                     t = *(pfoff++),
30052                     w0 = _cimg_lanczos(t + 2),
30053                     w1 = _cimg_lanczos(t + 1),
30054                     w2 = _cimg_lanczos(t),
30055                     w3 = _cimg_lanczos(t - 1),
30056                     w4 = _cimg_lanczos(t - 2),
30057                     val2 = (double)*ptrs,
30058                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
30059                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
30060                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
30061                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
30062                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30063                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30064                   ptrd+=sx;
30065                   ptrs+=*(poff++);
30066                 }
30067               }
30068             }
30069           }
30070           resx.assign();
30071         } else resy.assign(resx,true);
30072 
30073         if (sz!=_depth) {
30074           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
30075           else {
30076             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
30077             else {
30078               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
30079                 (double)_depth/sz;
30080               const unsigned int sxy = sx*sy;
30081               resz.assign(sx,sy,sz,_spectrum);
30082               curr = old = 0;
30083               unsigned int *poff = off._data;
30084               double *pfoff = foff._data;
30085               cimg_forZ(resz,z) {
30086                 *(pfoff++) = curr - (unsigned int)curr;
30087                 old = curr;
30088                 curr = std::min(depth() - 1.0,curr + fz);
30089                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
30090               }
30091               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
30092               cimg_forXYC(resz,x,y,c) {
30093                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
30094                   *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
30095                 T *ptrd = resz.data(x,y,0,c);
30096                 const unsigned int *poff = off._data;
30097                 const double *pfoff = foff._data;
30098                 cimg_forZ(resz,z) {
30099                   const double
30100                     t = *(pfoff++),
30101                     w0 = _cimg_lanczos(t + 2),
30102                     w1 = _cimg_lanczos(t + 1),
30103                     w2 = _cimg_lanczos(t),
30104                     w3 = _cimg_lanczos(t - 1),
30105                     w4 = _cimg_lanczos(t - 2),
30106                     val2 = (double)*ptrs,
30107                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
30108                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
30109                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
30110                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
30111                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30112                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30113                   ptrd+=sxy;
30114                   ptrs+=*(poff++);
30115                 }
30116               }
30117             }
30118           }
30119           resy.assign();
30120         } else resz.assign(resy,true);
30121 
30122         if (sc!=_spectrum) {
30123           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
30124           else {
30125             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
30126             else {
30127               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
30128                 (double)_spectrum/sc;
30129               const unsigned int sxyz = sx*sy*sz;
30130               resc.assign(sx,sy,sz,sc);
30131               curr = old = 0;
30132               unsigned int *poff = off._data;
30133               double *pfoff = foff._data;
30134               cimg_forC(resc,c) {
30135                 *(pfoff++) = curr - (unsigned int)curr;
30136                 old = curr;
30137                 curr = std::min(spectrum() - 1.0,curr + fc);
30138                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
30139               }
30140               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
30141               cimg_forXYZ(resc,x,y,z) {
30142                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
30143                   *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
30144                 T *ptrd = resc.data(x,y,z,0);
30145                 const unsigned int *poff = off._data;
30146                 const double *pfoff = foff._data;
30147                 cimg_forC(resc,c) {
30148                   const double
30149                     t = *(pfoff++),
30150                     w0 = _cimg_lanczos(t + 2),
30151                     w1 = _cimg_lanczos(t + 1),
30152                     w2 = _cimg_lanczos(t),
30153                     w3 = _cimg_lanczos(t - 1),
30154                     w4 = _cimg_lanczos(t - 2),
30155                     val2 = (double)*ptrs,
30156                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
30157                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
30158                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
30159                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
30160                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30161                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30162                   ptrd+=sxyz;
30163                   ptrs+=*(poff++);
30164                 }
30165               }
30166             }
30167           }
30168           resz.assign();
30169         } else resc.assign(resz,true);
30170 
30171         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
30172       } break;
30173 
30174         // Unknow interpolation.
30175         //
30176       default :
30177         throw CImgArgumentException(_cimg_instance
30178                                     "resize(): Invalid specified interpolation %d "
30179                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
30180                                     "5=cubic | 6=lanczos }).",
30181                                     cimg_instance,
30182                                     interpolation_type);
30183       }
30184       return res;
30185     }
30186 
30187     //! Resize image to dimensions of another image.
30188     /**
30189        \param src Reference image used for dimensions.
30190        \param interpolation_type Interpolation method.
30191        \param boundary_conditions Boundary conditions.
30192        \param centering_x Set centering type (only if \p interpolation_type=0).
30193        \param centering_y Set centering type (only if \p interpolation_type=0).
30194        \param centering_z Set centering type (only if \p interpolation_type=0).
30195        \param centering_c Set centering type (only if \p interpolation_type=0).
30196      **/
30197     template<typename t>
30198     CImg<T>& resize(const CImg<t>& src,
30199                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
30200                     const float centering_x = 0, const float centering_y = 0,
30201                     const float centering_z = 0, const float centering_c = 0) {
30202       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
30203                     centering_x,centering_y,centering_z,centering_c);
30204     }
30205 
30206     //! Resize image to dimensions of another image \newinstance.
30207     template<typename t>
30208     CImg<T> get_resize(const CImg<t>& src,
30209                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
30210                        const float centering_x = 0, const float centering_y = 0,
30211                        const float centering_z = 0, const float centering_c = 0) const {
30212       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
30213                         centering_x,centering_y,centering_z,centering_c);
30214     }
30215 
30216     //! Resize image to dimensions of a display window.
30217     /**
30218        \param disp Reference display window used for dimensions.
30219        \param interpolation_type Interpolation method.
30220        \param boundary_conditions Boundary conditions.
30221        \param centering_x Set centering type (only if \p interpolation_type=0).
30222        \param centering_y Set centering type (only if \p interpolation_type=0).
30223        \param centering_z Set centering type (only if \p interpolation_type=0).
30224        \param centering_c Set centering type (only if \p interpolation_type=0).
30225      **/
30226     CImg<T>& resize(const CImgDisplay& disp,
30227                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
30228                     const float centering_x = 0, const float centering_y = 0,
30229                     const float centering_z = 0, const float centering_c = 0) {
30230       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
30231                     centering_x,centering_y,centering_z,centering_c);
30232     }
30233 
30234     //! Resize image to dimensions of a display window \newinstance.
30235     CImg<T> get_resize(const CImgDisplay& disp,
30236                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
30237                        const float centering_x = 0, const float centering_y = 0,
30238                        const float centering_z = 0, const float centering_c = 0) const {
30239       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
30240                         centering_x,centering_y,centering_z,centering_c);
30241     }
30242 
30243     //! Resize image to half-size along XY axes, using an optimized filter.
30244     CImg<T>& resize_halfXY() {
30245       return get_resize_halfXY().move_to(*this);
30246     }
30247 
30248     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
30249     CImg<T> get_resize_halfXY() const {
30250       if (is_empty()) return *this;
30251       static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
30252                                         0.1231940459f,  0.1935127547f, 0.1231940459f,
30253                                         0.07842776544f, 0.1231940459f, 0.07842776544f };
30254       CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
30255       T *ptrd = res._data;
30256       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
30257         if (x%2 && y%2) *(ptrd++) = (T)
30258                           (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
30259                            I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
30260                            I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
30261       return res;
30262     }
30263 
30264     //! Resize image to double-size, using the Scale2X algorithm.
30265     /**
30266        \note Use anisotropic upscaling algorithm
30267        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
30268     **/
30269     CImg<T>& resize_doubleXY() {
30270       return get_resize_doubleXY().move_to(*this);
30271     }
30272 
30273     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
30274     CImg<T> get_resize_doubleXY() const {
30275 #define _cimg_gs2x_for3(bound,i) \
30276  for (int i = 0, _p1##i = 0, \
30277       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
30278       _n1##i<(int)(bound) || i==--_n1##i; \
30279       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
30280 
30281 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
30282   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
30283    _p1##x = 0, \
30284    _n1##x = (int)( \
30285    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
30286    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
30287    (I[7] = (T)(img)(0,_n1##y,z,c)),	\
30288    1>=(img)._width?(img).width() - 1:1); \
30289    (_n1##x<(img).width() && ( \
30290    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
30291    (I[5] = (T)(img)(_n1##x,y,z,c)), \
30292    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
30293    x==--_n1##x; \
30294    I[1] = I[2], \
30295    I[3] = I[4], I[4] = I[5], \
30296    I[7] = I[8], \
30297    _p1##x = x++, ++_n1##x)
30298 
30299       if (is_empty()) return *this;
30300       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
30301       CImg_3x3(I,T);
30302       cimg_forZC(*this,z,c) {
30303         T
30304           *ptrd1 = res.data(0,0,z,c),
30305           *ptrd2 = ptrd1 + res._width;
30306         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
30307           if (Icp!=Icn && Ipc!=Inc) {
30308             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
30309             *(ptrd1++) = Icp==Inc?Inc:Icc;
30310             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
30311             *(ptrd2++) = Icn==Inc?Inc:Icc;
30312           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
30313         }
30314       }
30315       return res;
30316     }
30317 
30318     //! Resize image to triple-size, using the Scale3X algorithm.
30319     /**
30320        \note Use anisotropic upscaling algorithm
30321        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
30322     **/
30323     CImg<T>& resize_tripleXY() {
30324       return get_resize_tripleXY().move_to(*this);
30325     }
30326 
30327     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
30328     CImg<T> get_resize_tripleXY() const {
30329 #define _cimg_gs3x_for3(bound,i) \
30330  for (int i = 0, _p1##i = 0, \
30331       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
30332       _n1##i<(int)(bound) || i==--_n1##i; \
30333       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
30334 
30335 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
30336   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
30337    _p1##x = 0, \
30338    _n1##x = (int)( \
30339    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
30340    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
30341    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),	\
30342    1>=(img)._width?(img).width() - 1:1); \
30343    (_n1##x<(img).width() && ( \
30344    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
30345    (I[5] = (T)(img)(_n1##x,y,z,c)), \
30346    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
30347    x==--_n1##x; \
30348    I[0] = I[1], I[1] = I[2], \
30349    I[3] = I[4], I[4] = I[5], \
30350    I[6] = I[7], I[7] = I[8], \
30351    _p1##x = x++, ++_n1##x)
30352 
30353       if (is_empty()) return *this;
30354       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
30355       CImg_3x3(I,T);
30356       cimg_forZC(*this,z,c) {
30357         T
30358           *ptrd1 = res.data(0,0,z,c),
30359           *ptrd2 = ptrd1 + res._width,
30360           *ptrd3 = ptrd2 + res._width;
30361         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
30362           if (Icp != Icn && Ipc != Inc) {
30363             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
30364             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
30365             *(ptrd1++) = Icp==Inc?Inc:Icc;
30366             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
30367             *(ptrd2++) = Icc;
30368             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
30369             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
30370             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
30371             *(ptrd3++) = Icn==Inc?Inc:Icc;
30372           } else {
30373             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
30374             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
30375             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
30376           }
30377         }
30378       }
30379       return res;
30380     }
30381 
30382     //! Mirror image content along specified axis.
30383     /**
30384        \param axis Mirror axis
30385     **/
30386     CImg<T>& mirror(const char axis) {
30387       if (is_empty()) return *this;
30388       T *pf, *pb, *buf = 0;
30389       switch (cimg::lowercase(axis)) {
30390       case 'x' : {
30391         pf = _data; pb = data(_width - 1);
30392         const unsigned int width2 = _width/2;
30393         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
30394           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
30395           pf+=_width - width2;
30396           pb+=_width + width2;
30397         }
30398       } break;
30399       case 'y' : {
30400         buf = new T[_width];
30401         pf = _data; pb = data(0,_height - 1);
30402         const unsigned int height2 = _height/2;
30403         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
30404           for (unsigned int y = 0; y<height2; ++y) {
30405             std::memcpy(buf,pf,_width*sizeof(T));
30406             std::memcpy(pf,pb,_width*sizeof(T));
30407             std::memcpy(pb,buf,_width*sizeof(T));
30408             pf+=_width;
30409             pb-=_width;
30410           }
30411           pf+=(ulongT)_width*(_height - height2);
30412           pb+=(ulongT)_width*(_height + height2);
30413         }
30414       } break;
30415       case 'z' : {
30416         buf = new T[(ulongT)_width*_height];
30417         pf = _data; pb = data(0,0,_depth - 1);
30418         const unsigned int depth2 = _depth/2;
30419         cimg_forC(*this,c) {
30420           for (unsigned int z = 0; z<depth2; ++z) {
30421             std::memcpy(buf,pf,_width*_height*sizeof(T));
30422             std::memcpy(pf,pb,_width*_height*sizeof(T));
30423             std::memcpy(pb,buf,_width*_height*sizeof(T));
30424             pf+=(ulongT)_width*_height;
30425             pb-=(ulongT)_width*_height;
30426           }
30427           pf+=(ulongT)_width*_height*(_depth - depth2);
30428           pb+=(ulongT)_width*_height*(_depth + depth2);
30429         }
30430       } break;
30431       case 'c' : {
30432         buf = new T[(ulongT)_width*_height*_depth];
30433         pf = _data; pb = data(0,0,0,_spectrum - 1);
30434         const unsigned int _spectrum2 = _spectrum/2;
30435         for (unsigned int v = 0; v<_spectrum2; ++v) {
30436           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
30437           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
30438           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
30439           pf+=(ulongT)_width*_height*_depth;
30440           pb-=(ulongT)_width*_height*_depth;
30441         }
30442       } break;
30443       default :
30444         throw CImgArgumentException(_cimg_instance
30445                                     "mirror(): Invalid specified axis '%c'.",
30446                                     cimg_instance,
30447                                     axis);
30448       }
30449       delete[] buf;
30450       return *this;
30451     }
30452 
30453     //! Mirror image content along specified axis \newinstance.
30454     CImg<T> get_mirror(const char axis) const {
30455       return (+*this).mirror(axis);
30456     }
30457 
30458     //! Mirror image content along specified axes.
30459     /**
30460        \param axes Mirror axes, as a C-string.
30461        \note \c axes may contains multiple characters, e.g. \c "xyz"
30462     **/
30463     CImg<T>& mirror(const char *const axes) {
30464       for (const char *s = axes; *s; ++s) mirror(*s);
30465       return *this;
30466     }
30467 
30468     //! Mirror image content along specified axes \newinstance.
30469     CImg<T> get_mirror(const char *const axes) const {
30470       return (+*this).mirror(axes);
30471     }
30472 
30473     //! Shift image content.
30474     /**
30475        \param delta_x Amount of displacement along the X-axis.
30476        \param delta_y Amount of displacement along the Y-axis.
30477        \param delta_z Amount of displacement along the Z-axis.
30478        \param delta_c Amount of displacement along the C-axis.
30479        \param boundary_conditions Border condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
30480     **/
30481     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
30482                    const unsigned int boundary_conditions=0) {
30483       if (is_empty()) return *this;
30484       if (boundary_conditions==3)
30485         return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
30486                         width() - delta_x - 1,
30487                         height() - delta_y - 1,
30488                         depth() - delta_z - 1,
30489                         spectrum() - delta_c - 1,3).move_to(*this);
30490       if (delta_x) // Shift along X-axis
30491         switch (boundary_conditions) {
30492         case 2 : { // Periodic
30493           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
30494           if (!ndelta_x) return *this;
30495           CImg<T> buf(cimg::abs(ndelta_x));
30496           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
30497               std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
30498               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
30499               std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
30500             } else cimg_forYZC(*this,y,z,c) {
30501               std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
30502               std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
30503               std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
30504             }
30505         } break;
30506         case 1 : // Neumann
30507           if (delta_x<0) {
30508             const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
30509             if (!ndelta_x) return *this;
30510             cimg_forYZC(*this,y,z,c) {
30511               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
30512               T *ptrd = data(_width - 1,y,z,c);
30513               const T val = *ptrd;
30514               for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
30515             }
30516           } else {
30517             const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
30518             if (!ndelta_x) return *this;
30519             cimg_forYZC(*this,y,z,c) {
30520               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
30521               T *ptrd = data(0,y,z,c);
30522               const T val = *ptrd;
30523               for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
30524             }
30525           }
30526           break;
30527         default : // Dirichlet
30528           if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
30529           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
30530               std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
30531               std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
30532             } else cimg_forYZC(*this,y,z,c) {
30533               std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
30534               std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
30535             }
30536         }
30537 
30538       if (delta_y) // Shift along Y-axis
30539         switch (boundary_conditions) {
30540         case 2 : { // Periodic
30541           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
30542           if (!ndelta_y) return *this;
30543           CImg<T> buf(width(),cimg::abs(ndelta_y));
30544           if (ndelta_y>0) cimg_forZC(*this,z,c) {
30545               std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
30546               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
30547               std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
30548             } else cimg_forZC(*this,z,c) {
30549               std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
30550               std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
30551               std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
30552             }
30553         } break;
30554         case 1 : // Neumann
30555           if (delta_y<0) {
30556             const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
30557             if (!ndelta_y) return *this;
30558             cimg_forZC(*this,z,c) {
30559               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
30560               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
30561               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
30562             }
30563           } else {
30564             const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
30565             if (!ndelta_y) return *this;
30566             cimg_forZC(*this,z,c) {
30567               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
30568               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
30569               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
30570             }
30571           }
30572           break;
30573         default : // Dirichlet
30574           if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
30575           if (delta_y<0) cimg_forZC(*this,z,c) {
30576               std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
30577               std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
30578             } else cimg_forZC(*this,z,c) {
30579               std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
30580               std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
30581             }
30582         }
30583 
30584       if (delta_z) // Shift along Z-axis
30585         switch (boundary_conditions) {
30586         case 2 : { // Periodic
30587           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
30588           if (!ndelta_z) return *this;
30589           CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
30590           if (ndelta_z>0) cimg_forC(*this,c) {
30591               std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
30592               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30593               std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
30594             } else cimg_forC(*this,c) {
30595               std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
30596               std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
30597               std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
30598             }
30599         } break;
30600         case 1 : // Neumann
30601           if (delta_z<0) {
30602             const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
30603             if (!ndelta_z) return *this;
30604             cimg_forC(*this,c) {
30605               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30606               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
30607               for (int l = 0; l<ndelta_z - 1; ++l) {
30608                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
30609               }
30610             }
30611           } else {
30612             const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
30613             if (!ndelta_z) return *this;
30614             cimg_forC(*this,c) {
30615               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30616               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
30617               for (int l = 0; l<ndelta_z - 1; ++l) {
30618                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
30619               }
30620             }
30621           }
30622           break;
30623         default : // Dirichlet
30624           if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
30625           if (delta_z<0) cimg_forC(*this,c) {
30626               std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
30627               std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
30628             } else cimg_forC(*this,c) {
30629               std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
30630               std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
30631             }
30632         }
30633 
30634       if (delta_c) // Shift along C-axis
30635         switch (boundary_conditions) {
30636         case 2 : { // Periodic
30637           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
30638           if (!ndelta_c) return *this;
30639           CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
30640           if (ndelta_c>0) {
30641             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
30642             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30643             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
30644           } else {
30645             std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
30646             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
30647             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
30648           }
30649         } break;
30650         case 1 : // Neumann
30651           if (delta_c<0) {
30652             const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
30653             if (!ndelta_c) return *this;
30654             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30655             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
30656             for (int l = 0; l<ndelta_c - 1; ++l) {
30657               std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
30658             }
30659           } else {
30660             const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
30661             if (!ndelta_c) return *this;
30662             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30663             T *ptrd = data(0,0,0,1);
30664             for (int l = 0; l<ndelta_c - 1; ++l) {
30665               std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
30666             }
30667           }
30668           break;
30669         default : // Dirichlet
30670           if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
30671           if (delta_c<0) {
30672             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
30673             std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
30674           } else {
30675             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
30676             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
30677           }
30678         }
30679       return *this;
30680     }
30681 
30682     //! Shift image content \newinstance.
30683     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
30684                       const unsigned int boundary_conditions=0) const {
30685       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
30686     }
30687 
30688     //! Permute axes order.
30689     /**
30690        \param order Axes permutations, as a C-string of 4 characters.
30691        This function permutes image content regarding the specified axes permutation.
30692     **/
30693     CImg<T>& permute_axes(const char *const order) {
30694       return get_permute_axes(order).move_to(*this);
30695     }
30696 
30697     //! Permute axes order \newinstance.
30698     CImg<T> get_permute_axes(const char *const order) const {
30699       const T foo = (T)0;
30700       return _permute_axes(order,foo);
30701     }
30702 
30703     template<typename t>
30704     CImg<t> _permute_axes(const char *const order, const t&) const {
30705       if (is_empty() || !order) return CImg<t>(*this,false);
30706       CImg<t> res;
30707       const T* ptrs = _data;
30708       unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
30709       for (unsigned int l = 0; order[l]; ++l) {
30710         int c = cimg::lowercase(order[l]);
30711         if (c!='x' && c!='y' && c!='z' && c!='c') { *s_code = 4; break; }
30712         else { ++n_code[c%=4]; s_code[l] = c; }
30713       }
30714       if (*order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
30715         const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
30716         ulongT wh, whd;
30717         switch (code) {
30718         case 0x0123 : // xyzc
30719           return +*this;
30720         case 0x0132 : // xycz
30721           res.assign(_width,_height,_spectrum,_depth);
30722           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30723           cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
30724           break;
30725         case 0x0213 : // xzyc
30726           res.assign(_width,_depth,_height,_spectrum);
30727           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30728           cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
30729           break;
30730         case 0x0231 : // xzcy
30731           res.assign(_width,_depth,_spectrum,_height);
30732           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30733           cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
30734           break;
30735         case 0x0312 : // xcyz
30736           res.assign(_width,_spectrum,_height,_depth);
30737           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30738           cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
30739           break;
30740         case 0x0321 : // xczy
30741           res.assign(_width,_spectrum,_depth,_height);
30742           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30743           cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
30744           break;
30745         case 0x1023 : // yxzc
30746           res.assign(_height,_width,_depth,_spectrum);
30747           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30748           cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
30749           break;
30750         case 0x1032 : // yxcz
30751           res.assign(_height,_width,_spectrum,_depth);
30752           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30753           cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
30754           break;
30755         case 0x1203 : // yzxc
30756           res.assign(_height,_depth,_width,_spectrum);
30757           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30758           cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
30759           break;
30760         case 0x1230 : // yzcx
30761           res.assign(_height,_depth,_spectrum,_width);
30762           switch (_width) {
30763           case 1 : {
30764             t *ptr_r = res.data(0,0,0,0);
30765             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30766               *(ptr_r++) = (t)*(ptrs++);
30767             }
30768           } break;
30769           case 2 : {
30770             t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
30771             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30772               *(ptr_r++) = (t)ptrs[0];
30773               *(ptr_g++) = (t)ptrs[1];
30774               ptrs+=2;
30775             }
30776           } break;
30777           case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
30778             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);
30779             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30780               *(ptr_r++) = (t)ptrs[0];
30781               *(ptr_g++) = (t)ptrs[1];
30782               *(ptr_b++) = (t)ptrs[2];
30783               ptrs+=3;
30784             }
30785           } break;
30786           case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
30787             t
30788               *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
30789               *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
30790             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30791               *(ptr_r++) = (t)ptrs[0];
30792               *(ptr_g++) = (t)ptrs[1];
30793               *(ptr_b++) = (t)ptrs[2];
30794               *(ptr_a++) = (t)ptrs[3];
30795               ptrs+=4;
30796             }
30797           } break;
30798           default : {
30799             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30800             cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
30801             return res;
30802           }
30803           }
30804           break;
30805         case 0x1302 : // ycxz
30806           res.assign(_height,_spectrum,_width,_depth);
30807           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30808           cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
30809           break;
30810         case 0x1320 : // yczx
30811           res.assign(_height,_spectrum,_depth,_width);
30812           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30813           cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
30814           break;
30815         case 0x2013 : // zxyc
30816           res.assign(_depth,_width,_height,_spectrum);
30817           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30818           cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
30819           break;
30820         case 0x2031 : // zxcy
30821           res.assign(_depth,_width,_spectrum,_height);
30822           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30823           cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
30824           break;
30825         case 0x2103 : // zyxc
30826           res.assign(_depth,_height,_width,_spectrum);
30827           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30828           cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
30829           break;
30830         case 0x2130 : // zycx
30831           res.assign(_depth,_height,_spectrum,_width);
30832           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30833           cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
30834           break;
30835         case 0x2301 : // zcxy
30836           res.assign(_depth,_spectrum,_width,_height);
30837           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30838           cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
30839           break;
30840         case 0x2310 : // zcyx
30841           res.assign(_depth,_spectrum,_height,_width);
30842           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30843           cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
30844           break;
30845         case 0x3012 : // cxyz
30846           res.assign(_spectrum,_width,_height,_depth);
30847           switch (_spectrum) {
30848           case 1 : {
30849             const T *ptr_r = data(0,0,0,0);
30850             t *ptrd = res._data;
30851             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
30852           } break;
30853           case 2 : {
30854             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
30855             t *ptrd = res._data;
30856             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30857               ptrd[0] = (t)*(ptr_r++);
30858               ptrd[1] = (t)*(ptr_g++);
30859               ptrd+=2;
30860             }
30861           } break;
30862           case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
30863             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
30864             t *ptrd = res._data;
30865             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30866               ptrd[0] = (t)*(ptr_r++);
30867               ptrd[1] = (t)*(ptr_g++);
30868               ptrd[2] = (t)*(ptr_b++);
30869               ptrd+=3;
30870             }
30871           } break;
30872           case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
30873             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);
30874             t *ptrd = res._data;
30875             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30876               ptrd[0] = (t)*(ptr_r++);
30877               ptrd[1] = (t)*(ptr_g++);
30878               ptrd[2] = (t)*(ptr_b++);
30879               ptrd[3] = (t)*(ptr_a++);
30880               ptrd+=4;
30881             }
30882           } break;
30883           default : {
30884             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30885             cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
30886           }
30887           }
30888           break;
30889         case 0x3021 : // cxzy
30890           res.assign(_spectrum,_width,_depth,_height);
30891           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30892           cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
30893           break;
30894         case 0x3102 : // cyxz
30895           res.assign(_spectrum,_height,_width,_depth);
30896           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30897           cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
30898           break;
30899         case 0x3120 : // cyzx
30900           res.assign(_spectrum,_height,_depth,_width);
30901           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30902           cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
30903           break;
30904         case 0x3201 : // czxy
30905           res.assign(_spectrum,_depth,_width,_height);
30906           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30907           cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
30908           break;
30909         case 0x3210 : // czyx
30910           res.assign(_spectrum,_depth,_height,_width);
30911           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30912           cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
30913           break;
30914         }
30915       }
30916       if (!res)
30917         throw CImgArgumentException(_cimg_instance
30918                                     "permute_axes(): Invalid specified permutation '%s'.",
30919                                     cimg_instance,
30920                                     order);
30921       return res;
30922     }
30923 
30924     //! Unroll pixel values along specified axis.
30925     /**
30926        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
30927     **/
30928     CImg<T>& unroll(const char axis) {
30929       const unsigned int siz = (unsigned int)size();
30930       if (siz) switch (cimg::lowercase(axis)) {
30931       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
30932       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
30933       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
30934       default : _spectrum = siz; _width = _height = _depth = 1;
30935       }
30936       return *this;
30937     }
30938 
30939     //! Unroll pixel values along specified axis \newinstance.
30940     CImg<T> get_unroll(const char axis) const {
30941       return (+*this).unroll(axis);
30942     }
30943 
30944     //! Rotate image with arbitrary angle.
30945     /**
30946        \param angle Rotation angle, in degrees.
30947        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
30948        \param boundary_conditions Boundary conditions.
30949               Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
30950        \note The size of the image is modified.
30951     **/
30952     CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
30953                     const unsigned int boundary_conditions=0) {
30954       const float nangle = cimg::mod(angle,360.0f);
30955       if (nangle==0.0f) return *this;
30956       return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
30957     }
30958 
30959     //! Rotate image with arbitrary angle \newinstance.
30960     CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
30961                        const unsigned int boundary_conditions=0) const {
30962       if (is_empty()) return *this;
30963       CImg<T> res;
30964       const float nangle = cimg::mod(angle,360.0f);
30965       if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles.
30966         const int wm1 = width() - 1, hm1 = height() - 1;
30967         const int iangle = (int)nangle/90;
30968         switch (iangle) {
30969         case 1 : { // 90 deg
30970           res.assign(_height,_width,_depth,_spectrum);
30971           T *ptrd = res._data;
30972           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
30973         } break;
30974         case 2 : { // 180 deg
30975           res.assign(_width,_height,_depth,_spectrum);
30976           T *ptrd = res._data;
30977           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
30978         } break;
30979         case 3 : { // 270 deg
30980           res.assign(_height,_width,_depth,_spectrum);
30981           T *ptrd = res._data;
30982           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
30983         } break;
30984         default : // 0 deg
30985           return *this;
30986         }
30987       } else { // Generic angle
30988         const float
30989           rad = (float)(nangle*cimg::PI/180.0),
30990           ca = (float)std::cos(rad), sa = (float)std::sin(rad),
30991           ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
30992           vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
30993           w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
30994         res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
30995         const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
30996         _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
30997       }
30998       return res;
30999     }
31000 
31001     //! Rotate image with arbitrary angle, around a center point.
31002     /**
31003        \param angle Rotation angle, in degrees.
31004        \param cx X-coordinate of the rotation center.
31005        \param cy Y-coordinate of the rotation center.
31006        \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
31007        \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31008     **/
31009     CImg<T>& rotate(const float angle, const float cx, const float cy,
31010                     const unsigned int interpolation, const unsigned int boundary_conditions=0) {
31011       return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
31012     }
31013 
31014     //! Rotate image with arbitrary angle, around a center point \newinstance.
31015     CImg<T> get_rotate(const float angle, const float cx, const float cy,
31016                        const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
31017       if (is_empty()) return *this;
31018       CImg<T> res(_width,_height,_depth,_spectrum);
31019       _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
31020       return res;
31021     }
31022 
31023     // [internal] Perform 2d rotation with arbitrary angle.
31024     void _rotate(CImg<T>& res, const float angle,
31025                  const unsigned int interpolation, const unsigned int boundary_conditions,
31026                  const float w2, const float h2,
31027                  const float rw2, const float rh2) const {
31028       const float
31029         rad = (float)(angle*cimg::PI/180.0),
31030         ca = (float)std::cos(rad), sa = (float)std::sin(rad);
31031 
31032       switch (boundary_conditions) {
31033       case 3 : { // Mirror
31034 
31035         switch (interpolation) {
31036         case 2 : { // Cubic interpolation
31037           const float ww = 2.0f*width(), hh = 2.0f*height();
31038           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31039             cimg_forXYZC(res,x,y,z,c) {
31040             const float xc = x - rw2, yc = y - rh2,
31041               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
31042               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
31043             res(x,y,z,c) = _cubic_cut_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31044           }
31045         } break;
31046         case 1 : { // Linear interpolation
31047           const float ww = 2.0f*width(), hh = 2.0f*height();
31048           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31049             cimg_forXYZC(res,x,y,z,c) {
31050             const float xc = x - rw2, yc = y - rh2,
31051               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
31052               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
31053             res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31054           }
31055         } break;
31056         default : { // Nearest-neighbor interpolation
31057           const int ww = 2*width(), hh = 2*height();
31058           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31059             cimg_forXYZC(res,x,y,z,c) {
31060             const float xc = x - rw2, yc = y - rh2,
31061               mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
31062               my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
31063             res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31064           }
31065         }
31066         }
31067       } break;
31068 
31069       case 2 : // Periodic
31070         switch (interpolation) {
31071         case 2 : { // Cubic interpolation
31072           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31073             cimg_forXYZC(res,x,y,z,c) {
31074             const float xc = x - rw2, yc = y - rh2;
31075             res(x,y,z,c) = _cubic_cut_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
31076                                            cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
31077           }
31078         } break;
31079         case 1 : { // Linear interpolation
31080           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31081             cimg_forXYZC(res,x,y,z,c) {
31082             const float xc = x - rw2, yc = y - rh2;
31083             res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
31084                                            cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
31085           }
31086         } break;
31087         default : { // Nearest-neighbor 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) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
31092                                    cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
31093           }
31094         }
31095         } break;
31096 
31097       case 1 : // Neumann
31098         switch (interpolation) {
31099         case 2 : { // Cubic interpolation
31100           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31101           cimg_forXYZC(res,x,y,z,c) {
31102             const float xc = x - rw2, yc = y - rh2;
31103             res(x,y,z,c) = _cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
31104           }
31105         } break;
31106         case 1 : { // Linear interpolation
31107           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31108           cimg_forXYZC(res,x,y,z,c) {
31109             const float xc = x - rw2, yc = y - rh2;
31110             res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
31111           }
31112         } break;
31113         default : { // Nearest-neighbor interpolation
31114           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31115           cimg_forXYZC(res,x,y,z,c) {
31116             const float xc = x - rw2, yc = y - rh2;
31117             res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
31118                                  (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
31119           }
31120         }
31121         } break;
31122 
31123       default : // Dirichlet
31124         switch (interpolation) {
31125         case 2 : { // Cubic interpolation
31126           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31127           cimg_forXYZC(res,x,y,z,c) {
31128             const float xc = x - rw2, yc = y - rh2;
31129             res(x,y,z,c) = cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
31130           }
31131         } break;
31132         case 1 : { // Linear interpolation
31133           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31134           cimg_forXYZC(res,x,y,z,c) {
31135             const float xc = x - rw2, yc = y - rh2;
31136             res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
31137           }
31138         } break;
31139         default : { // Nearest-neighbor interpolation
31140           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31141           cimg_forXYZC(res,x,y,z,c) {
31142             const float xc = x - rw2, yc = y - rh2;
31143             res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
31144                                 (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
31145           }
31146         }
31147         }
31148       }
31149     }
31150 
31151     //! Rotate volumetric image with arbitrary angle and axis.
31152     /**
31153        \param u X-coordinate of the 3d rotation axis.
31154        \param v Y-coordinate of the 3d rotation axis.
31155        \param w Z-coordinate of the 3d rotation axis.
31156        \param angle Rotation angle, in degrees.
31157        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
31158        \param boundary_conditions Boundary conditions.
31159               Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31160        \note Most of the time, size of the image is modified.
31161     **/
31162     CImg<T> rotate(const float u, const float v, const float w, const float angle,
31163                    const unsigned int interpolation, const unsigned int boundary_conditions) {
31164       const float nangle = cimg::mod(angle,360.0f);
31165       if (nangle==0.0f) return *this;
31166       return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
31167     }
31168 
31169     //! Rotate volumetric image with arbitrary angle and axis \newinstance.
31170     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
31171                        const unsigned int interpolation, const unsigned int boundary_conditions) const {
31172       if (is_empty()) return *this;
31173       CImg<T> res;
31174       const float
31175         w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
31176         w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
31177       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
31178       const CImg<Tfloat>
31179         X = R*CImg<Tfloat>(8,3,1,1,
31180                            0.0f,w1,w1,0.0f,0.0f,w1,w1,0.0f,
31181                            0.0f,0.0f,h1,h1,0.0f,0.0f,h1,h1,
31182                            0.0f,0.0f,0.0f,0.0f,d1,d1,d1,d1);
31183       float
31184         xm, xM = X.get_shared_row(0).max_min(xm),
31185         ym, yM = X.get_shared_row(1).max_min(ym),
31186         zm, zM = X.get_shared_row(2).max_min(zm);
31187       const int
31188         dx = (int)cimg::round(xM - xm),
31189         dy = (int)cimg::round(yM - ym),
31190         dz = (int)cimg::round(zM - zm);
31191       R.transpose();
31192       res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
31193       const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
31194       _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
31195       return res;
31196     }
31197 
31198     //! Rotate volumetric image with arbitrary angle and axis, around a center point.
31199     /**
31200        \param u X-coordinate of the 3d rotation axis.
31201        \param v Y-coordinate of the 3d rotation axis.
31202        \param w Z-coordinate of the 3d rotation axis.
31203        \param angle Rotation angle, in degrees.
31204        \param cx X-coordinate of the rotation center.
31205        \param cy Y-coordinate of the rotation center.
31206        \param cz Z-coordinate of the rotation center.
31207        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
31208        \param boundary_conditions Boundary conditions. Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic }</tt>.
31209        \note Most of the time, size of the image is modified.
31210     **/
31211     CImg<T> rotate(const float u, const float v, const float w, const float angle,
31212                    const float cx, const float cy, const float cz,
31213                    const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
31214       const float nangle = cimg::mod(angle,360.0f);
31215       if (nangle==0.0f) return *this;
31216       return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
31217     }
31218 
31219     //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
31220     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
31221                        const float cx, const float cy, const float cz,
31222                        const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
31223       if (is_empty()) return *this;
31224       CImg<T> res(_width,_height,_depth,_spectrum);
31225       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
31226       _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
31227       return res;
31228     }
31229 
31230     // [internal] Perform 3d rotation with arbitrary axis and angle.
31231     void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
31232                  const unsigned int interpolation, const unsigned int boundary_conditions,
31233                  const float w2, const float h2, const float d2,
31234                  const float rw2, const float rh2, const float rd2) const {
31235       switch (boundary_conditions) {
31236       case 3 : // Mirror
31237         switch (interpolation) {
31238         case 2 : { // Cubic interpolation
31239           const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
31240           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31241           cimg_forXYZ(res,x,y,z) {
31242             const float
31243               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31244               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31245               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31246               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31247             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X<width()?X:ww - X - 1,
31248                                                              Y<height()?Y:hh - Y - 1,
31249                                                              Z<depth()?Z:dd - Z - z,c);
31250           }
31251         } break;
31252         case 1 : { // Linear interpolation
31253           const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
31254           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31255           cimg_forXYZ(res,x,y,z) {
31256             const float
31257               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31258               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31259               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31260               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31261             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
31262                                                              Y<height()?Y:hh - Y - 1,
31263                                                              Z<depth()?Z:dd - Z - 1,c);
31264           }
31265         } break;
31266         default : { // Nearest-neighbor interpolation
31267           const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
31268           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31269           cimg_forXYZ(res,x,y,z) {
31270             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31271             const int
31272               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31273               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31274               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31275             cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
31276                                                     Y<height()?Y:hh - Y - 1,
31277                                                     Z<depth()?Z:dd - Z -  1,c);
31278           }
31279         }
31280         } break;
31281 
31282       case 2 : // Periodic
31283         switch (interpolation) {
31284         case 2 : { // Cubic interpolation
31285           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31286           cimg_forXYZ(res,x,y,z) {
31287             const float
31288               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31289               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
31290               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
31291               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
31292             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
31293           }
31294         } break;
31295         case 1 : { // Linear interpolation
31296           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31297           cimg_forXYZ(res,x,y,z) {
31298             const float
31299               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31300               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
31301               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
31302               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
31303             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X,Y,Z,c);
31304           }
31305         } break;
31306         default : { // Nearest-neighbor interpolation
31307           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31308           cimg_forXYZ(res,x,y,z) {
31309             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31310             const int
31311               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
31312               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
31313               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
31314             cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
31315           }
31316         }
31317         } break;
31318 
31319       case 1 : // Neumann
31320         switch (interpolation) {
31321         case 2 : { // Cubic interpolation
31322           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31323           cimg_forXYZ(res,x,y,z) {
31324             const float
31325               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31326               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31327               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31328               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31329             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
31330           }
31331         } break;
31332         case 1 : { // Linear interpolation
31333           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31334           cimg_forXYZ(res,x,y,z) {
31335             const float
31336               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31337               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31338               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31339               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31340             cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
31341           }
31342         } break;
31343         default : { // Nearest-neighbor interpolation
31344           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31345           cimg_forXYZ(res,x,y,z) {
31346             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31347             const int
31348               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
31349               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
31350               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
31351             cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
31352           }
31353         }
31354         } break;
31355 
31356       default : // Dirichlet
31357         switch (interpolation) {
31358         case 2 : { // Cubic interpolation
31359           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31360           cimg_forXYZ(res,x,y,z) {
31361             const float
31362               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31363               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31364               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31365               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31366             cimg_forC(res,c) res(x,y,z,c) = cubic_cut_atXYZ(X,Y,Z,c,(T)0);
31367           }
31368         } break;
31369         case 1 : { // Linear interpolation
31370           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31371           cimg_forXYZ(res,x,y,z) {
31372             const float
31373               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31374               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31375               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31376               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31377             cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
31378           }
31379         } break;
31380         default : { // Nearest-neighbor interpolation
31381           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31382           cimg_forXYZ(res,x,y,z) {
31383             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31384             const int
31385               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
31386               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
31387               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
31388             cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
31389           }
31390         }
31391         } break;
31392       }
31393     }
31394 
31395     //! Warp image content by a warping field.
31396     /**
31397        \param warp Warping field.
31398        \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
31399        \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
31400        \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31401     **/
31402     template<typename t>
31403     CImg<T>& warp(const CImg<t>& warp, const unsigned int mode=0,
31404                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
31405       return get_warp(warp,mode,interpolation,boundary_conditions).move_to(*this);
31406     }
31407 
31408     //! Warp image content by a warping field \newinstance
31409     template<typename t>
31410     CImg<T> get_warp(const CImg<t>& warp, const unsigned int mode=0,
31411                      const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
31412       if (is_empty() || !warp) return *this;
31413       if (mode && !is_sameXYZ(warp))
31414         throw CImgArgumentException(_cimg_instance
31415                                     "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
31416                                     "have different XYZ dimensions.",
31417                                     cimg_instance,
31418                                     warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
31419 
31420       CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
31421 
31422       if (warp._spectrum==1) { // 1d warping
31423         if (mode>=3) { // Forward-relative warp
31424           res.fill((T)0);
31425           if (interpolation>=1) // Linear interpolation
31426             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31427             cimg_forYZC(res,y,z,c) {
31428               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31429               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
31430             }
31431           else // Nearest-neighbor interpolation
31432             cimg_forYZC(res,y,z,c) {
31433               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31434               cimg_forX(res,x) {
31435                 const int X = x + (int)cimg::round(*(ptrs0++));
31436                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
31437               }
31438             }
31439         } else if (mode==2) { // Forward-absolute 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++),(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 = (int)cimg::round(*(ptrs0++));
31452                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
31453               }
31454             }
31455         } else if (mode==1) { // Backward-relative warp
31456           if (interpolation==2) // Cubic interpolation
31457             switch (boundary_conditions) {
31458             case 3 : { // Mirror
31459               const float w2 = 2.0f*width();
31460               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31461               cimg_forYZC(res,y,z,c) {
31462                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31463                 cimg_forX(res,x) {
31464                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
31465                   *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
31466                 }
31467               }
31468             } break;
31469             case 2 : // Periodic
31470               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31471               cimg_forYZC(res,y,z,c) {
31472                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31473                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
31474               }
31475               break;
31476             case 1 : // Neumann
31477               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31478               cimg_forYZC(res,y,z,c) {
31479                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31480                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(x - (float)*(ptrs0++),y,z,c);
31481               }
31482               break;
31483             default : // Dirichlet
31484               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31485               cimg_forYZC(res,y,z,c) {
31486                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31487                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
31488               }
31489             }
31490           else if (interpolation==1) // Linear interpolation
31491             switch (boundary_conditions) {
31492             case 3 : { // Mirror
31493               const float w2 = 2.0f*width();
31494               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31495               cimg_forYZC(res,y,z,c) {
31496                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31497                 cimg_forX(res,x) {
31498                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
31499                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
31500                 }
31501               }
31502             } break;
31503             case 2 : // Periodic
31504               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31505               cimg_forYZC(res,y,z,c) {
31506                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31507                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
31508               }
31509               break;
31510             case 1 : // Neumann
31511               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31512               cimg_forYZC(res,y,z,c) {
31513                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31514                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
31515               }
31516               break;
31517             default : // Dirichlet
31518               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31519               cimg_forYZC(res,y,z,c) {
31520                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31521                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
31522               }
31523             }
31524           else // Nearest-neighbor interpolation
31525             switch (boundary_conditions) {
31526             case 3 : { // Mirror
31527               const int w2 = 2*width();
31528               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31529               cimg_forYZC(res,y,z,c) {
31530                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31531                 cimg_forX(res,x) {
31532                   const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
31533                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
31534                 }
31535               }
31536             } break;
31537             case 2 : // Periodic
31538               cimg_forYZC(res,y,z,c) {
31539                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31540                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),y,z,c);
31541               }
31542               break;
31543             case 1 : // Neumann
31544               cimg_forYZC(res,y,z,c) {
31545                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31546                 cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
31547               }
31548               break;
31549             default : // Dirichlet
31550               cimg_forYZC(res,y,z,c) {
31551                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31552                 cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,(T)0);
31553               }
31554             }
31555         }
31556         else { // Backward-absolute warp
31557           if (interpolation==2) // Cubic interpolation
31558             switch (boundary_conditions) {
31559             case 3 : { // Mirror
31560               const float w2 = 2.0f*width();
31561               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31562                 cimg_forYZC(res,y,z,c) {
31563                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31564                 cimg_forX(res,x) {
31565                   const float mx = cimg::mod((float)*(ptrs0++),w2);
31566                   *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
31567                 }
31568               }
31569             } break;
31570             case 2 : // Periodic
31571               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31572               cimg_forYZC(res,y,z,c) {
31573                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31574                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
31575               }
31576               break;
31577             case 1 : // Neumann
31578               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31579               cimg_forYZC(res,y,z,c) {
31580                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31581                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX((float)*(ptrs0++),0,0,c);
31582               }
31583               break;
31584             default : // Dirichlet
31585               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31586               cimg_forYZC(res,y,z,c) {
31587                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31588                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atX((float)*(ptrs0++),0,0,c,(T)0);
31589               }
31590             }
31591           else if (interpolation==1) // Linear interpolation
31592             switch (boundary_conditions) {
31593             case 3 : { // Mirror
31594               const float w2 = 2.0f*width();
31595               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31596                 cimg_forYZC(res,y,z,c) {
31597                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31598                 cimg_forX(res,x) {
31599                   const float mx = cimg::mod((float)*(ptrs0++),w2);
31600                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
31601                 }
31602               }
31603             } break;
31604             case 2 : // Periodic
31605               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31606               cimg_forYZC(res,y,z,c) {
31607                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31608                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
31609               }
31610               break;
31611             case 1 : // Neumann
31612               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31613               cimg_forYZC(res,y,z,c) {
31614                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31615                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
31616               }
31617               break;
31618             default : // Dirichlet
31619               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31620               cimg_forYZC(res,y,z,c) {
31621                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31622                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
31623               }
31624             }
31625           else // Nearest-neighbor interpolation
31626             switch (boundary_conditions) {
31627             case 3 : { // Mirror
31628               const int w2 = 2*width();
31629               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31630                 cimg_forYZC(res,y,z,c) {
31631                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31632                 cimg_forX(res,x) {
31633                   const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
31634                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
31635                 }
31636               }
31637             } break;
31638             case 2 : // Periodic
31639               cimg_forYZC(res,y,z,c) {
31640                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31641                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),0,0,c);
31642               }
31643               break;
31644             case 1 : // Neumann
31645               cimg_forYZC(res,y,z,c) {
31646                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31647                 cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
31648               }
31649               break;
31650             default : // Dirichlet
31651               cimg_forYZC(res,y,z,c) {
31652                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31653                 cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,(T)0);
31654               }
31655             }
31656         }
31657 
31658       } else if (warp._spectrum==2) { // 2d warping
31659         if (mode>=3) { // Forward-relative warp
31660           res.fill((T)0);
31661           if (interpolation>=1) // Linear interpolation
31662             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31663             cimg_forYZC(res,y,z,c) {
31664               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31665               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
31666             }
31667           else // Nearest-neighbor interpolation
31668             cimg_forYZC(res,y,z,c) {
31669               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31670               cimg_forX(res,x) {
31671                 const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
31672                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
31673               }
31674             }
31675         } else if (mode==2) { // Forward-absolute 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++),(float)*(ptrs0++),(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 = (int)cimg::round(*(ptrs0++)), 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==1) { // Backward-relative warp
31692           if (interpolation==2) // Cubic interpolation
31693             switch (boundary_conditions) {
31694             case 3 : { // Mirror
31695               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31696               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31697               cimg_forYZC(res,y,z,c) {
31698                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31699                 cimg_forX(res,x) {
31700                   const float
31701                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31702                     my = cimg::mod(y - (float)*(ptrs1++),h2);
31703                   *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31704                 }
31705               }
31706             } break;
31707             case 2 : // Periodic
31708               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31709               cimg_forYZC(res,y,z,c) {
31710                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31711                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31712                                                              cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
31713               }
31714               break;
31715             case 1 : // Neumann
31716               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31717               cimg_forYZC(res,y,z,c) {
31718                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31719                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
31720               }
31721               break;
31722             default : // Dirichlet
31723               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31724               cimg_forYZC(res,y,z,c) {
31725                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31726                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
31727               }
31728             }
31729           else if (interpolation==1) // Linear interpolation
31730             switch (boundary_conditions) {
31731             case 3 : { // Mirror
31732               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31733               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31734               cimg_forYZC(res,y,z,c) {
31735                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31736                 cimg_forX(res,x) {
31737                   const float
31738                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31739                     my = cimg::mod(y - (float)*(ptrs1++),h2);
31740                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31741                 }
31742               }
31743             } break;
31744             case 2 : // Periodic
31745               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31746               cimg_forYZC(res,y,z,c) {
31747                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31748                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31749                                                              cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
31750               }
31751               break;
31752             case 1 : // Neumann
31753               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31754               cimg_forYZC(res,y,z,c) {
31755                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31756                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
31757               }
31758               break;
31759             default : // Dirichlet
31760               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31761               cimg_forYZC(res,y,z,c) {
31762                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31763                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
31764               }
31765             }
31766           else // Nearest-neighbor interpolation
31767             switch (boundary_conditions) {
31768             case 3 : { // Mirror
31769               const int w2 = 2*width(), h2 = 2*height();
31770               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31771               cimg_forYZC(res,y,z,c) {
31772                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31773                 cimg_forX(res,x) {
31774                   const int
31775                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
31776                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
31777                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31778                 }
31779               }
31780             } break;
31781             case 2 : // Periodic
31782               cimg_forYZC(res,y,z,c) {
31783                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31784                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
31785                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),z,c);
31786               }
31787               break;
31788             case 1 : // Neumann
31789               cimg_forYZC(res,y,z,c) {
31790                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31791                 cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
31792               }
31793               break;
31794             default : // Dirichlet
31795               cimg_forYZC(res,y,z,c) {
31796                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31797                 cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,(T)0);
31798               }
31799             }
31800         } else { // Backward-absolute warp
31801           if (interpolation==2) // Cubic interpolation
31802             switch (boundary_conditions) {
31803             case 3 : { // Mirror
31804               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31805               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31806               cimg_forYZC(res,y,z,c) {
31807                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31808                 cimg_forX(res,x) {
31809                   const float
31810                     mx = cimg::mod((float)*(ptrs0++),w2),
31811                     my = cimg::mod((float)*(ptrs1++),h2);
31812                   *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31813                 }
31814               }
31815             } break;
31816             case 2 : // Periodic
31817               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31818               cimg_forYZC(res,y,z,c) {
31819                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31820                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
31821                                                              cimg::mod((float)*(ptrs1++),(float)_height),0,c);
31822               }
31823               break;
31824             case 1 : // Neumann
31825               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31826               cimg_forYZC(res,y,z,c) {
31827                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31828                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
31829               }
31830               break;
31831             default : // Dirichlet
31832               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31833               cimg_forYZC(res,y,z,c) {
31834                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31835                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
31836               }
31837             }
31838           else if (interpolation==1) // Linear interpolation
31839             switch (boundary_conditions) {
31840             case 3 : { // Mirror
31841               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31842               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31843               cimg_forYZC(res,y,z,c) {
31844                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31845                 cimg_forX(res,x) {
31846                   const float
31847                     mx = cimg::mod((float)*(ptrs0++),w2),
31848                     my = cimg::mod((float)*(ptrs1++),h2);
31849                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31850                 }
31851               }
31852             } break;
31853             case 2 : // Periodic
31854               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31855               cimg_forYZC(res,y,z,c) {
31856                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31857                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
31858                                                              cimg::mod((float)*(ptrs1++),(float)_height),0,c);
31859               }
31860               break;
31861             case 1 : // Neumann
31862               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31863               cimg_forYZC(res,y,z,c) {
31864                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31865                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
31866               }
31867               break;
31868             default : // Dirichlet
31869               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31870               cimg_forYZC(res,y,z,c) {
31871                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31872                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
31873               }
31874             }
31875           else // Nearest-neighbor interpolation
31876             switch (boundary_conditions) {
31877             case 3 : { // Mirror
31878               const int w2 = 2*width(), h2 = 2*height();
31879               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31880               cimg_forYZC(res,y,z,c) {
31881                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31882                 cimg_forX(res,x) {
31883                   const int
31884                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
31885                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
31886                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31887                 }
31888               }
31889             } break;
31890             case 2 : // Periodic
31891               cimg_forYZC(res,y,z,c) {
31892                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31893                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
31894                                                      cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),0,c);
31895               }
31896               break;
31897             case 1 : // Neumann
31898               cimg_forYZC(res,y,z,c) {
31899                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31900                 cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
31901               }
31902               break;
31903             default : // Dirichlet
31904               cimg_forYZC(res,y,z,c) {
31905                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31906                 cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,(T)0);
31907               }
31908             }
31909         }
31910 
31911       } else { // 3d warping
31912         if (mode>=3) { // Forward-relative warp
31913           res.fill((T)0);
31914           if (interpolation>=1) // Linear interpolation
31915             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31916             cimg_forYZC(res,y,z,c) {
31917               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31918               const T *ptrs = data(0,y,z,c);
31919               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
31920                                                     z + (float)*(ptrs2++),c);
31921             }
31922           else // Nearest-neighbor interpolation
31923             cimg_forYZC(res,y,z,c) {
31924               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31925               const T *ptrs = data(0,y,z,c);
31926               cimg_forX(res,x) {
31927                 const int
31928                   X = x + (int)cimg::round(*(ptrs0++)),
31929                   Y = y + (int)cimg::round(*(ptrs1++)),
31930                   Z = z + (int)cimg::round(*(ptrs2++));
31931                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
31932               }
31933             }
31934         } else if (mode==2) { // Forward-absolute warp
31935           res.fill((T)0);
31936           if (interpolation>=1) // Linear interpolation
31937             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31938             cimg_forYZC(res,y,z,c) {
31939               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31940               const T *ptrs = data(0,y,z,c);
31941               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
31942             }
31943           else // Nearest-neighbor interpolation
31944             cimg_forYZC(res,y,z,c) {
31945               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31946               const T *ptrs = data(0,y,z,c);
31947               cimg_forX(res,x) {
31948                 const int
31949                   X = (int)cimg::round(*(ptrs0++)),
31950                   Y = (int)cimg::round(*(ptrs1++)),
31951                   Z = (int)cimg::round(*(ptrs2++));
31952                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
31953               }
31954             }
31955         } else if (mode==1) { // Backward-relative warp
31956           if (interpolation==2) // Cubic interpolation
31957             switch (boundary_conditions) {
31958             case 3 : { // Mirror
31959               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
31960               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31961               cimg_forYZC(res,y,z,c) {
31962                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31963                 T *ptrd = res.data(0,y,z,c);
31964                 cimg_forX(res,x) {
31965                   const float
31966                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31967                     my = cimg::mod(y - (float)*(ptrs1++),h2),
31968                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
31969                   *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
31970                                                my<height()?my:h2 - my - 1,
31971                                                mz<depth()?mz:d2 - mz - 1,c);
31972                 }
31973               }
31974             } break;
31975             case 2 : // Periodic
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) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31981                                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),
31982                                                               cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
31983               }
31984               break;
31985             case 1 : // Neumann
31986               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31987               cimg_forYZC(res,y,z,c) {
31988                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31989                 T *ptrd = res.data(0,y,z,c);
31990                 cimg_forX(res,x)
31991                   *(ptrd++) = _cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
31992               }
31993               break;
31994             default : // Dirichlet
31995               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31996               cimg_forYZC(res,y,z,c) {
31997                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31998                 T *ptrd = res.data(0,y,z,c);
31999                 cimg_forX(res,x)
32000                   *(ptrd++) = cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
32001               }
32002             }
32003           else if (interpolation==1) // Linear interpolation
32004             switch (boundary_conditions) {
32005             case 3 : { // Mirror
32006               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32007               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32008               cimg_forYZC(res,y,z,c) {
32009                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32010                 T *ptrd = res.data(0,y,z,c);
32011                 cimg_forX(res,x) {
32012                   const float
32013                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
32014                     my = cimg::mod(y - (float)*(ptrs1++),h2),
32015                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
32016                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
32017                                                my<height()?my:h2 - my - 1,
32018                                                mz<depth()?mz:d2 - mz - 1,c);
32019                 }
32020               }
32021             } break;
32022             case 2 : // Periodic
32023               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
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) *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
32028                                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),
32029                                                               cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
32030               }
32031               break;
32032             case 1 : // Neumann
32033               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32034               cimg_forYZC(res,y,z,c) {
32035                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32036                 T *ptrd = res.data(0,y,z,c);
32037                 cimg_forX(res,x)
32038                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
32039               }
32040               break;
32041             default : // Dirichlet
32042               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32043               cimg_forYZC(res,y,z,c) {
32044                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32045                 T *ptrd = res.data(0,y,z,c);
32046                 cimg_forX(res,x)
32047                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
32048               }
32049             }
32050           else // Nearest neighbor interpolation
32051             switch (boundary_conditions) {
32052             case 3 : { // Mirror
32053               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
32054               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32055               cimg_forYZC(res,y,z,c) {
32056                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32057                 T *ptrd = res.data(0,y,z,c);
32058                 cimg_forX(res,x) {
32059                   const int
32060                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
32061                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
32062                     mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
32063                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
32064                                       my<height()?my:h2 - my - 1,
32065                                       mz<depth()?mz:d2 - mz - 1,c);
32066                 }
32067               }
32068             } break;
32069             case 2 : // Periodic
32070               cimg_forYZC(res,y,z,c) {
32071                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32072                 T *ptrd = res.data(0,y,z,c);
32073                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
32074                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),
32075                                                      cimg::mod(z - (int)cimg::round(*(ptrs2++)),(int)_depth),c);
32076               }
32077               break;
32078             case 1 : // Neumann
32079               cimg_forYZC(res,y,z,c) {
32080                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32081                 T *ptrd = res.data(0,y,z,c);
32082                 cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
32083               }
32084               break;
32085             default : // Dirichlet
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++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,(T)0);
32090               }
32091             }
32092         } else { // Backward-absolute warp
32093           if (interpolation==2) // Cubic interpolation
32094             switch (boundary_conditions) {
32095             case 3 : { // Mirror
32096               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32097               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32098               cimg_forYZC(res,y,z,c) {
32099                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32100                 T *ptrd = res.data(0,y,z,c);
32101                 cimg_forX(res,x) {
32102                   const float
32103                     mx = cimg::mod((float)*(ptrs0++),w2),
32104                     my = cimg::mod((float)*(ptrs1++),h2),
32105                     mz = cimg::mod((float)*(ptrs2++),d2);
32106                   *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
32107                                                my<height()?my:h2 - my - 1,
32108                                                mz<depth()?mz:d2 - mz - 1,c);
32109                 }
32110               }
32111             } break;
32112             case 2 : // Periodic
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) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
32118                                                               cimg::mod((float)*(ptrs1++),(float)_height),
32119                                                               cimg::mod((float)*(ptrs2++),(float)_depth),c);
32120               }
32121               break;
32122             case 1 : // Neumann
32123               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32124               cimg_forYZC(res,y,z,c) {
32125                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32126                 T *ptrd = res.data(0,y,z,c);
32127                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
32128               }
32129               break;
32130             default : // Dirichlet
32131               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32132               cimg_forYZC(res,y,z,c) {
32133                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32134                 T *ptrd = res.data(0,y,z,c);
32135                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
32136                                                              c,(T)0);
32137               }
32138             }
32139           else if (interpolation==1) // Linear interpolation
32140             switch (boundary_conditions) {
32141             case 3 : { // Mirror
32142               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32143               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32144               cimg_forYZC(res,y,z,c) {
32145                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32146                 T *ptrd = res.data(0,y,z,c);
32147                 cimg_forX(res,x) {
32148                   const float
32149                     mx = cimg::mod((float)*(ptrs0++),w2),
32150                     my = cimg::mod((float)*(ptrs1++),h2),
32151                     mz = cimg::mod((float)*(ptrs2++),d2);
32152                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
32153                                                my<height()?my:h2 - my - 1,
32154                                                mz<depth()?mz:d2 - mz - 1,c);
32155                 }
32156               }
32157             } break;
32158             case 2 :// Periodic
32159               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
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) *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
32164                                                               cimg::mod((float)*(ptrs1++),(float)_height),
32165                                                               cimg::mod((float)*(ptrs2++),(float)_depth),c);
32166               }
32167               break;
32168             case 1 : // Neumann
32169               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32170               cimg_forYZC(res,y,z,c) {
32171                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32172                 T *ptrd = res.data(0,y,z,c);
32173                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
32174               }
32175               break;
32176             default : // Dirichlet
32177               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32178               cimg_forYZC(res,y,z,c) {
32179                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32180                 T *ptrd = res.data(0,y,z,c);
32181                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
32182                                                              c,(T)0);
32183               }
32184             }
32185           else // Nearest-neighbor interpolation
32186             switch (boundary_conditions) {
32187             case 3 : { // Mirror
32188               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
32189               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32190               cimg_forYZC(res,y,z,c) {
32191                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32192                 T *ptrd = res.data(0,y,z,c);
32193                 cimg_forX(res,x) {
32194                   const int
32195                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
32196                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
32197                     mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
32198                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
32199                                       my<height()?my:h2 - my - 1,
32200                                       mz<depth()?mz:d2 - mz - 1,c);
32201                 }
32202               }
32203             } break;
32204             case 2 : // Periodic
32205               cimg_forYZC(res,y,z,c) {
32206                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32207                 T *ptrd = res.data(0,y,z,c);
32208                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
32209                                                      cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),
32210                                                      cimg::mod((int)cimg::round(*(ptrs2++)),(int)_depth),c);
32211               }
32212               break;
32213             case 1 : // Neumann
32214               cimg_forYZC(res,y,z,c) {
32215                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32216                 T *ptrd = res.data(0,y,z,c);
32217                 cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
32218               }
32219               break;
32220             default : // Dirichlet
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++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,(T)0);
32225               }
32226             }
32227         }
32228       }
32229       return res;
32230     }
32231 
32232     //! Generate a 2d representation of a 3d image, with XY,XZ and YZ views.
32233     /**
32234        \param x0 X-coordinate of the projection point.
32235        \param y0 Y-coordinate of the projection point.
32236        \param z0 Z-coordinate of the projection point.
32237     **/
32238     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
32239       if (is_empty() || _depth<2) return +*this;
32240       const unsigned int
32241         _x0 = (x0>=_width)?_width - 1:x0,
32242         _y0 = (y0>=_height)?_height - 1:y0,
32243         _z0 = (z0>=_depth)?_depth - 1:z0;
32244       const CImg<T>
32245         img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
32246         img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
32247         resize(_depth,_height,1,-100,-1),
32248         img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
32249       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
32250         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
32251         draw_image(0,img_xy._height,img_xz);
32252     }
32253 
32254     //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace.
32255     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
32256       if (_depth<2) return *this;
32257       return get_projections2d(x0,y0,z0).move_to(*this);
32258     }
32259 
32260     //! Crop image region.
32261     /**
32262        \param x0 = X-coordinate of the upper-left crop rectangle corner.
32263        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
32264        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
32265        \param c0 = C-coordinate of the upper-left crop rectangle corner.
32266        \param x1 = X-coordinate of the lower-right crop rectangle corner.
32267        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
32268        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
32269        \param c1 = C-coordinate of the lower-right crop rectangle corner.
32270        \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
32271     **/
32272     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
32273                   const int x1, const int y1, const int z1, const int c1,
32274                   const unsigned int boundary_conditions=0) {
32275       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
32276     }
32277 
32278     //! Crop image region \newinstance.
32279     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
32280                      const int x1, const int y1, const int z1, const int c1,
32281                      const unsigned int boundary_conditions=0) const {
32282       if (is_empty())
32283         throw CImgInstanceException(_cimg_instance
32284                                     "crop(): Empty instance.",
32285                                     cimg_instance);
32286       const int
32287         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
32288         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
32289         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
32290         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
32291       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
32292       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
32293         switch (boundary_conditions) {
32294         case 3 : { // Mirror
32295           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
32296           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32297           cimg_forXYZC(res,x,y,z,c) {
32298             const int
32299               mx = cimg::mod(nx0 + x,w2),
32300               my = cimg::mod(ny0 + y,h2),
32301               mz = cimg::mod(nz0 + z,d2),
32302               mc = cimg::mod(nc0 + c,s2);
32303             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
32304                                    my<height()?my:h2 - my - 1,
32305                                    mz<depth()?mz:d2 - mz - 1,
32306                                    mc<spectrum()?mc:s2 - mc - 1);
32307           }
32308         } break;
32309         case 2 : { // Periodic
32310           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32311           cimg_forXYZC(res,x,y,z,c) {
32312             res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
32313                                    cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
32314           }
32315         } break;
32316         case 1 : // Neumann
32317           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32318           cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
32319           break;
32320         default : // Dirichlet
32321           res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
32322         }
32323       else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
32324       return res;
32325     }
32326 
32327     //! Crop image region \overloading.
32328     CImg<T>& crop(const int x0, const int y0, const int z0,
32329                   const int x1, const int y1, const int z1,
32330                   const unsigned int boundary_conditions=0) {
32331       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
32332     }
32333 
32334     //! Crop image region \newinstance.
32335     CImg<T> get_crop(const int x0, const int y0, const int z0,
32336                      const int x1, const int y1, const int z1,
32337                      const unsigned int boundary_conditions=0) const {
32338       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
32339     }
32340 
32341     //! Crop image region \overloading.
32342     CImg<T>& crop(const int x0, const int y0,
32343                   const int x1, const int y1,
32344                   const unsigned int boundary_conditions=0) {
32345       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
32346     }
32347 
32348     //! Crop image region \newinstance.
32349     CImg<T> get_crop(const int x0, const int y0,
32350                      const int x1, const int y1,
32351                      const unsigned int boundary_conditions=0) const {
32352       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
32353     }
32354 
32355     //! Crop image region \overloading.
32356     CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
32357       return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
32358     }
32359 
32360     //! Crop image region \newinstance.
32361     CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
32362       return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
32363     }
32364 
32365     //! Autocrop image region, regarding the specified background value.
32366     CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
32367       if (is_empty()) return *this;
32368       for (const char *s = axes; *s; ++s) {
32369         const char axis = cimg::lowercase(*s);
32370         const CImg<intT> coords = _autocrop(value,axis);
32371         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
32372         else switch (axis) {
32373         case 'x' : {
32374 	  const int x0 = coords[0], x1 = coords[1];
32375 	  if (x0>=0 && x1>=0) crop(x0,x1);
32376 	} break;
32377         case 'y' : {
32378 	  const int y0 = coords[0], y1 = coords[1];
32379 	  if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
32380 	} break;
32381         case 'z' : {
32382 	  const int z0 = coords[0], z1 = coords[1];
32383 	  if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
32384 	} break;
32385         default : {
32386 	  const int c0 = coords[0], c1 = coords[1];
32387 	  if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
32388 	}
32389         }
32390       }
32391       return *this;
32392     }
32393 
32394     //! Autocrop image region, regarding the specified background value \newinstance.
32395     CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
32396       return (+*this).autocrop(value,axes);
32397     }
32398 
32399     //! Autocrop image region, regarding the specified background color.
32400     /**
32401        \param color Color used for the crop. If \c 0, color is guessed.
32402        \param axes Axes used for the crop.
32403     **/
32404     CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
32405       if (is_empty()) return *this;
32406       if (!color) { // Guess color.
32407         const CImg<T> col1 = get_vector_at(0,0,0);
32408         const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
32409         autocrop(col1,axes);
32410         if (_width==w && _height==h && _depth==d && _spectrum==s) {
32411           const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
32412           autocrop(col2,axes);
32413         }
32414         return *this;
32415       }
32416       for (const char *s = axes; *s; ++s) {
32417         const char axis = cimg::lowercase(*s);
32418         switch (axis) {
32419         case 'x' : {
32420 	  int x0 = width(), x1 = -1;
32421 	  cimg_forC(*this,c) {
32422 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
32423 	    const int nx0 = coords[0], nx1 = coords[1];
32424 	    if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
32425 	  }
32426           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
32427 	} break;
32428         case 'y' : {
32429 	  int y0 = height(), y1 = -1;
32430 	  cimg_forC(*this,c) {
32431 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
32432 	    const int ny0 = coords[0], ny1 = coords[1];
32433 	    if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
32434 	  }
32435           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
32436 	} break;
32437         default : {
32438 	  int z0 = depth(), z1 = -1;
32439 	  cimg_forC(*this,c) {
32440 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
32441 	    const int nz0 = coords[0], nz1 = coords[1];
32442 	    if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
32443 	  }
32444 	  if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
32445 	}
32446         }
32447       }
32448       return *this;
32449     }
32450 
32451     //! Autocrop image region, regarding the specified background color \newinstance.
32452     CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
32453       return (+*this).autocrop(color,axes);
32454     }
32455 
32456     //! Autocrop image region, regarding the specified background color \overloading.
32457     template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
32458       return get_autocrop(color,axes).move_to(*this);
32459     }
32460 
32461     //! Autocrop image region, regarding the specified background color \newinstance.
32462     template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
32463       return get_autocrop(color._data,axes);
32464     }
32465 
32466     CImg<intT> _autocrop(const T& value, const char axis) const {
32467       CImg<intT> res;
32468       switch (cimg::lowercase(axis)) {
32469       case 'x' : {
32470         int x0 = -1, x1 = -1;
32471         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
32472           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
32473 	if (x0>=0) {
32474           for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
32475             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
32476         }
32477 	res = CImg<intT>::vector(x0,x1);
32478       } break;
32479       case 'y' : {
32480         int y0 = -1, y1 = -1;
32481         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
32482           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
32483 	if (y0>=0) {
32484           for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
32485             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
32486         }
32487   	res = CImg<intT>::vector(y0,y1);
32488       } break;
32489       case 'z' : {
32490         int z0 = -1, z1 = -1;
32491         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
32492           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
32493 	if (z0>=0) {
32494           for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
32495             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
32496         }
32497   	res = CImg<intT>::vector(z0,z1);
32498       } break;
32499       default : {
32500         int c0 = -1, c1 = -1;
32501         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
32502           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
32503 	if (c0>=0) {
32504           for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
32505             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
32506         }
32507   	res = CImg<intT>::vector(c0,c1);
32508       }
32509       }
32510       return res;
32511     }
32512 
32513     //! Return specified image column.
32514     /**
32515        \param x0 Image column.
32516     **/
32517     CImg<T> get_column(const int x0) const {
32518       return get_columns(x0,x0);
32519     }
32520 
32521     //! Return specified image column \inplace.
32522     CImg<T>& column(const int x0) {
32523       return columns(x0,x0);
32524     }
32525 
32526     //! Return specified range of image columns.
32527     /**
32528        \param x0 Starting image column.
32529        \param x1 Ending image column.
32530     **/
32531     CImg<T>& columns(const int x0, const int x1) {
32532       return get_columns(x0,x1).move_to(*this);
32533     }
32534 
32535     //! Return specified range of image columns \inplace.
32536     CImg<T> get_columns(const int x0, const int x1) const {
32537       return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
32538     }
32539 
32540     //! Return specified image row.
32541     CImg<T> get_row(const int y0) const {
32542       return get_rows(y0,y0);
32543     }
32544 
32545     //! Return specified image row \inplace.
32546     /**
32547        \param y0 Image row.
32548     **/
32549     CImg<T>& row(const int y0) {
32550       return rows(y0,y0);
32551     }
32552 
32553     //! Return specified range of image rows.
32554     /**
32555        \param y0 Starting image row.
32556        \param y1 Ending image row.
32557     **/
32558     CImg<T> get_rows(const int y0, const int y1) const {
32559       return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
32560     }
32561 
32562     //! Return specified range of image rows \inplace.
32563     CImg<T>& rows(const int y0, const int y1) {
32564       return get_rows(y0,y1).move_to(*this);
32565     }
32566 
32567     //! Return specified image slice.
32568     /**
32569        \param z0 Image slice.
32570     **/
32571     CImg<T> get_slice(const int z0) const {
32572       return get_slices(z0,z0);
32573     }
32574 
32575     //! Return specified image slice \inplace.
32576     CImg<T>& slice(const int z0) {
32577       return slices(z0,z0);
32578     }
32579 
32580     //! Return specified range of image slices.
32581     /**
32582        \param z0 Starting image slice.
32583        \param z1 Ending image slice.
32584     **/
32585     CImg<T> get_slices(const int z0, const int z1) const {
32586       return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
32587     }
32588 
32589     //! Return specified range of image slices \inplace.
32590     CImg<T>& slices(const int z0, const int z1) {
32591       return get_slices(z0,z1).move_to(*this);
32592     }
32593 
32594     //! Return specified image channel.
32595     /**
32596        \param c0 Image channel.
32597     **/
32598     CImg<T> get_channel(const int c0) const {
32599       return get_channels(c0,c0);
32600     }
32601 
32602     //! Return specified image channel \inplace.
32603     CImg<T>& channel(const int c0) {
32604       return channels(c0,c0);
32605     }
32606 
32607     //! Return specified range of image channels.
32608     /**
32609        \param c0 Starting image channel.
32610        \param c1 Ending image channel.
32611     **/
32612     CImg<T> get_channels(const int c0, const int c1) const {
32613       return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
32614     }
32615 
32616     //! Return specified range of image channels \inplace.
32617     CImg<T>& channels(const int c0, const int c1) {
32618       return get_channels(c0,c1).move_to(*this);
32619     }
32620 
32621     //! Return stream line of a 2d or 3d vector field.
32622     CImg<floatT> get_streamline(const float x, const float y, const float z,
32623                                 const float L=256, const float dl=0.1f,
32624                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
32625                                 const bool is_oriented_only=false) const {
32626       if (_spectrum!=2 && _spectrum!=3)
32627         throw CImgInstanceException(_cimg_instance
32628                                     "streamline(): Instance is not a 2d or 3d vector field.",
32629                                     cimg_instance);
32630       if (_spectrum==2) {
32631         if (is_oriented_only) {
32632           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
32633           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
32634                             0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
32635         } else {
32636           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
32637           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
32638                             0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
32639         }
32640       }
32641       if (is_oriented_only) {
32642         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
32643         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
32644                           0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
32645       }
32646       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
32647       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
32648                         0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
32649     }
32650 
32651     //! Return stream line of a 3d vector field.
32652     /**
32653        \param func Vector field function.
32654        \param x X-coordinate of the starting point of the streamline.
32655        \param y Y-coordinate of the starting point of the streamline.
32656        \param z Z-coordinate of the starting point of the streamline.
32657        \param L Streamline length.
32658        \param dl Streamline length increment.
32659        \param interpolation_type Type of interpolation.
32660          Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
32661        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
32662        \param is_oriented_only Tells if the direction of the vectors must be ignored.
32663        \param x0 X-coordinate of the first bounding-box vertex.
32664        \param y0 Y-coordinate of the first bounding-box vertex.
32665        \param z0 Z-coordinate of the first bounding-box vertex.
32666        \param x1 X-coordinate of the second bounding-box vertex.
32667        \param y1 Y-coordinate of the second bounding-box vertex.
32668        \param z1 Z-coordinate of the second bounding-box vertex.
32669     **/
32670     template<typename tfunc>
32671     static CImg<floatT> streamline(const tfunc& func,
32672                                    const float x, const float y, const float z,
32673                                    const float L=256, const float dl=0.1f,
32674                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
32675                                    const bool is_oriented_only=false,
32676                                    const float x0=0, const float y0=0, const float z0=0,
32677                                    const float x1=0, const float y1=0, const float z1=0) {
32678       if (dl<=0)
32679         throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
32680                                     "(should be >0).",
32681                                     pixel_type(),
32682                                     dl);
32683 
32684       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
32685       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
32686       const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
32687       CImg<floatT> coordinates(size_L,3);
32688       const float dl2 = dl/2;
32689       float
32690         *ptr_x = coordinates.data(0,0),
32691         *ptr_y = coordinates.data(0,1),
32692         *ptr_z = coordinates.data(0,2),
32693         pu = (float)(dl*func(x,y,z,0)),
32694         pv = (float)(dl*func(x,y,z,1)),
32695         pw = (float)(dl*func(x,y,z,2)),
32696         X = x, Y = y, Z = z;
32697 
32698       switch (interpolation_type) {
32699       case 0 : { // Nearest integer interpolation.
32700         cimg_forX(coordinates,l) {
32701           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32702           const int
32703             xi = (int)(X>0?X + 0.5f:X - 0.5f),
32704             yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
32705             zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
32706           float
32707             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
32708             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
32709             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
32710           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32711           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32712           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32713         }
32714       } break;
32715       case 1 : { // First-order interpolation.
32716         cimg_forX(coordinates,l) {
32717           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32718           float
32719             u = (float)(dl*func(X,Y,Z,0)),
32720             v = (float)(dl*func(X,Y,Z,1)),
32721             w = (float)(dl*func(X,Y,Z,2));
32722           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32723           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32724           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32725         }
32726       } break;
32727       case 2 : { // Second order interpolation.
32728         cimg_forX(coordinates,l) {
32729           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32730           float
32731             u0 = (float)(dl2*func(X,Y,Z,0)),
32732             v0 = (float)(dl2*func(X,Y,Z,1)),
32733             w0 = (float)(dl2*func(X,Y,Z,2));
32734           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
32735           float
32736             u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
32737             v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
32738             w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
32739           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32740           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32741           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32742         }
32743       } break;
32744       default : { // Fourth order interpolation.
32745         cimg_forX(coordinates,x) {
32746           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32747           float
32748             u0 = (float)(dl2*func(X,Y,Z,0)),
32749             v0 = (float)(dl2*func(X,Y,Z,1)),
32750             w0 = (float)(dl2*func(X,Y,Z,2));
32751           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
32752           float
32753             u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
32754             v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
32755             w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
32756           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
32757           float
32758             u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
32759             v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
32760             w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
32761           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
32762           float
32763             u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
32764             v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
32765             w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
32766           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
32767           const float
32768             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
32769             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
32770             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
32771           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32772           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32773         }
32774       }
32775       }
32776       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
32777       return coordinates;
32778     }
32779 
32780     //! Return stream line of a 3d vector field \overloading.
32781     static CImg<floatT> streamline(const char *const expression,
32782                                    const float x, const float y, const float z,
32783                                    const float L=256, const float dl=0.1f,
32784                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
32785                                    const bool is_oriented_only=false,
32786                                    const float x0=0, const float y0=0, const float z0=0,
32787                                    const float x1=0, const float y1=0, const float z1=0) {
32788       _functor4d_streamline_expr func(expression);
32789       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
32790     }
32791 
32792     struct _functor4d_streamline2d_directed {
32793       const CImg<T>& ref;
32794       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
32795       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32796         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
32797       }
32798     };
32799 
32800     struct _functor4d_streamline3d_directed {
32801       const CImg<T>& ref;
32802       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
32803       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32804         return (float)ref._linear_atXYZ(x,y,z,c);
32805       }
32806     };
32807 
32808     struct _functor4d_streamline2d_oriented {
32809       const CImg<T>& ref;
32810       CImg<floatT> *pI;
32811       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
32812       ~_functor4d_streamline2d_oriented() { delete pI; }
32813       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32814 #define _cimg_vecalign2d(i,j) \
32815         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); }
32816         int
32817           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
32818           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
32819           zi = (int)z;
32820         const float
32821           dx = x - xi,
32822           dy = y - yi;
32823         if (c==0) {
32824           CImg<floatT>& I = *pI;
32825           if (xi<0) xi = 0;
32826           if (nxi<0) nxi = 0;
32827           if (xi>=ref.width()) xi = ref.width() - 1;
32828           if (nxi>=ref.width()) nxi = ref.width() - 1;
32829           if (yi<0) yi = 0;
32830           if (nyi<0) nyi = 0;
32831           if (yi>=ref.height()) yi = ref.height() - 1;
32832           if (nyi>=ref.height()) nyi = ref.height() - 1;
32833           I(0,0,0) = (float)ref(xi,yi,zi,0);   I(0,0,1) = (float)ref(xi,yi,zi,1);
32834           I(1,0,0) = (float)ref(nxi,yi,zi,0);  I(1,0,1) = (float)ref(nxi,yi,zi,1);
32835           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
32836           I(0,1,0) = (float)ref(xi,nyi,zi,0);  I(0,1,1) = (float)ref(xi,nyi,zi,1);
32837           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
32838         }
32839         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
32840       }
32841     };
32842 
32843     struct _functor4d_streamline3d_oriented {
32844       const CImg<T>& ref;
32845       CImg<floatT> *pI;
32846       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
32847       ~_functor4d_streamline3d_oriented() { delete pI; }
32848       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32849 #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) { \
32850   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); }
32851         int
32852           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
32853           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
32854           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
32855         const float
32856           dx = x - xi,
32857           dy = y - yi,
32858           dz = z - zi;
32859         if (c==0) {
32860           CImg<floatT>& I = *pI;
32861           if (xi<0) xi = 0;
32862           if (nxi<0) nxi = 0;
32863           if (xi>=ref.width()) xi = ref.width() - 1;
32864           if (nxi>=ref.width()) nxi = ref.width() - 1;
32865           if (yi<0) yi = 0;
32866           if (nyi<0) nyi = 0;
32867           if (yi>=ref.height()) yi = ref.height() - 1;
32868           if (nyi>=ref.height()) nyi = ref.height() - 1;
32869           if (zi<0) zi = 0;
32870           if (nzi<0) nzi = 0;
32871           if (zi>=ref.depth()) zi = ref.depth() - 1;
32872           if (nzi>=ref.depth()) nzi = ref.depth() - 1;
32873           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
32874           I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
32875           I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
32876           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
32877           I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
32878           I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
32879           I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
32880           I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
32881           I(1,0,1,1) = (float)ref(nxi,yi,nzi,1);  I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
32882           I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
32883           I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
32884           I(0,1,1,1) = (float)ref(xi,nyi,nzi,1);  I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
32885           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
32886           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
32887         }
32888         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
32889       }
32890     };
32891 
32892     struct _functor4d_streamline_expr {
32893       _cimg_math_parser *mp;
32894       ~_functor4d_streamline_expr() { mp->end(); delete mp; }
32895       _functor4d_streamline_expr(const char *const expr):mp(0) {
32896         mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
32897       }
32898       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32899         return (float)(*mp)(x,y,z,c);
32900       }
32901     };
32902 
32903     //! Return a shared-memory image referencing a range of pixels of the image instance.
32904     /**
32905        \param x0 X-coordinate of the starting pixel.
32906        \param x1 X-coordinate of the ending pixel.
32907        \param y0 Y-coordinate.
32908        \param z0 Z-coordinate.
32909        \param c0 C-coordinate.
32910      **/
32911     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
32912                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
32913       const unsigned int
32914         beg = (unsigned int)offset(x0,y0,z0,c0),
32915         end = (unsigned int)offset(x1,y0,z0,c0);
32916       if (beg>end || beg>=size() || end>=size())
32917         throw CImgArgumentException(_cimg_instance
32918                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
32919                                     cimg_instance,
32920                                     x0,x1,y0,z0,c0);
32921 
32922       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
32923     }
32924 
32925     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
32926     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
32927                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
32928       const unsigned int
32929         beg = (unsigned int)offset(x0,y0,z0,c0),
32930         end = (unsigned int)offset(x1,y0,z0,c0);
32931       if (beg>end || beg>=size() || end>=size())
32932         throw CImgArgumentException(_cimg_instance
32933                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
32934                                     cimg_instance,
32935                                     x0,x1,y0,z0,c0);
32936 
32937       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
32938     }
32939 
32940     //! Return a shared-memory image referencing a range of rows of the image instance.
32941     /**
32942        \param y0 Y-coordinate of the starting row.
32943        \param y1 Y-coordinate of the ending row.
32944        \param z0 Z-coordinate.
32945        \param c0 C-coordinate.
32946     **/
32947     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
32948                              const unsigned int z0=0, const unsigned int c0=0) {
32949       const unsigned int
32950         beg = (unsigned int)offset(0,y0,z0,c0),
32951         end = (unsigned int)offset(0,y1,z0,c0);
32952       if (beg>end || beg>=size() || end>=size())
32953         throw CImgArgumentException(_cimg_instance
32954                                     "get_shared_rows(): Invalid request of a shared-memory subset "
32955                                     "(0->%u,%u->%u,%u,%u).",
32956                                     cimg_instance,
32957                                     _width - 1,y0,y1,z0,c0);
32958 
32959       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
32960     }
32961 
32962     //! Return a shared-memory image referencing a range of rows of the image instance \const.
32963     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
32964                                    const unsigned int z0=0, const unsigned int c0=0) const {
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 one row of the image instance.
32979     /**
32980        \param y0 Y-coordinate.
32981        \param z0 Z-coordinate.
32982        \param c0 C-coordinate.
32983     **/
32984     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
32985       return get_shared_rows(y0,y0,z0,c0);
32986     }
32987 
32988     //! Return a shared-memory image referencing one row of the image instance \const.
32989     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
32990       return get_shared_rows(y0,y0,z0,c0);
32991     }
32992 
32993     //! Return a shared memory image referencing a range of slices of the image instance.
32994     /**
32995        \param z0 Z-coordinate of the starting slice.
32996        \param z1 Z-coordinate of the ending slice.
32997        \param c0 C-coordinate.
32998     **/
32999     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
33000       const unsigned int
33001         beg = (unsigned int)offset(0,0,z0,c0),
33002         end = (unsigned int)offset(0,0,z1,c0);
33003       if (beg>end || beg>=size() || end>=size())
33004         throw CImgArgumentException(_cimg_instance
33005                                     "get_shared_slices(): Invalid request of a shared-memory subset "
33006                                     "(0->%u,0->%u,%u->%u,%u).",
33007                                     cimg_instance,
33008                                     _width - 1,_height - 1,z0,z1,c0);
33009 
33010       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
33011     }
33012 
33013     //! Return a shared memory image referencing a range of slices of the image instance \const.
33014     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
33015       const unsigned int
33016         beg = (unsigned int)offset(0,0,z0,c0),
33017         end = (unsigned int)offset(0,0,z1,c0);
33018       if (beg>end || beg>=size() || end>=size())
33019         throw CImgArgumentException(_cimg_instance
33020                                     "get_shared_slices(): Invalid request of a shared-memory subset "
33021                                     "(0->%u,0->%u,%u->%u,%u).",
33022                                     cimg_instance,
33023                                     _width - 1,_height - 1,z0,z1,c0);
33024 
33025       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
33026     }
33027 
33028     //! Return a shared-memory image referencing one slice of the image instance.
33029     /**
33030        \param z0 Z-coordinate.
33031        \param c0 C-coordinate.
33032     **/
33033     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
33034       return get_shared_slices(z0,z0,c0);
33035     }
33036 
33037     //! Return a shared-memory image referencing one slice of the image instance \const.
33038     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
33039       return get_shared_slices(z0,z0,c0);
33040     }
33041 
33042     //! Return a shared-memory image referencing a range of channels of the image instance.
33043     /**
33044        \param c0 C-coordinate of the starting channel.
33045        \param c1 C-coordinate of the ending channel.
33046     **/
33047     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
33048       const unsigned int
33049         beg = (unsigned int)offset(0,0,0,c0),
33050         end = (unsigned int)offset(0,0,0,c1);
33051       if (beg>end || beg>=size() || end>=size())
33052         throw CImgArgumentException(_cimg_instance
33053                                     "get_shared_channels(): Invalid request of a shared-memory subset "
33054                                     "(0->%u,0->%u,0->%u,%u->%u).",
33055                                     cimg_instance,
33056                                     _width - 1,_height - 1,_depth - 1,c0,c1);
33057 
33058       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
33059     }
33060 
33061     //! Return a shared-memory image referencing a range of channels of the image instance \const.
33062     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
33063       const unsigned int
33064         beg = (unsigned int)offset(0,0,0,c0),
33065         end = (unsigned int)offset(0,0,0,c1);
33066       if (beg>end || beg>=size() || end>=size())
33067         throw CImgArgumentException(_cimg_instance
33068                                     "get_shared_channels(): Invalid request of a shared-memory subset "
33069                                     "(0->%u,0->%u,0->%u,%u->%u).",
33070                                     cimg_instance,
33071                                     _width - 1,_height - 1,_depth - 1,c0,c1);
33072 
33073       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
33074     }
33075 
33076     //! Return a shared-memory image referencing one channel of the image instance.
33077     /**
33078        \param c0 C-coordinate.
33079     **/
33080     CImg<T> get_shared_channel(const unsigned int c0) {
33081       return get_shared_channels(c0,c0);
33082     }
33083 
33084     //! Return a shared-memory image referencing one channel of the image instance \const.
33085     const CImg<T> get_shared_channel(const unsigned int c0) const {
33086       return get_shared_channels(c0,c0);
33087     }
33088 
33089     //! Return a shared-memory version of the image instance.
33090     CImg<T> get_shared() {
33091       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
33092     }
33093 
33094     //! Return a shared-memory version of the image instance \const.
33095     const CImg<T> get_shared() const {
33096       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
33097     }
33098 
33099     //! Split image into a list along specified axis.
33100     /**
33101        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
33102        \param nb Number of splitted parts.
33103        \note
33104        - If \c nb==0, instance image is splitted into blocs of egal values along the specified axis.
33105        - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide.
33106        - If \c nb>0, instance image is splitted into \c nb blocs.
33107     **/
33108     CImgList<T> get_split(const char axis, const int nb=-1) const {
33109       CImgList<T> res;
33110       if (is_empty()) return res;
33111       const char _axis = cimg::lowercase(axis);
33112 
33113       if (nb<0) { // Split by bloc size.
33114         const unsigned int dp = (unsigned int)(nb?-nb:1);
33115         switch (_axis) {
33116         case 'x': {
33117           if (_width>dp) {
33118             res.assign(_width/dp + (_width%dp?1:0),1,1);
33119             const unsigned int pe = _width - dp;
33120             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _height*_depth*_spectrum>=128))
33121             for (unsigned int p = 0; p<pe; p+=dp)
33122               get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
33123             get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33124           } else res.assign(*this);
33125         } break;
33126         case 'y': {
33127           if (_height>dp) {
33128             res.assign(_height/dp + (_height%dp?1:0),1,1);
33129             const unsigned int pe = _height - dp;
33130             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_depth*_spectrum>=128))
33131             for (unsigned int p = 0; p<pe; p+=dp)
33132               get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
33133             get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33134           } else res.assign(*this);
33135         } break;
33136         case 'z': {
33137           if (_depth>dp) {
33138             res.assign(_depth/dp + (_depth%dp?1:0),1,1);
33139             const unsigned int pe = _depth - dp;
33140             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_spectrum>=128))
33141             for (unsigned int p = 0; p<pe; p+=dp)
33142               get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
33143             get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33144           } else res.assign(*this);
33145         } break;
33146         case 'c' : {
33147           if (_spectrum>dp) {
33148             res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
33149             const unsigned int pe = _spectrum - dp;
33150             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_depth>=128))
33151             for (unsigned int p = 0; p<pe; p+=dp)
33152               get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
33153             get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33154           } else res.assign(*this);
33155         }
33156         }
33157       } else if (nb>0) { // Split by number of (non-homogeneous) blocs.
33158         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
33159         if ((unsigned int)nb>siz)
33160           throw CImgArgumentException(_cimg_instance
33161                                       "get_split(): Instance cannot be split along %c-axis into %u blocs.",
33162                                       cimg_instance,
33163                                       axis,nb);
33164         if (nb==1) res.assign(*this);
33165         else {
33166           int err = (int)siz;
33167           unsigned int _p = 0;
33168           switch (_axis) {
33169           case 'x' : {
33170             cimg_forX(*this,p) if ((err-=nb)<=0) {
33171               get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
33172               err+=(int)siz;
33173               _p = p + 1U;
33174             }
33175           } break;
33176           case 'y' : {
33177             cimg_forY(*this,p) if ((err-=nb)<=0) {
33178               get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
33179               err+=(int)siz;
33180               _p = p + 1U;
33181             }
33182           } break;
33183           case 'z' : {
33184             cimg_forZ(*this,p) if ((err-=nb)<=0) {
33185               get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
33186               err+=(int)siz;
33187               _p = p + 1U;
33188             }
33189           } break;
33190           case 'c' : {
33191             cimg_forC(*this,p) if ((err-=nb)<=0) {
33192               get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
33193               err+=(int)siz;
33194               _p = p + 1U;
33195             }
33196           }
33197           }
33198         }
33199       } else { // Split by egal values according to specified axis.
33200         T current = *_data;
33201         switch (_axis) {
33202         case 'x' : {
33203           int i0 = 0;
33204           cimg_forX(*this,i)
33205             if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
33206           get_columns(i0,width() - 1).move_to(res);
33207         } break;
33208         case 'y' : {
33209           int i0 = 0;
33210           cimg_forY(*this,i)
33211             if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
33212           get_rows(i0,height() - 1).move_to(res);
33213         } break;
33214         case 'z' : {
33215           int i0 = 0;
33216           cimg_forZ(*this,i)
33217             if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
33218           get_slices(i0,depth() - 1).move_to(res);
33219         } break;
33220         case 'c' : {
33221           int i0 = 0;
33222           cimg_forC(*this,i)
33223             if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
33224           get_channels(i0,spectrum() - 1).move_to(res);
33225         } break;
33226         default : {
33227           longT i0 = 0;
33228           cimg_foroff(*this,i)
33229             if ((*this)[i]!=current) {
33230               CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
33231               i0 = (longT)i; current = (*this)[i];
33232             }
33233           CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
33234         }
33235         }
33236       }
33237       return res;
33238     }
33239 
33240     //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
33241     /**
33242        \param values Splitting value sequence.
33243        \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
33244        \param keep_values Tells if the splitting sequence must be kept in the splitted blocs.
33245      **/
33246     template<typename t>
33247     CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
33248       CImgList<T> res;
33249       if (is_empty()) return res;
33250       const ulongT vsiz = values.size();
33251       const char _axis = cimg::lowercase(axis);
33252       if (!vsiz) return CImgList<T>(*this);
33253       if (vsiz==1) { // Split according to a single value.
33254         const T value = (T)*values;
33255         switch (_axis) {
33256         case 'x' : {
33257           unsigned int i0 = 0, i = 0;
33258           do {
33259             while (i<_width && (*this)(i)==value) ++i;
33260             if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
33261             while (i<_width && (*this)(i)!=value) ++i;
33262             if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
33263           } while (i<_width);
33264         } break;
33265         case 'y' : {
33266           unsigned int i0 = 0, i = 0;
33267           do {
33268             while (i<_height && (*this)(0,i)==value) ++i;
33269             if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
33270             while (i<_height && (*this)(0,i)!=value) ++i;
33271             if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
33272           } while (i<_height);
33273         } break;
33274         case 'z' : {
33275           unsigned int i0 = 0, i = 0;
33276           do {
33277             while (i<_depth && (*this)(0,0,i)==value) ++i;
33278             if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
33279             while (i<_depth && (*this)(0,0,i)!=value) ++i;
33280             if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
33281           } while (i<_depth);
33282         } break;
33283         case 'c' : {
33284           unsigned int i0 = 0, i = 0;
33285           do {
33286             while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
33287             if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
33288             while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
33289             if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
33290           } while (i<_spectrum);
33291         } break;
33292         default : {
33293           const ulongT siz = size();
33294           ulongT i0 = 0, i = 0;
33295           do {
33296             while (i<siz && (*this)[i]==value) ++i;
33297             if (i>i0) { if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
33298             while (i<siz && (*this)[i]!=value) ++i;
33299             if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
33300           } while (i<siz);
33301         }
33302         }
33303       } else { // Split according to multiple values.
33304         ulongT j = 0;
33305         switch (_axis) {
33306         case 'x' : {
33307           unsigned int i0 = 0, i1 = 0, i = 0;
33308           do {
33309             if ((*this)(i)==*values) {
33310               i1 = i; j = 0;
33311               while (i<_width && (*this)(i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33312               i-=j;
33313               if (i>i1) {
33314                 if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
33315                 if (keep_values) get_columns(i1,i - 1).move_to(res);
33316                 i0 = i;
33317               } else ++i;
33318             } else ++i;
33319           } while (i<_width);
33320           if (i0<_width) get_columns(i0,width() - 1).move_to(res);
33321         } break;
33322         case 'y' : {
33323           unsigned int i0 = 0, i1 = 0, i = 0;
33324           do {
33325             if ((*this)(0,i)==*values) {
33326               i1 = i; j = 0;
33327               while (i<_height && (*this)(0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33328               i-=j;
33329               if (i>i1) {
33330                 if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
33331                 if (keep_values) get_rows(i1,i - 1).move_to(res);
33332                 i0 = i;
33333               } else ++i;
33334             } else ++i;
33335           } while (i<_height);
33336           if (i0<_height) get_rows(i0,height() - 1).move_to(res);
33337         } break;
33338         case 'z' : {
33339           unsigned int i0 = 0, i1 = 0, i = 0;
33340           do {
33341             if ((*this)(0,0,i)==*values) {
33342               i1 = i; j = 0;
33343               while (i<_depth && (*this)(0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33344               i-=j;
33345               if (i>i1) {
33346                 if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
33347                 if (keep_values) get_slices(i1,i - 1).move_to(res);
33348                 i0 = i;
33349               } else ++i;
33350             } else ++i;
33351           } while (i<_depth);
33352           if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
33353         } break;
33354         case 'c' : {
33355           unsigned int i0 = 0, i1 = 0, i = 0;
33356           do {
33357             if ((*this)(0,0,0,i)==*values) {
33358               i1 = i; j = 0;
33359               while (i<_spectrum && (*this)(0,0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33360               i-=j;
33361               if (i>i1) {
33362                 if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
33363                 if (keep_values) get_channels(i1,i - 1).move_to(res);
33364                 i0 = i;
33365               } else ++i;
33366             } else ++i;
33367           } while (i<_spectrum);
33368           if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
33369         } break;
33370         default : {
33371           ulongT i0 = 0, i1 = 0, i = 0;
33372           const ulongT siz = size();
33373           do {
33374             if ((*this)[i]==*values) {
33375               i1 = i; j = 0;
33376               while (i<siz && (*this)[i]==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33377               i-=j;
33378               if (i>i1) {
33379                 if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
33380                 if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
33381                 i0 = i;
33382               } else ++i;
33383             } else ++i;
33384           } while (i<siz);
33385           if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
33386         } break;
33387         }
33388       }
33389       return res;
33390     }
33391 
33392     //! Append two images along specified axis.
33393     /**
33394        \param img Image to append with instance image.
33395        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
33396        \param align Append alignment in \c [0,1].
33397     **/
33398     template<typename t>
33399     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
33400       if (is_empty()) return assign(img,false);
33401       if (!img) return *this;
33402       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
33403     }
33404 
33405     //! Append two images along specified axis \specialization.
33406     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
33407       if (is_empty()) return assign(img,false);
33408       if (!img) return *this;
33409       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
33410     }
33411 
33412     //! Append two images along specified axis \const.
33413     template<typename t>
33414     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
33415       if (is_empty()) return +img;
33416       if (!img) return +*this;
33417       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
33418     }
33419 
33420     //! Append two images along specified axis \specialization.
33421     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
33422       if (is_empty()) return +img;
33423       if (!img) return +*this;
33424       return CImgList<T>(*this,img,true).get_append(axis,align);
33425     }
33426 
33427     //@}
33428     //---------------------------------------
33429     //
33430     //! \name Filtering / Transforms
33431     //@{
33432     //---------------------------------------
33433 
33434     //! Correlate image by a kernel.
33435     /**
33436        \param kernel = the correlation kernel.
33437        \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
33438        \param is_normalized = enable local normalization.
33439        \note
33440        - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
33441        res(x,y,z) = sum_{i,j,k} (*this)(x + i,y + j,z + k)*kernel(i,j,k).
33442     **/
33443     template<typename t>
33444     CImg<T>& correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
33445                        const bool is_normalized=false) {
33446       if (is_empty() || !kernel) return *this;
33447       return get_correlate(kernel,boundary_conditions,is_normalized).move_to(*this);
33448     }
33449 
33450     template<typename t>
33451     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
33452                                       const bool is_normalized=false) const {
33453       return _correlate(kernel,boundary_conditions,is_normalized,false);
33454     }
33455 
33456     //! Correlate image by a kernel \newinstance.
33457     template<typename t>
33458     CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const bool boundary_conditions,
33459                                    const bool is_normalized, const bool is_convolution) const {
33460       if (is_empty() || !kernel) return *this;
33461       typedef _cimg_Ttfloat Ttfloat;
33462       CImg<Ttfloat> res;
33463       const ulongT
33464         res_whd = (ulongT)_width*_height*_depth,
33465         res_size = res_whd*std::max(_spectrum,kernel._spectrum);
33466       const bool
33467         is_inner_parallel = _width*_height*_depth>=32768,
33468         is_outer_parallel = res_size>=32768;
33469       _cimg_abort_init_omp;
33470       cimg_abort_init;
33471 
33472       if (kernel._width==kernel._height &&
33473           ((kernel._depth==1 && kernel._width<=6) || (kernel._depth==kernel._width && kernel._width<=3))) {
33474 
33475         // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel.
33476         if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries
33477           // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster.
33478           res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)).
33479             _correlate(kernel,true,is_normalized,is_convolution);
33480           if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2);
33481           else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2);
33482 
33483         } else { // Neumann boundaries
33484           res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33485           cimg::unused(is_inner_parallel,is_outer_parallel);
33486           CImg<t> _kernel;
33487           if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution
33488             const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2);
33489             if (dw || dh || dd)
33490               kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0).
33491                 move_to(_kernel);
33492           }
33493           if (!_kernel) _kernel = kernel.get_shared();
33494 
33495           switch (_kernel._depth) {
33496           case 3 : {
33497             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33498               cimg_forC(res,c) {
33499               cimg_abort_test;
33500               const CImg<T> img = get_shared_channel(c%_spectrum);
33501               const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33502               CImg<T> I(27);
33503               Ttfloat *ptrd = res.data(0,0,0,c);
33504               if (is_normalized) {
33505                 const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33506                 cimg_for3x3x3(img,x,y,z,0,I,T) {
33507                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
33508                                        I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
33509                                        I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
33510                                        I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
33511                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33512                                        I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
33513                                        I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
33514                                        I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
33515                                        I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
33516                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
33517                                            I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
33518                                            I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
33519                                            I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33520                                            I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
33521                                            I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
33522                                            I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
33523                                            I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33524                                            I[24]*K[24] + I[25]*K[25] + I[26]*K[26])/std::sqrt(N):0);
33525                 }
33526               } else cimg_for3x3x3(img,x,y,z,0,I,T)
33527                        *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
33528                                              I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
33529                                              I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
33530                                              I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33531                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
33532                                              I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
33533                                              I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
33534                                              I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33535                                              I[24]*K[24] + I[25]*K[25] + I[26]*K[26]);
33536             }
33537           } break;
33538           case 2 : {
33539             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33540               cimg_forC(res,c) {
33541               cimg_abort_test;
33542               const CImg<T> img = get_shared_channel(c%_spectrum);
33543               const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33544               CImg<T> I(8);
33545               Ttfloat *ptrd = res.data(0,0,0,c);
33546               if (is_normalized) {
33547                 const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33548                 cimg_for2x2x2(img,x,y,z,0,I,T) {
33549                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
33550                                        I[2]*I[2] + I[3]*I[3] +
33551                                        I[4]*I[4] + I[5]*I[5] +
33552                                        I[6]*I[6] + I[7]*I[7]);
33553                   *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
33554                                            I[2]*K[2] + I[3]*K[3] +
33555                                            I[4]*K[4] + I[5]*K[5] +
33556                                            I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0);
33557                 }
33558               } else cimg_for2x2x2(img,x,y,z,0,I,T)
33559                        *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
33560                                              I[2]*K[2] + I[3]*K[3] +
33561                                              I[4]*K[4] + I[5]*K[5] +
33562                                              I[6]*K[6] + I[7]*K[7]);
33563             }
33564           } break;
33565           default :
33566           case 1 :
33567             switch (_kernel._width) {
33568             case 6 : {
33569               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33570                 cimg_forC(res,c) {
33571                 cimg_abort_test;
33572                 const CImg<T> img = get_shared_channel(c%_spectrum);
33573                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33574                 CImg<T> I(36);
33575                 Ttfloat *ptrd = res.data(0,0,0,c);
33576                 if (is_normalized) {
33577                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33578                   cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) {
33579                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
33580                                          I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
33581                                          I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33582                                          I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
33583                                          I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] +
33584                                          I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
33585                                          I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] +
33586                                          I[35]*I[35]);
33587                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33588                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33589                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33590                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33591                                              I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33592                                              I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33593                                              I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
33594                                              I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
33595                                              I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/
33596                                           std::sqrt(N):0);
33597                   }
33598                 } else cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T)
33599                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33600                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33601                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33602                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33603                                                I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33604                                                I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33605                                                I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
33606                                                I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
33607                                                I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]);
33608               }
33609             } break;
33610             case 5 : {
33611               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33612                 cimg_forC(res,c) {
33613                 cimg_abort_test;
33614                 const CImg<T> img = get_shared_channel(c%_spectrum);
33615                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33616                 CImg<T> I(25);
33617                 Ttfloat *ptrd = res.data(0,0,0,c);
33618                 if (is_normalized) {
33619                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33620                   cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) {
33621                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
33622                                          I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
33623                                          I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33624                                          I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
33625                                          I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
33626                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33627                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33628                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33629                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33630                                              I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33631                                              I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33632                                              I[24]*K[24])/std::sqrt(N):0);
33633                   }
33634                 } else cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T)
33635                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33636                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33637                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33638                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33639                                                I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33640                                                I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33641                                                I[24]*K[24]);
33642               }
33643             } break;
33644             case 4 : {
33645               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33646                 cimg_forC(res,c) {
33647                 cimg_abort_test;
33648                 const CImg<T> img = get_shared_channel(c%_spectrum);
33649                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33650                 CImg<T> I(16);
33651                 Ttfloat *ptrd = res.data(0,0,0,c);
33652                 if (is_normalized) {
33653                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33654                   cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) {
33655                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
33656                                          I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
33657                                          I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
33658                                          I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
33659                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33660                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33661                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33662                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/
33663                                           std::sqrt(N):0);
33664                   }
33665                 } else cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T)
33666                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33667                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33668                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33669                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]);
33670               }
33671             } break;
33672             case 3 : {
33673               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33674                 cimg_forC(res,c) {
33675                 cimg_abort_test;
33676                 const CImg<T> img = get_shared_channel(c%_spectrum);
33677                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33678                 CImg<T> I(9);
33679                 Ttfloat *ptrd = res.data(0,0,0,c);
33680                 if (is_normalized) {
33681                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33682                   cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) {
33683                     const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
33684                                          I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
33685                                          I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
33686                     *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
33687                                              I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
33688                                              I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0);
33689                   }
33690                 } else cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T)
33691                          *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
33692                                                I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
33693                                                I[6]*K[6] + I[7]*K[7] + I[8]*K[8]);
33694               }
33695             } break;
33696             case 2 : {
33697               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33698                 cimg_forC(res,c) {
33699                 cimg_abort_test;
33700                 const CImg<T> img = get_shared_channel(c%_spectrum);
33701                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33702                 CImg<T> I(4);
33703                 Ttfloat *ptrd = res.data(0,0,0,c);
33704                 if (is_normalized) {
33705                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33706                   cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) {
33707                     const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
33708                                          I[2]*I[2] + I[3]*I[3]);
33709                     *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
33710                                              I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0);
33711                   }
33712                 } else cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T)
33713                          *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
33714                                                I[2]*K[2] + I[3]*K[3]);
33715               }
33716             } break;
33717             case 1 :
33718               if (is_normalized) res.fill(1);
33719               else cimg_forC(res,c) {
33720                   cimg_abort_test;
33721                   const CImg<T> img = get_shared_channel(c%_spectrum);
33722                   const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33723                   res.get_shared_channel(c).assign(img)*=K[0];
33724                 }
33725               break;
33726             }
33727           }
33728         }
33729       }
33730 
33731       if (!res) { // Generic version for other kernels and boundary conditions.
33732         res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33733         int
33734           mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
33735           mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1;
33736         if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution
33737         const int
33738           mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
33739         cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
33740         cimg_forC(res,c) _cimg_abort_try_omp {
33741           cimg_abort_test;
33742           const CImg<T> img = get_shared_channel(c%_spectrum);
33743           const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
33744           if (is_normalized) { // Normalized correlation.
33745             const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33746             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33747             for (int z = mz1; z<mze; ++z)
33748               for (int y = my1; y<mye; ++y)
33749                 for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33750                   cimg_abort_test2;
33751                   Ttfloat val = 0, N = 0;
33752                   for (int zm = -mz1; zm<=mz2; ++zm)
33753                     for (int ym = -my1; ym<=my2; ++ym)
33754                       for (int xm = -mx1; xm<=mx2; ++xm) {
33755                         const Ttfloat _val = (Ttfloat)img(x + xm,y + ym,z + zm);
33756                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33757                         N+=_val*_val;
33758                       }
33759                   N*=M;
33760                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33761                 } _cimg_abort_catch_omp2
33762             if (boundary_conditions)
33763               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33764               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33765                 cimg_abort_test2;
33766                 for (int x = 0; x<width();
33767                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33768                   Ttfloat val = 0, N = 0;
33769                   for (int zm = -mz1; zm<=mz2; ++zm)
33770                     for (int ym = -my1; ym<=my2; ++ym)
33771                       for (int xm = -mx1; xm<=mx2; ++xm) {
33772                         const Ttfloat _val = (Ttfloat)img._atXYZ(x + xm,y + ym,z + zm);
33773                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33774                         N+=_val*_val;
33775                       }
33776                   N*=M;
33777                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33778                 }
33779               } _cimg_abort_catch_omp2
33780             else
33781               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33782               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33783                 cimg_abort_test2;
33784                 for (int x = 0; x<width();
33785                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33786                   Ttfloat val = 0, N = 0;
33787                   for (int zm = -mz1; zm<=mz2; ++zm)
33788                     for (int ym = -my1; ym<=my2; ++ym)
33789                       for (int xm = -mx1; xm<=mx2; ++xm) {
33790                         const Ttfloat _val = (Ttfloat)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
33791                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33792                         N+=_val*_val;
33793                       }
33794                   N*=M;
33795                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33796                 }
33797               } _cimg_abort_catch_omp2
33798           } else { // Classical correlation.
33799             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33800               for (int z = mz1; z<mze; ++z)
33801               for (int y = my1; y<mye; ++y)
33802                 for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33803                   cimg_abort_test2;
33804                   Ttfloat val = 0;
33805                   for (int zm = -mz1; zm<=mz2; ++zm)
33806                     for (int ym = -my1; ym<=my2; ++ym)
33807                       for (int xm = -mx1; xm<=mx2; ++xm)
33808                         val+=img(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
33809                   res(x,y,z,c) = (Ttfloat)val;
33810                 } _cimg_abort_catch_omp2
33811             if (boundary_conditions)
33812               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33813               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33814                 cimg_abort_test2;
33815                 for (int x = 0; x<width();
33816                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33817                   Ttfloat val = 0;
33818                   for (int zm = -mz1; zm<=mz2; ++zm)
33819                     for (int ym = -my1; ym<=my2; ++ym)
33820                       for (int xm = -mx1; xm<=mx2; ++xm)
33821                         val+=img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
33822                   res(x,y,z,c) = (Ttfloat)val;
33823                 }
33824               } _cimg_abort_catch_omp2
33825             else
33826               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33827               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33828                 cimg_abort_test2;
33829                 for (int x = 0; x<width();
33830                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33831                   Ttfloat val = 0;
33832                   for (int zm = -mz1; zm<=mz2; ++zm)
33833                     for (int ym = -my1; ym<=my2; ++ym)
33834                       for (int xm = -mx1; xm<=mx2; ++xm)
33835                         val+=img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm);
33836                   res(x,y,z,c) = (Ttfloat)val;
33837                 }
33838               } _cimg_abort_catch_omp2
33839           }
33840         } _cimg_abort_catch_omp
33841       }
33842       cimg_abort_test;
33843       return res;
33844     }
33845 
33846     //! Convolve image by a kernel.
33847     /**
33848        \param kernel = the correlation kernel.
33849        \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
33850        \param is_normalized = enable local normalization.
33851        \note
33852        - The result \p res of the convolution of an image \p img by a kernel \p kernel is defined to be:
33853        res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*kernel(i,j,k)
33854     **/
33855     template<typename t>
33856     CImg<T>& convolve(const CImg<t>& kernel, const bool boundary_conditions=true, const bool is_normalized=false) {
33857       if (is_empty() || !kernel) return *this;
33858       return get_convolve(kernel,boundary_conditions,is_normalized).move_to(*this);
33859     }
33860 
33861     //! Convolve image by a kernel \newinstance.
33862     template<typename t>
33863     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const bool boundary_conditions=true,
33864                                      const bool is_normalized=false) const {
33865       return _correlate(CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
33866                         get_mirror('x').resize(kernel,-1),boundary_conditions,is_normalized,true);
33867     }
33868 
33869     //! Cumulate image values, optionally along specified axis.
33870     /**
33871        \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
33872     **/
33873     CImg<T>& cumulate(const char axis=0) {
33874       switch (cimg::lowercase(axis)) {
33875       case 'x' :
33876         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=512 && _height*_depth*_spectrum>=16))
33877         cimg_forYZC(*this,y,z,c) {
33878           T *ptrd = data(0,y,z,c);
33879           Tlong cumul = (Tlong)0;
33880           cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
33881         }
33882         break;
33883       case 'y' : {
33884         const ulongT w = (ulongT)_width;
33885         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_height>=512 && _width*_depth*_spectrum>=16))
33886         cimg_forXZC(*this,x,z,c) {
33887           T *ptrd = data(x,0,z,c);
33888           Tlong cumul = (Tlong)0;
33889           cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
33890         }
33891       } break;
33892       case 'z' : {
33893         const ulongT wh = (ulongT)_width*_height;
33894         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_depth>=512 && _width*_depth*_spectrum>=16))
33895         cimg_forXYC(*this,x,y,c) {
33896           T *ptrd = data(x,y,0,c);
33897           Tlong cumul = (Tlong)0;
33898           cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
33899         }
33900       } break;
33901       case 'c' : {
33902         const ulongT whd = (ulongT)_width*_height*_depth;
33903         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_spectrum>=512 && _width*_height*_depth>=16))
33904         cimg_forXYZ(*this,x,y,z) {
33905           T *ptrd = data(x,y,z,0);
33906           Tlong cumul = (Tlong)0;
33907           cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
33908         }
33909       } break;
33910       default : { // Global cumulation.
33911         Tlong cumul = (Tlong)0;
33912         cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
33913       }
33914       }
33915       return *this;
33916     }
33917 
33918     //! Cumulate image values, optionally along specified axis \newinstance.
33919     CImg<Tlong> get_cumulate(const char axis=0) const {
33920       return CImg<Tlong>(*this,false).cumulate(axis);
33921     }
33922 
33923     //! Cumulate image values, along specified axes.
33924     /**
33925        \param axes Cumulation axes, as a C-string.
33926        \note \c axes may contains multiple characters, e.g. \c "xyz"
33927     **/
33928     CImg<T>& cumulate(const char *const axes) {
33929       for (const char *s = axes; *s; ++s) cumulate(*s);
33930       return *this;
33931     }
33932 
33933     //! Cumulate image values, along specified axes \newinstance.
33934     CImg<Tlong> get_cumulate(const char *const axes) const {
33935       return CImg<Tlong>(*this,false).cumulate(axes);
33936     }
33937 
33938     //! Erode image by a structuring element.
33939     /**
33940        \param kernel Structuring element.
33941        \param boundary_conditions Boundary conditions.
33942        \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
33943     **/
33944     template<typename t>
33945     CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true,
33946                    const bool is_real=false) {
33947       if (is_empty() || !kernel) return *this;
33948       return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
33949     }
33950 
33951     //! Erode image by a structuring element \newinstance.
33952     template<typename t>
33953     CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true,
33954                              const bool is_real=false) const {
33955       if (is_empty() || !kernel) return *this;
33956       if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
33957       typedef _cimg_Tt Tt;
33958       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33959       const int
33960         mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
33961         mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
33962         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
33963       const bool
33964         is_inner_parallel = _width*_height*_depth>=32768,
33965         is_outer_parallel = res.size()>=32768;
33966       cimg::unused(is_inner_parallel,is_outer_parallel);
33967       _cimg_abort_init_omp;
33968       cimg_abort_init;
33969       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
33970       cimg_forC(res,c) _cimg_abort_try_omp {
33971         cimg_abort_test;
33972         const CImg<T> img = get_shared_channel(c%_spectrum);
33973         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
33974         if (is_real) { // Real erosion
33975           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33976           for (int z = mz1; z<mze; ++z)
33977             for (int y = my1; y<mye; ++y)
33978               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33979                 cimg_abort_test2;
33980                 Tt min_val = cimg::type<Tt>::max();
33981                 for (int zm = -mz1; zm<=mz2; ++zm)
33982                   for (int ym = -my1; ym<=my2; ++ym)
33983                     for (int xm = -mx1; xm<=mx2; ++xm) {
33984                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
33985                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
33986                       if (cval<min_val) min_val = cval;
33987                     }
33988                 res(x,y,z,c) = min_val;
33989               } _cimg_abort_catch_omp2
33990           if (boundary_conditions)
33991             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33992             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33993               cimg_abort_test2;
33994               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33995                 Tt min_val = cimg::type<Tt>::max();
33996                 for (int zm = -mz1; zm<=mz2; ++zm)
33997                   for (int ym = -my1; ym<=my2; ++ym)
33998                     for (int xm = -mx1; xm<=mx2; ++xm) {
33999                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
34000                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval);
34001                       if (cval<min_val) min_val = cval;
34002                     }
34003                 res(x,y,z,c) = min_val;
34004               }
34005             } _cimg_abort_catch_omp2
34006           else
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,0,(T)0) - 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 
34023         } else { // Binary erosion
34024           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34025           for (int z = mz1; z<mze; ++z)
34026             for (int y = my1; y<mye; ++y)
34027               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34028                 cimg_abort_test2;
34029                 Tt min_val = cimg::type<Tt>::max();
34030                 for (int zm = -mz1; zm<=mz2; ++zm)
34031                   for (int ym = -my1; ym<=my2; ++ym)
34032                     for (int xm = -mx1; xm<=mx2; ++xm)
34033                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
34034                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
34035                         if (cval<min_val) min_val = cval;
34036                       }
34037                 res(x,y,z,c) = min_val;
34038               } _cimg_abort_catch_omp2
34039           if (boundary_conditions)
34040             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34041             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34042               cimg_abort_test2;
34043               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34044                 Tt min_val = cimg::type<Tt>::max();
34045                 for (int zm = -mz1; zm<=mz2; ++zm)
34046                   for (int ym = -my1; ym<=my2; ++ym)
34047                     for (int xm = -mx1; xm<=mx2; ++xm)
34048                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
34049                         const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
34050                         if (cval<min_val) min_val = cval;
34051                       }
34052                 res(x,y,z,c) = min_val;
34053               }
34054             } _cimg_abort_catch_omp2
34055           else
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,0,(T)0);
34066                         if (cval<min_val) min_val = cval;
34067                       }
34068                 res(x,y,z,c) = min_val;
34069               }
34070             } _cimg_abort_catch_omp2
34071         }
34072       } _cimg_abort_catch_omp
34073       cimg_abort_test;
34074       return res;
34075     }
34076 
34077     //! Erode image by a rectangular structuring element of specified size.
34078     /**
34079        \param sx Width of the structuring element.
34080        \param sy Height of the structuring element.
34081        \param sz Depth of the structuring element.
34082     **/
34083     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
34084       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
34085       if (sx>1 && _width>1) { // Along X-axis.
34086         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;
34087         CImg<T> buf(L);
34088         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34089         cimg_forYZC(*this,y,z,c) {
34090           T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
34091           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34092           T cur = *ptrs; ptrs+=off; bool is_first = true;
34093           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34094             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
34095           *(ptrd++) = cur;
34096           if (ptrs>=ptrse) {
34097             T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34098           } else {
34099             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34100               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34101               *(ptrd++) = cur;
34102             }
34103             for (int p = L - s - 1; p>0; --p) {
34104               const T val = *ptrs; ptrs+=off;
34105               if (is_first) {
34106                 const T *nptrs = ptrs - off; cur = val;
34107                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34108                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34109               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34110               *(ptrd++) = cur;
34111             }
34112             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34113             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34114               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34115             }
34116             *(ptrd--) = cur;
34117             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34118               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34119             }
34120             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34121           }
34122         }
34123       }
34124 
34125       if (sy>1 && _height>1) { // Along Y-axis.
34126         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
34127           s2 = _s2>L?L:_s2;
34128         CImg<T> buf(L);
34129         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34130         cimg_forXZC(*this,x,z,c) {
34131           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34132           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34133           T cur = *ptrs; ptrs+=off; bool is_first = true;
34134           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34135             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34136           }
34137           *(ptrd++) = cur;
34138           if (ptrs>=ptrse) {
34139             T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34140           } else {
34141             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34142               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34143               *(ptrd++) = cur;
34144             }
34145             for (int p = L - s - 1; p>0; --p) {
34146               const T val = *ptrs; ptrs+=off;
34147               if (is_first) {
34148                 const T *nptrs = ptrs - off; cur = val;
34149                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34150                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34151               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34152               *(ptrd++) = cur;
34153             }
34154             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34155             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34156               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34157             }
34158             *(ptrd--) = cur;
34159             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34160               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34161             }
34162             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34163           }
34164         }
34165       }
34166 
34167       if (sz>1 && _depth>1) { // Along Z-axis.
34168         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
34169           s2 = _s2>L?L:_s2;
34170         CImg<T> buf(L);
34171         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34172         cimg_forXYC(*this,x,y,c) {
34173           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34174           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34175           T cur = *ptrs; ptrs+=off; bool is_first = true;
34176           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34177             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34178           }
34179           *(ptrd++) = cur;
34180           if (ptrs>=ptrse) {
34181             T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34182           } else {
34183             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34184               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34185               *(ptrd++) = cur;
34186             }
34187             for (int p = L - s - 1; p>0; --p) {
34188               const T val = *ptrs; ptrs+=off;
34189               if (is_first) {
34190                 const T *nptrs = ptrs - off; cur = val;
34191                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34192                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34193               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34194               *(ptrd++) = cur;
34195             }
34196             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34197             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34198               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34199             }
34200             *(ptrd--) = cur;
34201             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34202               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34203             }
34204             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34205           }
34206         }
34207       }
34208       return *this;
34209     }
34210 
34211     //! Erode image by a rectangular structuring element of specified size \newinstance.
34212     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
34213       return (+*this).erode(sx,sy,sz);
34214     }
34215 
34216     //! Erode the image by a square structuring element of specified size.
34217     /**
34218        \param s Size of the structuring element.
34219     **/
34220     CImg<T>& erode(const unsigned int s) {
34221       return erode(s,s,s);
34222     }
34223 
34224     //! Erode the image by a square structuring element of specified size \newinstance.
34225     CImg<T> get_erode(const unsigned int s) const {
34226       return (+*this).erode(s);
34227     }
34228 
34229     //! Dilate image by a structuring element.
34230     /**
34231        \param kernel Structuring element.
34232        \param boundary_conditions Boundary conditions.
34233        \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
34234     **/
34235     template<typename t>
34236     CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
34237                     const bool is_real=false) {
34238       if (is_empty() || !kernel) return *this;
34239       return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
34240     }
34241 
34242     //! Dilate image by a structuring element \newinstance.
34243     template<typename t>
34244     CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
34245                               const bool is_real=false) const {
34246       if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
34247       typedef _cimg_Tt Tt;
34248       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
34249       const int
34250         mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
34251         mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
34252         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
34253       const bool
34254         is_inner_parallel = _width*_height*_depth>=32768,
34255         is_outer_parallel = res.size()>=32768;
34256       cimg::unused(is_inner_parallel,is_outer_parallel);
34257       _cimg_abort_init_omp;
34258       cimg_abort_init;
34259       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
34260       cimg_forC(res,c) _cimg_abort_try_omp {
34261         cimg_abort_test;
34262         const CImg<T> img = get_shared_channel(c%_spectrum);
34263         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
34264         if (is_real) { // Real dilation
34265           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34266           for (int z = mz1; z<mze; ++z)
34267             for (int y = my1; y<mye; ++y)
34268               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34269                 cimg_abort_test2;
34270                 Tt max_val = cimg::type<Tt>::min();
34271                 for (int zm = -mz1; zm<=mz2; ++zm)
34272                   for (int ym = -my1; ym<=my2; ++ym)
34273                     for (int xm = -mx1; xm<=mx2; ++xm) {
34274                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
34275                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
34276                       if (cval>max_val) max_val = cval;
34277                     }
34278                 res(x,y,z,c) = max_val;
34279               } _cimg_abort_catch_omp2
34280           if (boundary_conditions)
34281             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34282             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34283               cimg_abort_test2;
34284               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34285                 Tt max_val = cimg::type<Tt>::min();
34286                 for (int zm = -mz1; zm<=mz2; ++zm)
34287                   for (int ym = -my1; ym<=my2; ++ym)
34288                     for (int xm = -mx1; xm<=mx2; ++xm) {
34289                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
34290                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval);
34291                       if (cval>max_val) max_val = cval;
34292                     }
34293                 res(x,y,z,c) = max_val;
34294               }
34295             } _cimg_abort_catch_omp2
34296           else
34297             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34298             cimg_forYZ(*this,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,0,(T)0) + 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 { // Binary dilation
34313           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34314           for (int z = mz1; z<mze; ++z)
34315             for (int y = my1; y<mye; ++y)
34316               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34317                 cimg_abort_test2;
34318                 Tt max_val = cimg::type<Tt>::min();
34319                 for (int zm = -mz1; zm<=mz2; ++zm)
34320                   for (int ym = -my1; ym<=my2; ++ym)
34321                     for (int xm = -mx1; xm<=mx2; ++xm)
34322                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
34323                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
34324                         if (cval>max_val) max_val = cval;
34325                       }
34326                 res(x,y,z,c) = max_val;
34327               } _cimg_abort_catch_omp2
34328           if (boundary_conditions)
34329             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34330             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34331               cimg_abort_test2;
34332               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34333                 Tt max_val = cimg::type<Tt>::min();
34334                 for (int zm = -mz1; zm<=mz2; ++zm)
34335                   for (int ym = -my1; ym<=my2; ++ym)
34336                     for (int xm = -mx1; xm<=mx2; ++xm)
34337                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
34338                         const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
34339                         if (cval>max_val) max_val = cval;
34340                       }
34341                 res(x,y,z,c) = max_val;
34342               }
34343             } _cimg_abort_catch_omp2
34344           else
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,0,(T)0);
34355                         if (cval>max_val) max_val = cval;
34356                       }
34357                 res(x,y,z,c) = max_val;
34358               }
34359             } _cimg_abort_catch_omp2
34360         }
34361       } _cimg_abort_catch_omp
34362       cimg_abort_test;
34363       return res;
34364     }
34365 
34366     //! Dilate image by a rectangular structuring element of specified size.
34367     /**
34368        \param sx Width of the structuring element.
34369        \param sy Height of the structuring element.
34370        \param sz Depth of the structuring element.
34371     **/
34372     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
34373       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
34374       if (sx>1 && _width>1) { // Along X-axis.
34375         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;
34376         CImg<T> buf(L);
34377         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34378         cimg_forYZC(*this,y,z,c) {
34379           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34380           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34381           T cur = *ptrs; ptrs+=off; bool is_first = true;
34382           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34383             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34384           }
34385           *(ptrd++) = cur;
34386           if (ptrs>=ptrse) {
34387             T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34388           } else {
34389             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34390               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34391               *(ptrd++) = cur;
34392             }
34393             for (int p = L - s - 1; p>0; --p) {
34394               const T val = *ptrs; ptrs+=off;
34395               if (is_first) {
34396                 const T *nptrs = ptrs - off; cur = val;
34397                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34398                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34399               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34400               *(ptrd++) = cur;
34401             }
34402             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34403             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34404               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34405             }
34406             *(ptrd--) = cur;
34407             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34408               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34409             }
34410             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34411           }
34412         }
34413       }
34414 
34415       if (sy>1 && _height>1) { // Along Y-axis.
34416         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
34417           s2 = _s2>L?L:_s2;
34418         CImg<T> buf(L);
34419         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34420         cimg_forXZC(*this,x,z,c) {
34421           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34422           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34423           T cur = *ptrs; ptrs+=off; bool is_first = true;
34424           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34425             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34426           }
34427           *(ptrd++) = cur;
34428           if (ptrs>=ptrse) {
34429             T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34430           } else {
34431             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34432               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34433               *(ptrd++) = cur;
34434             }
34435             for (int p = L - s - 1; p>0; --p) {
34436               const T val = *ptrs; ptrs+=off;
34437               if (is_first) {
34438                 const T *nptrs = ptrs - off; cur = val;
34439                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34440                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34441               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34442               *(ptrd++) = cur;
34443             }
34444             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34445             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34446               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34447             }
34448             *(ptrd--) = cur;
34449             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34450               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34451             }
34452             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34453           }
34454         }
34455       }
34456 
34457       if (sz>1 && _depth>1) { // Along Z-axis.
34458         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
34459           s2 = _s2>L?L:_s2;
34460         CImg<T> buf(L);
34461         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34462         cimg_forXYC(*this,x,y,c) {
34463           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34464           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34465           T cur = *ptrs; ptrs+=off; bool is_first = true;
34466           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34467             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34468           }
34469           *(ptrd++) = cur;
34470           if (ptrs>=ptrse) {
34471             T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34472           } else {
34473             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34474               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34475               *(ptrd++) = cur;
34476             }
34477             for (int p = L - s - 1; p>0; --p) {
34478               const T val = *ptrs; ptrs+=off;
34479               if (is_first) {
34480                 const T *nptrs = ptrs - off; cur = val;
34481                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34482                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34483               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34484               *(ptrd++) = cur;
34485             }
34486             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34487             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34488               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34489             }
34490             *(ptrd--) = cur;
34491             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34492               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34493             }
34494             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34495           }
34496         }
34497       }
34498       return *this;
34499     }
34500 
34501     //! Dilate image by a rectangular structuring element of specified size \newinstance.
34502     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
34503       return (+*this).dilate(sx,sy,sz);
34504     }
34505 
34506     //! Dilate image by a square structuring element of specified size.
34507     /**
34508        \param s Size of the structuring element.
34509     **/
34510     CImg<T>& dilate(const unsigned int s) {
34511       return dilate(s,s,s);
34512     }
34513 
34514     //! Dilate image by a square structuring element of specified size \newinstance.
34515     CImg<T> get_dilate(const unsigned int s) const {
34516       return (+*this).dilate(s);
34517     }
34518 
34519     //! Compute watershed transform.
34520     /**
34521        \param priority Priority map.
34522        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
34523        in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
34524        \note Non-zero values of the instance instance are propagated to zero-valued ones according to
34525        specified the priority map.
34526     **/
34527     template<typename t>
34528     CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
34529 #define _cimg_watershed_init(cond,X,Y,Z) \
34530       if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
34531 
34532 #define _cimg_watershed_propagate(cond,X,Y,Z) \
34533       if (cond) { \
34534         if ((*this)(X,Y,Z)) { \
34535           ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
34536           d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
34537           if (d<dmin) { dmin = d; nmin = ns; label = (*this)(xs,ys,zs); } \
34538         } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
34539       }
34540 
34541       if (is_empty()) return *this;
34542       if (!is_sameXYZ(priority))
34543         throw CImgArgumentException(_cimg_instance
34544                                     "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
34545                                     "have different dimensions.",
34546                                     cimg_instance,
34547                                     priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
34548       if (_spectrum!=1) {
34549         cimg_forC(*this,c)
34550           get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
34551         return *this;
34552       }
34553 
34554       CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
34555       CImg<typename cimg::superset2<T,t,int>::type> Q;
34556       unsigned int sizeQ = 0;
34557       int px, nx, py, ny, pz, nz;
34558       bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
34559       const bool is_3d = _depth>1;
34560 
34561       // Find seed points and insert them in priority queue.
34562       unsigned int nb_seeds = 0;
34563       const T *ptrs = _data;
34564       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3d version
34565         if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
34566         seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
34567         px = x - 1; nx = x + 1;
34568         py = y - 1; ny = y + 1;
34569         pz = z - 1; nz = z + 1;
34570         is_px = px>=0; is_nx = nx<width();
34571         is_py = py>=0; is_ny = ny<height();
34572         is_pz = pz>=0; is_nz = nz<depth();
34573         _cimg_watershed_init(is_px,px,y,z);
34574         _cimg_watershed_init(is_nx,nx,y,z);
34575         _cimg_watershed_init(is_py,x,py,z);
34576         _cimg_watershed_init(is_ny,x,ny,z);
34577         if (is_3d) {
34578           _cimg_watershed_init(is_pz,x,y,pz);
34579           _cimg_watershed_init(is_nz,x,y,nz);
34580         }
34581         if (is_high_connectivity) {
34582           _cimg_watershed_init(is_px && is_py,px,py,z);
34583           _cimg_watershed_init(is_nx && is_py,nx,py,z);
34584           _cimg_watershed_init(is_px && is_ny,px,ny,z);
34585           _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
34586           if (is_3d) {
34587             _cimg_watershed_init(is_px && is_pz,px,y,pz);
34588             _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
34589             _cimg_watershed_init(is_px && is_nz,px,y,nz);
34590             _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
34591             _cimg_watershed_init(is_py && is_pz,x,py,pz);
34592             _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
34593             _cimg_watershed_init(is_py && is_nz,x,py,nz);
34594             _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
34595             _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
34596             _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
34597             _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
34598             _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
34599             _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
34600             _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
34601             _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
34602             _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
34603           }
34604         }
34605         labels(x,y,z) = nb_seeds;
34606       }
34607 
34608       // Start watershed computation.
34609       while (sizeQ) {
34610 
34611         // Get and remove point with maximal priority from the queue.
34612         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
34613         const unsigned int n = labels(x,y,z);
34614         px = x - 1; nx = x + 1;
34615         py = y - 1; ny = y + 1;
34616         pz = z - 1; nz = z + 1;
34617         is_px = px>=0; is_nx = nx<width();
34618         is_py = py>=0; is_ny = ny<height();
34619         is_pz = pz>=0; is_nz = nz<depth();
34620 
34621         // Check labels of the neighbors.
34622         Q._priority_queue_remove(sizeQ);
34623 
34624         unsigned int xs, ys, zs, ns, nmin = 0;
34625         float d, dmin = cimg::type<float>::inf();
34626         T label = (T)0;
34627         _cimg_watershed_propagate(is_px,px,y,z);
34628         _cimg_watershed_propagate(is_nx,nx,y,z);
34629         _cimg_watershed_propagate(is_py,x,py,z);
34630         _cimg_watershed_propagate(is_ny,x,ny,z);
34631         if (is_3d) {
34632           _cimg_watershed_propagate(is_pz,x,y,pz);
34633           _cimg_watershed_propagate(is_nz,x,y,nz);
34634         }
34635         if (is_high_connectivity) {
34636           _cimg_watershed_propagate(is_px && is_py,px,py,z);
34637           _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
34638           _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
34639           _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
34640           if (is_3d) {
34641             _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
34642             _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
34643             _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
34644             _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
34645             _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
34646             _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
34647             _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
34648             _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
34649             _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
34650             _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
34651             _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
34652             _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
34653             _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
34654             _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
34655             _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
34656             _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
34657           }
34658         }
34659         (*this)(x,y,z) = label;
34660         labels(x,y,z) = ++nmin;
34661       }
34662       return *this;
34663     }
34664 
34665     //! Compute watershed transform \newinstance.
34666     template<typename t>
34667     CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
34668       return (+*this).watershed(priority,is_high_connectivity);
34669     }
34670 
34671     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
34672     template<typename tq, typename tv>
34673     bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
34674                                 const unsigned int x, const unsigned int y, const unsigned int z,
34675                                 const unsigned int n=1) {
34676       if (is_queued(x,y,z)) return false;
34677       is_queued(x,y,z) = (tq)n;
34678       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
34679       (*this)(siz - 1,0) = (T)value;
34680       (*this)(siz - 1,1) = (T)x;
34681       (*this)(siz - 1,2) = (T)y;
34682       (*this)(siz - 1,3) = (T)z;
34683       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
34684         cimg::swap((*this)(pos,0),(*this)(par,0));
34685         cimg::swap((*this)(pos,1),(*this)(par,1));
34686         cimg::swap((*this)(pos,2),(*this)(par,2));
34687         cimg::swap((*this)(pos,3),(*this)(par,3));
34688       }
34689       return true;
34690     }
34691 
34692     CImg<T>& _priority_queue_remove(unsigned int& siz) {
34693       (*this)(0,0) = (*this)(--siz,0);
34694       (*this)(0,1) = (*this)(siz,1);
34695       (*this)(0,2) = (*this)(siz,2);
34696       (*this)(0,3) = (*this)(siz,3);
34697       const float value = (*this)(0,0);
34698       for (unsigned int pos = 0, left = 0, right = 0;
34699            ((right=2*(pos + 1),(left=right - 1))<siz && value<(*this)(left,0)) ||
34700              (right<siz && value<(*this)(right,0));) {
34701         if (right<siz) {
34702           if ((*this)(left,0)>(*this)(right,0)) {
34703             cimg::swap((*this)(pos,0),(*this)(left,0));
34704             cimg::swap((*this)(pos,1),(*this)(left,1));
34705             cimg::swap((*this)(pos,2),(*this)(left,2));
34706             cimg::swap((*this)(pos,3),(*this)(left,3));
34707             pos = left;
34708           } else {
34709             cimg::swap((*this)(pos,0),(*this)(right,0));
34710             cimg::swap((*this)(pos,1),(*this)(right,1));
34711             cimg::swap((*this)(pos,2),(*this)(right,2));
34712             cimg::swap((*this)(pos,3),(*this)(right,3));
34713             pos = right;
34714           }
34715         } else {
34716           cimg::swap((*this)(pos,0),(*this)(left,0));
34717           cimg::swap((*this)(pos,1),(*this)(left,1));
34718           cimg::swap((*this)(pos,2),(*this)(left,2));
34719           cimg::swap((*this)(pos,3),(*this)(left,3));
34720           pos = left;
34721         }
34722       }
34723       return *this;
34724     }
34725 
34726     //! Apply recursive Deriche filter.
34727     /**
34728        \param sigma Standard deviation of the filter.
34729        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
34730        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
34731        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
34732     **/
34733     CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
34734                      const bool boundary_conditions=true) {
34735 #define _cimg_deriche_apply \
34736   CImg<Tfloat> Y(N); \
34737   Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
34738   T xp = (T)0; \
34739   if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
34740   for (int m = 0; m<N; ++m) { \
34741     const T xc = *ptrX; ptrX+=off; \
34742     const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
34743     xp = xc; yb = yp; yp = yc; \
34744   } \
34745   T xn = (T)0, xa = (T)0; \
34746   Tfloat yn = 0, ya = 0; \
34747   if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
34748   for (int n = N - 1; n>=0; --n) { \
34749     const T xc = *(ptrX-=off); \
34750     const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
34751     xa = xn; xn = xc; ya = yn; yn = yc; \
34752     *ptrX = (T)(*(--ptrY)+yc); \
34753   }
34754       const char naxis = cimg::lowercase(axis);
34755       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
34756       if (is_empty() || (nsigma<0.1f && !order)) return *this;
34757       const float
34758         nnsigma = nsigma<0.1f?0.1f:nsigma,
34759         alpha = 1.695f/nnsigma,
34760         ema = (float)std::exp(-alpha),
34761         ema2 = (float)std::exp(-2*alpha),
34762         b1 = -2*ema,
34763         b2 = ema2;
34764       float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
34765       switch (order) {
34766       case 0 : {
34767         const float k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
34768         a0 = k;
34769         a1 = k*(alpha - 1)*ema;
34770         a2 = k*(alpha + 1)*ema;
34771         a3 = -k*ema2;
34772       } break;
34773       case 1 : {
34774         const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
34775 	a0 = a3 = 0;
34776 	a1 = k*ema;
34777         a2 = -a1;
34778       } break;
34779       case 2 : {
34780         const float
34781           ea = (float)std::exp(-alpha),
34782           k = -(ema2 - 1)/(2*alpha*ema),
34783           kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
34784         a0 = kn;
34785         a1 = -kn*(1 + k*alpha)*ema;
34786         a2 = kn*(1 - k*alpha)*ema;
34787         a3 = -kn*ema2;
34788       } break;
34789       default :
34790         throw CImgArgumentException(_cimg_instance
34791                                     "deriche(): Invalid specified filter order %u "
34792                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
34793                                     cimg_instance,
34794                                     order);
34795       }
34796       coefp = (a0 + a1)/(1 + b1 + b2);
34797       coefn = (a2 + a3)/(1 + b1 + b2);
34798       switch (naxis) {
34799       case 'x' : {
34800         const int N = width();
34801         const ulongT off = 1U;
34802         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34803         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
34804       } break;
34805       case 'y' : {
34806         const int N = height();
34807         const ulongT off = (ulongT)_width;
34808         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34809         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
34810       } break;
34811       case 'z' : {
34812         const int N = depth();
34813         const ulongT off = (ulongT)_width*_height;
34814         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34815         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
34816       } break;
34817       default : {
34818         const int N = spectrum();
34819         const ulongT off = (ulongT)_width*_height*_depth;
34820         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34821         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
34822       }
34823       }
34824       return *this;
34825     }
34826 
34827     //! Apply recursive Deriche filter \newinstance.
34828     CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
34829                              const bool boundary_conditions=true) const {
34830       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
34831     }
34832 
34833     // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
34834     /*
34835        \param ptr the pointer of the data
34836        \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
34837        \param N size of the data
34838        \param off the offset between two data point
34839        \param order the order of the filter 0 (smoothing), 1st derivtive, 2nd derivative, 3rd derivative
34840        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
34841        \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
34842     */
34843     static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
34844 				      const unsigned int order, const bool boundary_conditions) {
34845       double val[4] = { 0 };  // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
34846       const double
34847 	sumsq = filter[0], sum = sumsq * sumsq,
34848 	a1 = filter[1], a2 = filter[2], a3 = filter[3],
34849 	scaleM = 1.0 / ( (1.0 + a1 - a2 + a3) * (1.0 - a1 - a2 - a3) * (1.0 + a2 + (a1 - a3) * a3) );
34850       double M[9]; // Triggs matrix
34851       M[0] = scaleM * (-a3 * a1 + 1.0 - a3 * a3 - a2);
34852       M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
34853       M[2] = scaleM * a3 * (a1 + a3 * a2);
34854       M[3] = scaleM * (a1 + a3 * a2);
34855       M[4] = -scaleM * (a2 - 1.0) * (a2 + a3 * a1);
34856       M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.0);
34857       M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
34858       M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
34859       M[8] = scaleM * a3 * (a1 + a3 * a2);
34860       switch (order) {
34861       case 0 : {
34862 	const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
34863 	for (int pass = 0; pass<2; ++pass) {
34864 	  if (!pass) {
34865 	    for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
34866 	  } else {
34867 	    // apply Triggs boundary conditions
34868 	    const double
34869 	      uplus = iplus/(1.0 - a1 - a2 - a3), vplus = uplus/(1.0 - a1 - a2 - a3),
34870 	      unp  = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
34871 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
34872 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
34873 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
34874 	    *data = (T)val[0];
34875 	    data -= off;
34876 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34877 	  }
34878 	  for (int n = pass; n<N; ++n) {
34879 	    val[0] = (*data);
34880 	    if (pass) val[0] *= sum;
34881 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34882 	    *data = (T)val[0];
34883 	    if (!pass) data += off; else data -= off;
34884 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34885 	  }
34886 	  if (!pass) data -= off;
34887 	}
34888       } break;
34889       case 1 : {
34890 	double x[3]; // [front,center,back]
34891 	for (int pass = 0; pass<2; ++pass) {
34892 	  if (!pass) {
34893 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34894 	    for (int k = 0; k<4; ++k) val[k] = 0;
34895 	  } else {
34896 	    // apply Triggs boundary conditions
34897 	    const double
34898 	      unp  = val[1], unp1 = val[2], unp2 = val[3];
34899 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34900 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34901 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34902 	    *data = (T)val[0];
34903 	    data -= off;
34904 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34905 	  }
34906 	  for (int n = pass; n<N - 1; ++n) {
34907 	    if (!pass) {
34908 	      x[0] = *(data + off);
34909 	      val[0] = 0.5f * (x[0] - x[2]);
34910 	    } else val[0] = (*data) * sum;
34911 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34912 	    *data = (T)val[0];
34913 	    if (!pass) {
34914 	      data += off;
34915 	      for (int k = 2; k>0; --k) x[k] = x[k - 1];
34916 	    } else { data-=off;}
34917 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34918 	  }
34919 	  *data = (T)0;
34920 	}
34921       } break;
34922       case 2: {
34923 	double x[3]; // [front,center,back]
34924 	for (int pass = 0; pass<2; ++pass) {
34925 	  if (!pass) {
34926 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34927 	    for (int k = 0; k<4; ++k) val[k] = 0;
34928 	  } else {
34929 	    // apply Triggs boundary conditions
34930 	    const double
34931 	      unp  = val[1], unp1 = val[2], unp2 = val[3];
34932 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34933 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34934 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34935 	    *data = (T)val[0];
34936 	    data -= off;
34937 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34938 	  }
34939 	  for (int n = pass; n<N - 1; ++n) {
34940 	    if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
34941 	    else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
34942 	    for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
34943 	    *data = (T)val[0];
34944 	    if (!pass) data += off; else data -= off;
34945 	    for (int k = 2; k>0; --k) x[k] = x[k - 1];
34946 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34947 	  }
34948 	  *data = (T)0;
34949 	}
34950       } break;
34951       case 3: {
34952 	double x[3]; // [front,center,back]
34953 	for (int pass = 0; pass<2; ++pass) {
34954 	  if (!pass) {
34955 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34956 	    for (int k = 0; k<4; ++k) val[k] = 0;
34957 	  } else {
34958 	    // apply Triggs boundary conditions
34959 	    const double
34960 	      unp = val[1], unp1 = val[2], unp2 = val[3];
34961 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34962 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34963 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34964 	    *data = (T)val[0];
34965 	    data -= off;
34966 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34967 	  }
34968 	  for (int n = pass; n<N - 1; ++n) {
34969 	    if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
34970 	    else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
34971 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34972 	    *data = (T)val[0];
34973 	    if (!pass) data += off; else data -= off;
34974 	    for (int k = 2; k>0; --k) x[k] = x[k - 1];
34975 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34976 	  }
34977 	  *data = (T)0;
34978 	}
34979       } break;
34980       }
34981     }
34982 
34983     //! Van Vliet recursive Gaussian filter.
34984     /**
34985        \param sigma standard deviation of the Gaussian filter
34986        \param order the order of the filter 0,1,2,3
34987        \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
34988        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
34989        \note dirichlet boundary condition has a strange behavior
34990 
34991        I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
34992        IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
34993 
34994        (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
34995 
34996        Boundary conditions (only for order 0) using Triggs matrix, from
34997        B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
34998        recursive filtering. IEEE Trans. Signal Processing,
34999        vol. 54, pp. 2365-2367, 2006.
35000     **/
35001     CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
35002                       const bool boundary_conditions=true) {
35003       if (is_empty()) return *this;
35004       if (!cimg::type<T>::is_float())
35005         return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
35006       const char naxis = cimg::lowercase(axis);
35007       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
35008       if (is_empty() || (nsigma<0.5f && !order)) return *this;
35009       const double
35010 	nnsigma = nsigma<0.5f?0.5f:nsigma,
35011 	m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
35012         m1sq = m1 * m1, m2sq = m2 * m2,
35013 	q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
35014 	qsq = q * q,
35015 	scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
35016 	b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
35017 	b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
35018 	b3 = -qsq * q / scale,
35019 	B = ( m0 * (m1sq + m2sq) ) / scale;
35020       double filter[4];
35021       filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
35022       switch (naxis) {
35023       case 'x' : {
35024         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35025 	cimg_forYZC(*this,y,z,c)
35026 	  _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
35027       } break;
35028       case 'y' : {
35029         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35030 	cimg_forXZC(*this,x,z,c)
35031 	  _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
35032       } break;
35033       case 'z' : {
35034         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35035 	cimg_forXYC(*this,x,y,c)
35036 	  _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
35037 				order,boundary_conditions);
35038       } break;
35039       default : {
35040         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35041 	cimg_forXYZ(*this,x,y,z)
35042 	  _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
35043 				order,boundary_conditions);
35044       }
35045       }
35046       return *this;
35047     }
35048 
35049     //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
35050     CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
35051                               const bool boundary_conditions=true) const {
35052       return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
35053     }
35054 
35055     //! Blur image.
35056     /**
35057        \param sigma_x Standard deviation of the blur, along the X-axis.
35058        \param sigma_y Standard deviation of the blur, along the Y-axis.
35059        \param sigma_z Standard deviation of the blur, along the Z-axis.
35060        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
35061        \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
35062        \note
35063        - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur.
35064        - This is a recursive algorithm, not depending on the values of the standard deviations.
35065        \see deriche(), vanvliet().
35066     **/
35067     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
35068                   const bool boundary_conditions=true, const bool is_gaussian=false) {
35069       if (is_empty()) return *this;
35070       if (is_gaussian) {
35071         if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
35072         if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
35073         if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
35074       } else {
35075         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
35076         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
35077         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
35078       }
35079       return *this;
35080     }
35081 
35082     //! Blur image \newinstance.
35083     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
35084                           const bool boundary_conditions=true, const bool is_gaussian=false) const {
35085       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
35086     }
35087 
35088     //! Blur image isotropically.
35089     /**
35090        \param sigma Standard deviation of the blur.
35091        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
35092        \param is_gaussian Use a gaussian kernel (VanVliet) is set, a pseudo-gaussian (Deriche) otherwise.
35093        \see deriche(), vanvliet().
35094     **/
35095     CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) {
35096       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
35097       return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
35098     }
35099 
35100     //! Blur image isotropically \newinstance.
35101     CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const {
35102       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
35103     }
35104 
35105     //! Blur image anisotropically, directed by a field of diffusion tensors.
35106     /**
35107        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
35108        \param amplitude Amplitude of the smoothing.
35109        \param dl Spatial discretization.
35110        \param da Angular discretization.
35111        \param gauss_prec Precision of the diffusion process.
35112        \param interpolation_type Interpolation scheme.
35113          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
35114        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
35115     **/
35116     template<typename t>
35117     CImg<T>& blur_anisotropic(const CImg<t>& G,
35118                               const float amplitude=60, const float dl=0.8f, const float da=30,
35119                               const float gauss_prec=2, const unsigned int interpolation_type=0,
35120                               const bool is_fast_approx=1) {
35121 
35122       // Check arguments and init variables
35123       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
35124         throw CImgArgumentException(_cimg_instance
35125                                     "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
35126                                     cimg_instance,
35127                                     G._width,G._height,G._depth,G._spectrum,G._data);
35128 
35129       if (is_empty() || amplitude<=0 || dl<0) return *this;
35130       const bool is_3d = (G._spectrum==6);
35131       T val_min, val_max = max_min(val_min);
35132       _cimg_abort_init_omp;
35133       cimg_abort_init;
35134 
35135       if (da<=0) {  // Iterated oriented Laplacians
35136         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
35137         for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
35138           Tfloat *ptrd = velocity._data, veloc_max = 0;
35139           if (is_3d) // 3d version
35140             cimg_forC(*this,c) {
35141               cimg_abort_test;
35142               CImg_3x3x3(I,Tfloat);
35143               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
35144                 const Tfloat
35145                   ixx = Incc + Ipcc - 2*Iccc,
35146                   ixy = (Innc + Ippc - Inpc - Ipnc)/4,
35147                   ixz = (Incn + Ipcp - Incp - Ipcn)/4,
35148                   iyy = Icnc + Icpc - 2*Iccc,
35149                   iyz = (Icnn + Icpp - Icnp - Icpn)/4,
35150                   izz = Iccn + Iccp - 2*Iccc,
35151                   veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
35152                                    G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
35153                 *(ptrd++) = veloc;
35154                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
35155               }
35156             }
35157           else // 2d version
35158             cimg_forZC(*this,z,c) {
35159               cimg_abort_test;
35160               CImg_3x3(I,Tfloat);
35161               cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
35162                 const Tfloat
35163                   ixx = Inc + Ipc - 2*Icc,
35164                   ixy = (Inn + Ipp - Inp - Ipn)/4,
35165                   iyy = Icn + Icp - 2*Icc,
35166                   veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
35167                 *(ptrd++) = veloc;
35168                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
35169               }
35170             }
35171           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
35172         }
35173       } else { // LIC-based smoothing.
35174         const ulongT whd = (ulongT)_width*_height*_depth;
35175         const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
35176         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
35177         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
35178         int N = 0;
35179         if (is_3d) { // 3d version
35180           for (float phi = cimg::mod(180.0f,da)/2.0f; phi<=180; phi+=da) {
35181             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
35182               da2 = datmp<1?360.0f:datmp;
35183             for (float theta = 0; theta<360; (theta+=da2),++N) {
35184               const float
35185                 thetar = (float)(theta*cimg::PI/180),
35186                 vx = (float)(std::cos(thetar)*std::cos(phir)),
35187                 vy = (float)(std::sin(thetar)*std::cos(phir)),
35188                 vz = (float)std::sin(phir);
35189               const t
35190                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
35191                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
35192               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);
35193               cimg_forXYZ(G,xg,yg,zg) {
35194                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
35195                 const float
35196                   u = (float)(a*vx + b*vy + c*vz),
35197                   v = (float)(b*vx + d*vy + e*vz),
35198                   w = (float)(c*vx + e*vy + f*vz),
35199                   n = 1e-5f + cimg::hypot(u,v,w),
35200                   dln = dl/n;
35201                 *(pd0++) = (Tfloat)(u*dln);
35202                 *(pd1++) = (Tfloat)(v*dln);
35203                 *(pd2++) = (Tfloat)(w*dln);
35204                 *(pd3++) = (Tfloat)n;
35205               }
35206 
35207               cimg_abort_test;
35208               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=2)
35209                                  firstprivate(val))
35210               cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
35211                 cimg_abort_test2;
35212                 cimg_forX(*this,x) {
35213                   val.fill(0);
35214                   const float
35215                     n = (float)W(x,y,z,3),
35216                     fsigma = (float)(n*sqrt2amplitude),
35217                     fsigma2 = 2*fsigma*fsigma,
35218                     length = gauss_prec*fsigma;
35219                   float
35220                     S = 0,
35221                     X = (float)x,
35222                     Y = (float)y,
35223                     Z = (float)z;
35224                   switch (interpolation_type) {
35225                   case 0 : { // Nearest neighbor
35226                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35227                       const int
35228                         cx = (int)(X + 0.5f),
35229                         cy = (int)(Y + 0.5f),
35230                         cz = (int)(Z + 0.5f);
35231                       const float
35232                         u = (float)W(cx,cy,cz,0),
35233                         v = (float)W(cx,cy,cz,1),
35234                         w = (float)W(cx,cy,cz,2);
35235                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
35236                       else {
35237                         const float coef = (float)std::exp(-l*l/fsigma2);
35238                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
35239                         S+=coef;
35240                       }
35241                       X+=u; Y+=v; Z+=w;
35242                     }
35243                   } break;
35244                   case 1 : { // Linear interpolation
35245                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35246                       const float
35247                         u = (float)(W._linear_atXYZ(X,Y,Z,0)),
35248                         v = (float)(W._linear_atXYZ(X,Y,Z,1)),
35249                         w = (float)(W._linear_atXYZ(X,Y,Z,2));
35250                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
35251                       else {
35252                         const float coef = (float)std::exp(-l*l/fsigma2);
35253                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
35254                         S+=coef;
35255                       }
35256                       X+=u; Y+=v; Z+=w;
35257                     }
35258                   } break;
35259                   default : { // 2nd order Runge Kutta
35260                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35261                       const float
35262                         u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
35263                         v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
35264                         w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
35265                         u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
35266                         v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
35267                         w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
35268                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
35269                       else {
35270                         const float coef = (float)std::exp(-l*l/fsigma2);
35271                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
35272                         S+=coef;
35273                       }
35274                       X+=u; Y+=v; Z+=w;
35275                     }
35276                   } break;
35277                   }
35278                   Tfloat *ptrd = res.data(x,y,z);
35279                   if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
35280                   else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
35281                 }
35282               } _cimg_abort_catch_omp2
35283             }
35284           }
35285         } else { // 2d LIC algorithm
35286           for (float theta = cimg::mod(360.0f,da)/2.0f; theta<360; (theta+=da),++N) {
35287             const float thetar = (float)(theta*cimg::PI/180),
35288               vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
35289             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
35290             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
35291             cimg_forXY(G,xg,yg) {
35292               const t a = *(pa++), b = *(pb++), c = *(pc++);
35293               const float
35294                 u = (float)(a*vx + b*vy),
35295                 v = (float)(b*vx + c*vy),
35296                 n = std::max(1e-5f,cimg::hypot(u,v)),
35297                 dln = dl/n;
35298               *(pd0++) = (Tfloat)(u*dln);
35299               *(pd1++) = (Tfloat)(v*dln);
35300               *(pd2++) = (Tfloat)n;
35301             }
35302 
35303             cimg_abort_test;
35304             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val))
35305             cimg_forY(*this,y) _cimg_abort_try_omp2 {
35306               cimg_abort_test2;
35307               cimg_forX(*this,x) {
35308                 val.fill(0);
35309                 const float
35310                   n = (float)W(x,y,0,2),
35311                   fsigma = (float)(n*sqrt2amplitude),
35312                   fsigma2 = 2*fsigma*fsigma,
35313                   length = gauss_prec*fsigma;
35314                 float
35315                   S = 0,
35316                   X = (float)x,
35317                   Y = (float)y;
35318                 switch (interpolation_type) {
35319                 case 0 : { // Nearest-neighbor
35320                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35321                     const int
35322                       cx = (int)(X + 0.5f),
35323                       cy = (int)(Y + 0.5f);
35324                     const float
35325                       u = (float)W(cx,cy,0,0),
35326                       v = (float)W(cx,cy,0,1);
35327                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
35328                     else {
35329                       const float coef = (float)std::exp(-l*l/fsigma2);
35330                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
35331                       S+=coef;
35332                     }
35333                     X+=u; Y+=v;
35334                   }
35335                 } break;
35336                 case 1 : { // Linear interpolation
35337                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35338                     const float
35339                       u = (float)(W._linear_atXY(X,Y,0,0)),
35340                       v = (float)(W._linear_atXY(X,Y,0,1));
35341                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
35342                     else {
35343                       const float coef = (float)std::exp(-l*l/fsigma2);
35344                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
35345                       S+=coef;
35346                     }
35347                     X+=u; Y+=v;
35348                   }
35349                 } break;
35350                 default : { // 2nd-order Runge-kutta interpolation
35351                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35352                     const float
35353                       u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
35354                       v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
35355                       u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
35356                       v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
35357                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
35358                     else {
35359                       const float coef = (float)std::exp(-l*l/fsigma2);
35360                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
35361                       S+=coef;
35362                     }
35363                     X+=u; Y+=v;
35364                   }
35365                 }
35366                 }
35367                 Tfloat *ptrd = res.data(x,y);
35368                 if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
35369                 else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
35370               }
35371             } _cimg_abort_catch_omp2
35372           }
35373         }
35374         const Tfloat *ptrs = res._data;
35375         cimg_for(*this,ptrd,T) {
35376           const Tfloat val = *(ptrs++)/N;
35377           *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val);
35378         }
35379       }
35380       cimg_abort_test;
35381       return *this;
35382     }
35383 
35384     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
35385     template<typename t>
35386     CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
35387                                       const float amplitude=60, const float dl=0.8f, const float da=30,
35388                                       const float gauss_prec=2, const unsigned int interpolation_type=0,
35389                                       const bool is_fast_approx=true) const {
35390       return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
35391     }
35392 
35393     //! Blur image anisotropically, in an edge-preserving way.
35394     /**
35395        \param amplitude Amplitude of the smoothing.
35396        \param sharpness Sharpness.
35397        \param anisotropy Anisotropy.
35398        \param alpha Standard deviation of the gradient blur.
35399        \param sigma Standard deviation of the structure tensor blur.
35400        \param dl Spatial discretization.
35401        \param da Angular discretization.
35402        \param gauss_prec Precision of the diffusion process.
35403        \param interpolation_type Interpolation scheme.
35404          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
35405        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
35406      **/
35407     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
35408                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
35409                               const float gauss_prec=2, const unsigned int interpolation_type=0,
35410                               const bool is_fast_approx=true) {
35411       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
35412                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
35413     }
35414 
35415     //! Blur image anisotropically, in an edge-preserving way \newinstance.
35416     CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
35417                                       const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
35418                                       const float da=30, const float gauss_prec=2,
35419                                       const unsigned int interpolation_type=0,
35420                                       const bool is_fast_approx=true) const {
35421       return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
35422                                                         interpolation_type,is_fast_approx);
35423     }
35424 
35425     //! Blur image, with the joint bilateral filter.
35426     /**
35427        \param guide Image used to model the smoothing weights.
35428        \param sigma_x Amount of blur along the X-axis.
35429        \param sigma_y Amount of blur along the Y-axis.
35430        \param sigma_z Amount of blur along the Z-axis.
35431        \param sigma_r Amount of blur along the value axis.
35432        \param sampling_x Amount of downsampling along the X-axis used for the approximation.
35433          Defaults (0) to sigma_x.
35434        \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
35435          Defaults (0) to sigma_y.
35436        \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
35437          Defaults (0) to sigma_z.
35438        \param sampling_r Amount of downsampling along the value axis used for the approximation.
35439          Defaults (0) to sigma_r.
35440        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
35441        (extended for 3d volumetric images).
35442        It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
35443     **/
35444     template<typename t>
35445     CImg<T>& blur_bilateral(const CImg<t>& guide,
35446                             const float sigma_x, const float sigma_y,
35447                             const float sigma_z, const float sigma_r,
35448                             const float sampling_x, const float sampling_y,
35449                             const float sampling_z, const float sampling_r) {
35450       if (!is_sameXYZ(guide))
35451         throw CImgArgumentException(_cimg_instance
35452                                     "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
35453                                     cimg_instance,
35454                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
35455       if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
35456       T edge_min, edge_max = guide.max_min(edge_min);
35457       if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
35458       const float
35459         edge_delta = (float)(edge_max - edge_min),
35460         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
35461         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
35462         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
35463         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*(edge_max - edge_min)/100,
35464         _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.0f),
35465         _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.0f),
35466         _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.0f),
35467         _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
35468         derived_sigma_x = _sigma_x / _sampling_x,
35469         derived_sigma_y = _sigma_y / _sampling_y,
35470         derived_sigma_z = _sigma_z / _sampling_z,
35471         derived_sigma_r = _sigma_r / _sampling_r;
35472       const int
35473         padding_x = (int)(2*derived_sigma_x) + 1,
35474         padding_y = (int)(2*derived_sigma_y) + 1,
35475         padding_z = (int)(2*derived_sigma_z) + 1,
35476         padding_r = (int)(2*derived_sigma_r) + 1;
35477       const unsigned int
35478         bx = (unsigned int)((_width  - 1)/_sampling_x + 1 + 2*padding_x),
35479         by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
35480         bz = (unsigned int)((_depth  - 1)/_sampling_z + 1 + 2*padding_z),
35481         br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
35482       if (bx>0 || by>0 || bz>0 || br>0) {
35483         const bool is_3d = (_depth>1);
35484         if (is_3d) { // 3d version of the algorithm
35485           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
35486           cimg_forC(*this,c) {
35487             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
35488             bgrid.fill(0); bgridw.fill(0);
35489             cimg_forXYZ(*this,x,y,z) {
35490               const T val = (*this)(x,y,z,c);
35491               const float edge = (float)_guide(x,y,z);
35492               const int
35493                 X = (int)cimg::round(x/_sampling_x) + padding_x,
35494                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
35495                 Z = (int)cimg::round(z/_sampling_z) + padding_z,
35496                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
35497               bgrid(X,Y,Z,R)+=(float)val;
35498               bgridw(X,Y,Z,R)+=1;
35499             }
35500             bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
35501             bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
35502 
35503             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(size()>=4096))
35504             cimg_forXYZ(*this,x,y,z) {
35505               const float edge = (float)_guide(x,y,z);
35506               const float
35507                 X = x/_sampling_x + padding_x,
35508                 Y = y/_sampling_y + padding_y,
35509                 Z = z/_sampling_z + padding_z,
35510                 R = (edge - edge_min)/_sampling_r + padding_r;
35511               const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
35512               (*this)(x,y,z,c) = (T)(bval0/bval1);
35513             }
35514           }
35515         } else { // 2d version of the algorithm
35516           CImg<floatT> bgrid(bx,by,br,2);
35517           cimg_forC(*this,c) {
35518             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
35519             bgrid.fill(0);
35520             cimg_forXY(*this,x,y) {
35521               const T val = (*this)(x,y,c);
35522               const float edge = (float)_guide(x,y);
35523               const int
35524                 X = (int)cimg::round(x/_sampling_x) + padding_x,
35525                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
35526                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
35527               bgrid(X,Y,R,0)+=(float)val;
35528               bgrid(X,Y,R,1)+=1;
35529             }
35530             bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
35531 
35532             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>=4096))
35533             cimg_forXY(*this,x,y) {
35534               const float edge = (float)_guide(x,y);
35535               const float
35536                 X = x/_sampling_x + padding_x,
35537                 Y = y/_sampling_y + padding_y,
35538                 R = (edge - edge_min)/_sampling_r + padding_r;
35539               const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
35540               (*this)(x,y,c) = (T)(bval0/bval1);
35541             }
35542           }
35543         }
35544       }
35545       return *this;
35546     }
35547 
35548     //! Blur image, with the joint bilateral filter \newinstance.
35549     template<typename t>
35550     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
35551                                     const float sigma_x, const float sigma_y,
35552                                     const float sigma_z, const float sigma_r,
35553                                     const float sampling_x, const float sampling_y,
35554                                     const float sampling_z, const float sampling_r) const {
35555       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
35556                                                       sampling_x,sampling_y,sampling_z,sampling_r);
35557     }
35558 
35559     //! Blur image using the joint bilateral filter.
35560     /**
35561        \param guide Image used to model the smoothing weights.
35562        \param sigma_s Amount of blur along the XYZ-axes.
35563        \param sigma_r Amount of blur along the value axis.
35564        \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
35565        \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
35566     **/
35567     template<typename t>
35568     CImg<T>& blur_bilateral(const CImg<t>& guide,
35569                             const float sigma_s, const float sigma_r,
35570                             const float sampling_s=0, const float sampling_r=0) {
35571       const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
35572       return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
35573     }
35574 
35575     //! Blur image using the bilateral filter \newinstance.
35576     template<typename t>
35577     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
35578                                     const float sigma_s, const float sigma_r,
35579                                     const float sampling_s=0, const float sampling_r=0) const {
35580       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
35581     }
35582 
35583     // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
35584     /*
35585       \param ptr the pointer of the data
35586       \param N size of the data
35587       \param boxsize Size of the box filter (can be subpixel).
35588       \param off the offset between two data point
35589       \param order the order of the filter 0 (smoothing), 1st derivtive and 2nd derivative.
35590       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
35591     */
35592     static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
35593                                      const int order, const bool boundary_conditions,
35594                                      const unsigned int nb_iter) {
35595       // Smooth.
35596       if (boxsize>1 && nb_iter) {
35597         const int w2 = (int)(boxsize - 1)/2;
35598         const unsigned int winsize = 2*w2 + 1U;
35599         const double frac = (boxsize - winsize)/2.;
35600         CImg<T> win(winsize);
35601         for (unsigned int iter = 0; iter<nb_iter; ++iter) {
35602           Tdouble sum = 0; // window sum
35603           for (int x = -w2; x<=w2; ++x) {
35604             win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x);
35605             sum+=win[x + w2];
35606           }
35607           int ifirst = 0, ilast = 2*w2;
35608           T
35609             prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1),
35610             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1);
35611           for (int x = 0; x < N - 1; ++x) {
35612             const double sum2 = sum + frac * (prev + next);
35613             ptr[x*off] = (T)(sum2/boxsize);
35614             prev = win[ifirst];
35615             sum-=prev;
35616             ifirst = (int)((ifirst + 1)%winsize);
35617             ilast = (int)((ilast + 1)%winsize);
35618             win[ilast] = next;
35619             sum+=next;
35620             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2);
35621           }
35622           const double sum2 = sum + frac * (prev + next);
35623           ptr[(N - 1)*off] = (T)(sum2/boxsize);
35624         }
35625       }
35626 
35627       // Derive.
35628       switch (order) {
35629       case 0 :
35630         break;
35631       case 1 : {
35632         Tfloat
35633           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
35634           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
35635           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
35636         for (int x = 0; x<N - 1; ++x) {
35637           ptr[x*off] = (T)((n-p)/2.0);
35638           p = c;
35639           c = n;
35640           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
35641         }
35642         ptr[(N - 1)*off] = (T)((n-p)/2.0);
35643       } break;
35644       case 2: {
35645         Tfloat
35646           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
35647           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
35648           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
35649         for (int x = 0; x<N - 1; ++x) {
35650           ptr[x*off] = (T)(n - 2*c + p);
35651           p = c;
35652           c = n;
35653           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
35654         }
35655         ptr[(N - 1)*off] = (T)(n - 2*c + p);
35656       } break;
35657       }
35658     }
35659 
35660     static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
35661                                    const bool boundary_conditions, const int x) {
35662       if (x<0) return boundary_conditions?ptr[0]:T();
35663       if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T();
35664       return ptr[x*off];
35665     }
35666 
35667     // Apply box filter of order 0,1,2.
35668     /**
35669       \param boxsize Size of the box window (can be subpixel)
35670       \param order the order of the filter 0,1 or 2.
35671       \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
35672       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
35673       \param nb_iter Number of filter iterations.
35674     **/
35675     CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
35676                        const bool boundary_conditions=true,
35677                        const unsigned int nb_iter=1) {
35678       if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this;
35679       const char naxis = cimg::lowercase(axis);
35680       const float nboxsize = boxsize>=0?boxsize:-boxsize*
35681         (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
35682       switch (naxis) {
35683       case 'x' : {
35684         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35685         cimg_forYZC(*this,y,z,c)
35686           _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
35687       } break;
35688       case 'y' : {
35689         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35690         cimg_forXZC(*this,x,z,c)
35691           _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
35692       } break;
35693       case 'z' : {
35694         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35695         cimg_forXYC(*this,x,y,c)
35696           _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
35697       } break;
35698       default : {
35699         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35700         cimg_forXYZ(*this,x,y,z)
35701           _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
35702                                order,boundary_conditions,nb_iter);
35703       }
35704       }
35705       return *this;
35706     }
35707 
35708     // Apply box filter of order 0,1 or 2 \newinstance.
35709     CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
35710                                const bool boundary_conditions=true,
35711                                const unsigned int nb_iter=1) const {
35712       return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
35713     }
35714 
35715     //! Blur image with a box filter.
35716     /**
35717        \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
35718        \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
35719        \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
35720        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
35721        \param nb_iter Number of filter iterations.
35722        \note
35723        - This is a recursive algorithm, not depending on the values of the box kernel size.
35724        \see blur().
35725     **/
35726     CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
35727                       const bool boundary_conditions=true,
35728                       const unsigned int nb_iter=1) {
35729       if (is_empty()) return *this;
35730       if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
35731       if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
35732       if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
35733       return *this;
35734     }
35735 
35736     //! Blur image with a box filter \newinstance.
35737     CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
35738                               const bool boundary_conditions=true) const {
35739       return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
35740     }
35741 
35742     //! Blur image with a box filter.
35743     /**
35744        \param boxsize Size of the box window (can be subpixel).
35745        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
35746        \see deriche(), vanvliet().
35747     **/
35748     CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) {
35749       const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
35750       return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
35751     }
35752 
35753     //! Blur image with a box filter \newinstance.
35754     CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const {
35755       return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
35756     }
35757 
35758     //! Blur image, with the image guided filter.
35759     /**
35760        \param guide Image used to guide the smoothing process.
35761        \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
35762        \param regularization Regularization parameter.
35763                              If negative, it is expressed as a percentage of the guide value range.
35764        \note This method implements the filtering algorithm described in:
35765        He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
35766        IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
35767     **/
35768     template<typename t>
35769     CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
35770       return get_blur_guided(guide,radius,regularization).move_to(*this);
35771     }
35772 
35773     //! Blur image, with the image guided filter \newinstance.
35774     template<typename t>
35775     CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
35776       if (!is_sameXYZ(guide))
35777         throw CImgArgumentException(_cimg_instance
35778                                     "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
35779                                     cimg_instance,
35780                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
35781       if (is_empty() || !radius) return *this;
35782       const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
35783       float _regularization = regularization;
35784       if (regularization<0) {
35785         T edge_min, edge_max = guide.max_min(edge_min);
35786         if (edge_min==edge_max) return *this;
35787         _regularization = -regularization*(edge_max - edge_min)/100;
35788       }
35789       _regularization = std::max(_regularization,0.01f);
35790       const unsigned int psize = (unsigned int)(1 + 2*_radius);
35791       const CImg<uintT> N = CImg<uintT>(_width,_height,_depth,1,1)._blur_guided(psize);
35792       CImg<Tfloat>
35793         mean_I = CImg<Tfloat>(guide,false)._blur_guided(psize).div(N),
35794         mean_p = CImg<Tfloat>(*this,false)._blur_guided(psize).div(N),
35795         cov_Ip = CImg<Tfloat>(*this,false).mul(guide)._blur_guided(psize).div(N)-=mean_p.get_mul(mean_I),
35796         var_I = CImg<Tfloat>(guide,false).sqr()._blur_guided(psize).div(N)-=mean_I.get_sqr(),
35797         &a = cov_Ip.div(var_I+=_regularization),
35798         &b = mean_p-=a.get_mul(mean_I);
35799       a._blur_guided(psize).div(N);
35800       b._blur_guided(psize).div(N);
35801       return a.mul(guide)+=b;
35802     }
35803 
35804     // [internal] Perform box filter with dirichlet boundary conditions.
35805     CImg<T>& _blur_guided(const unsigned int psize) {
35806       const int p1 = (int)psize/2, p2 = (int)psize - p1;
35807       if (_depth!=1) {
35808         CImg<floatT> cumul = get_cumulate('z'), cumul2 = cumul.get_shift(0,0,p2,0,1);
35809         (cumul.shift(0,0,-p1,0,1)-=cumul2).move_to(*this);
35810       }
35811       if (_height!=1) {
35812         CImg<floatT> cumul = get_cumulate('y'), cumul2 = cumul.get_shift(0,p2,0,0,1);
35813         (cumul.shift(0,-p1,0,0,1)-=cumul2).move_to(*this);
35814       }
35815       if (_width!=1) {
35816         CImg<floatT> cumul = get_cumulate('x'), cumul2 = cumul.get_shift(p2,0,0,0,1);
35817         (cumul.shift(-p1,0,0,0,1)-=cumul2).move_to(*this);
35818       }
35819       return *this;
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,r1,color,opacity,pattern);
44194         else return draw_circle(x0,y0,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)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 : // Segment
46030         case 6 : {
46031           const unsigned int
46032             i0 = (unsigned int)primitive(0),
46033             i1 = (unsigned int)primitive(1);
46034           const tpfloat
46035             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46036             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
46037           tpfloat xm, xM, ym, yM;
46038           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46039           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46040           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
46041             visibles(l) = (unsigned int)l;
46042             zrange(l) = (z0 + z1)/2;
46043           }
46044         } break;
46045         case 3 :  // Triangle
46046         case 9 : {
46047           const unsigned int
46048             i0 = (unsigned int)primitive(0),
46049             i1 = (unsigned int)primitive(1),
46050             i2 = (unsigned int)primitive(2);
46051           const tpfloat
46052             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46053             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
46054             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
46055           tpfloat xm, xM, ym, yM;
46056           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46057           if (x2<xm) xm = x2;
46058           if (x2>xM) xM = x2;
46059           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46060           if (y2<ym) ym = y2;
46061           if (y2>yM) yM = y2;
46062           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
46063             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
46064             if (is_double_sided || d<0) {
46065               visibles(l) = (unsigned int)l;
46066               zrange(l) = (z0 + z1 + z2)/3;
46067             }
46068           }
46069         } break;
46070         case 4 : // Rectangle
46071         case 12 : {
46072           const unsigned int
46073             i0 = (unsigned int)primitive(0),
46074             i1 = (unsigned int)primitive(1),
46075             i2 = (unsigned int)primitive(2),
46076             i3 = (unsigned int)primitive(3);
46077           const tpfloat
46078             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46079             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
46080             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
46081             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
46082           tpfloat xm, xM, ym, yM;
46083           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46084           if (x2<xm) xm = x2;
46085           if (x2>xM) xM = x2;
46086           if (x3<xm) xm = x3;
46087           if (x3>xM) xM = x3;
46088           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46089           if (y2<ym) ym = y2;
46090           if (y2>yM) yM = y2;
46091           if (y3<ym) ym = y3;
46092           if (y3>yM) yM = y3;
46093           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
46094             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
46095             if (is_double_sided || d<0) {
46096               visibles(l) = (unsigned int)l;
46097               zrange(l) = (z0 + z1 + z2 + z3)/4;
46098             }
46099           }
46100         } break;
46101         default :
46102           if (render_type==5) cimg::mutex(10,0);
46103           throw CImgArgumentException(_cimg_instance
46104                                       "draw_object3d(): Invalid primitive[%u] with size %u "
46105                                       "(should have size 1,2,3,4,5,6,9 or 12).",
46106                                       cimg_instance,
46107                                       l,primitive.size());
46108         }
46109       }
46110 
46111       // Force transparent primitives to be drawn last when zbuffer is activated
46112       // (and if object contains no spheres or sprites).
46113       if (is_forward)
46114         cimglist_for(primitives,l)
46115           if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
46116 
46117       // Sort only visibles primitives.
46118       unsigned int *p_visibles = visibles._data;
46119       tpfloat *p_zrange = zrange._data;
46120       const tpfloat *ptrz = p_zrange;
46121       cimg_for(visibles,ptr,unsigned int) {
46122         if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
46123         ++ptrz;
46124       }
46125       const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
46126       if (!nb_visibles) {
46127         if (render_type==5) cimg::mutex(10,0);
46128         return *this;
46129       }
46130       CImg<uintT> permutations;
46131       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
46132 
46133       // Compute light properties
46134       CImg<floatT> lightprops;
46135       switch (render_type) {
46136       case 3 : { // Flat Shading
46137         lightprops.assign(nb_visibles);
46138         cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46139         cimg_forX(lightprops,l) {
46140           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
46141           const unsigned int psize = (unsigned int)primitive.size();
46142           if (psize==3 || psize==4 || psize==9 || psize==12) {
46143             const unsigned int
46144               i0 = (unsigned int)primitive(0),
46145               i1 = (unsigned int)primitive(1),
46146               i2 = (unsigned int)primitive(2);
46147             const tpfloat
46148               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
46149               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
46150               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
46151               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
46152               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
46153               nx = dy1*dz2 - dz1*dy2,
46154               ny = dz1*dx2 - dx1*dz2,
46155               nz = dx1*dy2 - dy1*dx2,
46156               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46157               lx = X + (x0 + x1 + x2)/3 - lightx,
46158               ly = Y + (y0 + y1 + y2)/3 - lighty,
46159               lz = Z + (z0 + z1 + z2)/3 - lightz,
46160               nl = 1e-5f + cimg::hypot(lx,ly,lz),
46161               factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
46162             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
46163           } else lightprops[l] = 1;
46164         }
46165       } break;
46166 
46167       case 4 : // Gouraud Shading
46168       case 5 : { // Phong-Shading
46169         CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
46170         cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46171         for (unsigned int l = 0; l<nb_visibles; ++l) {
46172           const CImg<tf>& primitive = primitives[visibles(l)];
46173           const unsigned int psize = (unsigned int)primitive.size();
46174           const bool
46175             triangle_flag = (psize==3) || (psize==9),
46176             rectangle_flag = (psize==4) || (psize==12);
46177           if (triangle_flag || rectangle_flag) {
46178             const unsigned int
46179               i0 = (unsigned int)primitive(0),
46180               i1 = (unsigned int)primitive(1),
46181               i2 = (unsigned int)primitive(2),
46182               i3 = rectangle_flag?(unsigned int)primitive(3):0;
46183             const tpfloat
46184               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
46185               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
46186               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
46187               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
46188               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
46189               nnx = dy1*dz2 - dz1*dy2,
46190               nny = dz1*dx2 - dx1*dz2,
46191               nnz = dx1*dy2 - dy1*dx2,
46192               norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
46193               nx = nnx/norm,
46194               ny = nny/norm,
46195               nz = nnz/norm;
46196             unsigned int ix = 0, iy = 1, iz = 2;
46197             if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
46198             vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
46199             vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
46200             vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
46201             if (rectangle_flag) {
46202               vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
46203             }
46204           }
46205         }
46206 
46207         if (is_double_sided) cimg_forX(vertices_normals,p) {
46208             const float
46209               nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
46210               nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
46211               n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
46212             if (n1>n0) {
46213               vertices_normals(p,0) = -nx1;
46214               vertices_normals(p,1) = -ny1;
46215               vertices_normals(p,2) = -nz1;
46216             }
46217           }
46218 
46219         if (render_type==4) {
46220           lightprops.assign(vertices._width);
46221           cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46222           cimg_forX(lightprops,l) {
46223             const tpfloat
46224               nx = vertices_normals(l,0),
46225               ny = vertices_normals(l,1),
46226               nz = vertices_normals(l,2),
46227               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46228               lx = X + vertices(l,0) - lightx,
46229               ly = Y + vertices(l,1) - lighty,
46230               lz = Z + vertices(l,2) - lightz,
46231               nl = 1e-5f + cimg::hypot(lx,ly,lz),
46232               factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
46233             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
46234           }
46235         } else {
46236           const unsigned int
46237             lw2 = light_texture._width/2 - 1,
46238             lh2 = light_texture._height/2 - 1;
46239           lightprops.assign(vertices._width,2);
46240           cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46241           cimg_forX(lightprops,l) {
46242             const tpfloat
46243               nx = vertices_normals(l,0),
46244               ny = vertices_normals(l,1),
46245               nz = vertices_normals(l,2),
46246               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46247               nnx = nx/norm,
46248               nny = ny/norm;
46249             lightprops(l,0) = lw2*(1 + nnx);
46250             lightprops(l,1) = lh2*(1 + nny);
46251           }
46252         }
46253       } break;
46254       }
46255 
46256       // Draw visible primitives
46257       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
46258       CImg<_to> _opacity;
46259 
46260       for (unsigned int l = 0; l<nb_visibles; ++l) {
46261         const unsigned int n_primitive = visibles(permutations(l));
46262         const CImg<tf>& primitive = primitives[n_primitive];
46263         const CImg<tc>
46264           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
46265           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
46266             __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
46267           &color = _color?_color:(__color?__color:default_color);
46268         const tc *const pcolor = color._data;
46269         const float opacity = __draw_object3d(opacities,n_primitive,_opacity);
46270 
46271 #ifdef cimg_use_board
46272         LibBoard::Board &board = *(LibBoard::Board*)pboard;
46273 #endif
46274 
46275         switch (primitive.size()) {
46276         case 1 : { // Colored point or sprite
46277           const unsigned int n0 = (unsigned int)primitive[0];
46278           const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
46279 
46280           if (_opacity.is_empty()) { // Scalar opacity.
46281 
46282             if (color.size()==_spectrum) { // Colored point.
46283               draw_point(x0,y0,pcolor,opacity);
46284 #ifdef cimg_use_board
46285               if (pboard) {
46286                 board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46287                 board.drawDot((float)x0,height()-(float)y0);
46288               }
46289 #endif
46290             } else { // Sprite.
46291               const tpfloat z = Z + vertices(n0,2);
46292               const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
46293               const unsigned int
46294                 _sw = (unsigned int)(color._width*factor),
46295                 _sh = (unsigned int)(color._height*factor),
46296                 sw = _sw?_sw:1, sh = _sh?_sh:1;
46297               const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
46298               if (sw<=3*_width/2 && sh<=3*_height/2 &&
46299                   (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
46300                 const CImg<tc>
46301                   _sprite = (sw!=color._width || sh!=color._height)?
46302                     color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
46303                   &sprite = _sprite?_sprite:color;
46304                 draw_image(nx0,ny0,sprite,opacity);
46305 #ifdef cimg_use_board
46306                 if (pboard) {
46307                   board.setPenColorRGBi(128,128,128);
46308                   board.setFillColor(LibBoard::Color::Null);
46309                   board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
46310                 }
46311 #endif
46312               }
46313             }
46314           } else { // Opacity mask.
46315             const tpfloat z = Z + vertices(n0,2);
46316             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
46317             const unsigned int
46318               _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
46319               _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
46320               sw = _sw?_sw:1, sh = _sh?_sh:1;
46321             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
46322             if (sw<=3*_width/2 && sh<=3*_height/2 &&
46323                 (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
46324               const CImg<tc>
46325                 _sprite = (sw!=color._width || sh!=color._height)?
46326                   color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
46327                 &sprite = _sprite?_sprite:color;
46328               const CImg<_to>
46329                 _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
46330                   _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
46331                 &nopacity = _nopacity?_nopacity:_opacity;
46332               draw_image(nx0,ny0,sprite,nopacity);
46333 #ifdef cimg_use_board
46334               if (pboard) {
46335                 board.setPenColorRGBi(128,128,128);
46336                 board.setFillColor(LibBoard::Color::Null);
46337                 board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
46338               }
46339 #endif
46340             }
46341           }
46342         } break;
46343         case 2 : { // Colored line
46344           const unsigned int
46345             n0 = (unsigned int)primitive[0],
46346             n1 = (unsigned int)primitive[1];
46347           const int
46348             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46349             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
46350           const float
46351             z0 = vertices(n0,2) + Z + _focale,
46352             z1 = vertices(n1,2) + Z + _focale;
46353           if (render_type) {
46354             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
46355             else draw_line(x0,y0,x1,y1,pcolor,opacity);
46356 #ifdef cimg_use_board
46357             if (pboard) {
46358               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46359               board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
46360             }
46361 #endif
46362           } else {
46363             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
46364 #ifdef cimg_use_board
46365             if (pboard) {
46366               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46367               board.drawDot((float)x0,height() - (float)y0);
46368               board.drawDot((float)x1,height() - (float)y1);
46369             }
46370 #endif
46371           }
46372         } break;
46373         case 5 : { // Colored sphere
46374           const unsigned int
46375             n0 = (unsigned int)primitive[0],
46376             n1 = (unsigned int)primitive[1],
46377             is_wireframe = (unsigned int)primitive[2];
46378           const float
46379             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
46380             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
46381             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
46382             zc = Z + Zc + _focale,
46383             xc = X + Xc*(absfocale?absfocale/zc:1),
46384             yc = Y + Yc*(absfocale?absfocale/zc:1),
46385             radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
46386                                       vertices(n1,1) - vertices(n0,1),
46387                                       vertices(n1,2) - vertices(n0,2))*(absfocale?absfocale/zc:1);
46388           switch (render_type) {
46389           case 0 :
46390             draw_point((int)xc,(int)yc,pcolor,opacity);
46391 #ifdef cimg_use_board
46392             if (pboard) {
46393               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46394               board.drawDot(xc,height() - yc);
46395             }
46396 #endif
46397             break;
46398           case 1 :
46399             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
46400 #ifdef cimg_use_board
46401             if (pboard) {
46402               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46403               board.setFillColor(LibBoard::Color::Null);
46404               board.drawCircle(xc,height() - yc,radius);
46405             }
46406 #endif
46407             break;
46408           default :
46409             if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
46410             else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
46411 #ifdef cimg_use_board
46412             if (pboard) {
46413               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46414               if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
46415               else {
46416                 board.setFillColor(LibBoard::Color::Null);
46417                 board.drawCircle(xc,height() - yc,radius);
46418               }
46419             }
46420 #endif
46421             break;
46422           }
46423         } break;
46424         case 6 : { // Textured line
46425           if (!__color) {
46426             if (render_type==5) cimg::mutex(10,0);
46427             throw CImgArgumentException(_cimg_instance
46428                                         "draw_object3d(): Undefined texture for line primitive [%u].",
46429                                         cimg_instance,n_primitive);
46430           }
46431           const unsigned int
46432             n0 = (unsigned int)primitive[0],
46433             n1 = (unsigned int)primitive[1];
46434           const int
46435             tx0 = (int)primitive[2], ty0 = (int)primitive[3],
46436             tx1 = (int)primitive[4], ty1 = (int)primitive[5],
46437             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46438             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
46439           const float
46440             z0 = vertices(n0,2) + Z + _focale,
46441             z1 = vertices(n1,2) + Z + _focale;
46442           if (render_type) {
46443             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
46444             else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity);
46445 #ifdef cimg_use_board
46446             if (pboard) {
46447               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46448               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46449             }
46450 #endif
46451           } else {
46452             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46453                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46454               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46455                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
46456 #ifdef cimg_use_board
46457             if (pboard) {
46458               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46459               board.drawDot((float)x0,height() - (float)y0);
46460               board.drawDot((float)x1,height() - (float)y1);
46461             }
46462 #endif
46463           }
46464         } break;
46465         case 3 : { // Colored triangle
46466           const unsigned int
46467             n0 = (unsigned int)primitive[0],
46468             n1 = (unsigned int)primitive[1],
46469             n2 = (unsigned int)primitive[2];
46470           const int
46471             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46472             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46473             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
46474           const float
46475             z0 = vertices(n0,2) + Z + _focale,
46476             z1 = vertices(n1,2) + Z + _focale,
46477             z2 = vertices(n2,2) + Z + _focale;
46478           switch (render_type) {
46479           case 0 :
46480             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
46481 #ifdef cimg_use_board
46482             if (pboard) {
46483               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46484               board.drawDot((float)x0,height() - (float)y0);
46485               board.drawDot((float)x1,height() - (float)y1);
46486               board.drawDot((float)x2,height() - (float)y2);
46487             }
46488 #endif
46489             break;
46490           case 1 :
46491             if (zbuffer)
46492               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
46493                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
46494             else
46495               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
46496                 draw_line(x1,y1,x2,y2,pcolor,opacity);
46497 #ifdef cimg_use_board
46498             if (pboard) {
46499               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46500               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46501               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
46502               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46503             }
46504 #endif
46505             break;
46506           case 2 :
46507             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
46508             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
46509 #ifdef cimg_use_board
46510             if (pboard) {
46511               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46512               board.fillTriangle((float)x0,height() - (float)y0,
46513                                  (float)x1,height() - (float)y1,
46514                                  (float)x2,height() - (float)y2);
46515             }
46516 #endif
46517             break;
46518           case 3 :
46519             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
46520             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
46521 #ifdef cimg_use_board
46522             if (pboard) {
46523               const float lp = std::min(lightprops(l),1);
46524               board.setPenColorRGBi((unsigned char)(color[0]*lp),
46525                                      (unsigned char)(color[1]*lp),
46526                                      (unsigned char)(color[2]*lp),
46527                                      (unsigned char)(opacity*255));
46528               board.fillTriangle((float)x0,height() - (float)y0,
46529                                  (float)x1,height() - (float)y1,
46530                                  (float)x2,height() - (float)y2);
46531             }
46532 #endif
46533             break;
46534           case 4 :
46535             if (zbuffer)
46536               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
46537                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46538             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46539 #ifdef cimg_use_board
46540             if (pboard) {
46541               board.setPenColorRGBi((unsigned char)(color[0]),
46542                                      (unsigned char)(color[1]),
46543                                      (unsigned char)(color[2]),
46544                                      (unsigned char)(opacity*255));
46545               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
46546                                          (float)x1,height() - (float)y1,lightprops(n1),
46547                                          (float)x2,height() - (float)y2,lightprops(n2));
46548             }
46549 #endif
46550             break;
46551           case 5 : {
46552             const unsigned int
46553               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
46554               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
46555               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
46556             if (zbuffer)
46557               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46558             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46559 #ifdef cimg_use_board
46560             if (pboard) {
46561               const float
46562                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
46563                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
46564                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
46565                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
46566                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
46567                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
46568               board.setPenColorRGBi((unsigned char)(color[0]),
46569                                      (unsigned char)(color[1]),
46570                                      (unsigned char)(color[2]),
46571                                      (unsigned char)(opacity*255));
46572               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46573                                          (float)x1,height() - (float)y1,l1,
46574                                          (float)x2,height() - (float)y2,l2);
46575             }
46576 #endif
46577           } break;
46578           }
46579         } break;
46580         case 4 : { // Colored rectangle
46581           const unsigned int
46582             n0 = (unsigned int)primitive[0],
46583             n1 = (unsigned int)primitive[1],
46584             n2 = (unsigned int)primitive[2],
46585             n3 = (unsigned int)primitive[3];
46586           const int
46587             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46588             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46589             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
46590             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
46591           const float
46592             z0 = vertices(n0,2) + Z + _focale,
46593             z1 = vertices(n1,2) + Z + _focale,
46594             z2 = vertices(n2,2) + Z + _focale,
46595             z3 = vertices(n3,2) + Z + _focale;
46596 
46597           switch (render_type) {
46598           case 0 :
46599             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
46600               draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
46601 #ifdef cimg_use_board
46602             if (pboard) {
46603               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46604               board.drawDot((float)x0,height() - (float)y0);
46605               board.drawDot((float)x1,height() - (float)y1);
46606               board.drawDot((float)x2,height() - (float)y2);
46607               board.drawDot((float)x3,height() - (float)y3);
46608             }
46609 #endif
46610             break;
46611           case 1 :
46612             if (zbuffer)
46613               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
46614                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
46615             else
46616               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
46617                 draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
46618 #ifdef cimg_use_board
46619             if (pboard) {
46620               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46621               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46622               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46623               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
46624               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
46625             }
46626 #endif
46627             break;
46628           case 2 :
46629             if (zbuffer)
46630               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
46631                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
46632             else
46633               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
46634 #ifdef cimg_use_board
46635             if (pboard) {
46636               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46637               board.fillTriangle((float)x0,height() - (float)y0,
46638                                  (float)x1,height() - (float)y1,
46639                                  (float)x2,height() - (float)y2);
46640               board.fillTriangle((float)x0,height() - (float)y0,
46641                                  (float)x2,height() - (float)y2,
46642                                  (float)x3,height() - (float)y3);
46643             }
46644 #endif
46645             break;
46646           case 3 :
46647             if (zbuffer)
46648               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
46649                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
46650             else
46651               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
46652                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
46653 #ifdef cimg_use_board
46654             if (pboard) {
46655               const float lp = std::min(lightprops(l),1);
46656               board.setPenColorRGBi((unsigned char)(color[0]*lp),
46657                                      (unsigned char)(color[1]*lp),
46658                                      (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
46659               board.fillTriangle((float)x0,height() - (float)y0,
46660                                  (float)x1,height() - (float)y1,
46661                                  (float)x2,height() - (float)y2);
46662               board.fillTriangle((float)x0,height() - (float)y0,
46663                                  (float)x2,height() - (float)y2,
46664                                  (float)x3,height() - (float)y3);
46665             }
46666 #endif
46667             break;
46668           case 4 : {
46669             const float
46670               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
46671               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
46672             if (zbuffer)
46673               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
46674                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
46675             else
46676               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
46677                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
46678 #ifdef cimg_use_board
46679             if (pboard) {
46680               board.setPenColorRGBi((unsigned char)(color[0]),
46681                                      (unsigned char)(color[1]),
46682                                      (unsigned char)(color[2]),
46683                                      (unsigned char)(opacity*255));
46684               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
46685                                          (float)x1,height() - (float)y1,lightprop1,
46686                                          (float)x2,height() - (float)y2,lightprop2);
46687               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
46688                                          (float)x2,height() - (float)y2,lightprop2,
46689                                          (float)x3,height() - (float)y3,lightprop3);
46690             }
46691 #endif
46692           } break;
46693           case 5 : {
46694             const unsigned int
46695               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
46696               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
46697               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
46698               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
46699             if (zbuffer)
46700               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
46701                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
46702             else
46703               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
46704                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
46705 #ifdef cimg_use_board
46706             if (pboard) {
46707               const float
46708                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
46709                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
46710                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
46711                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
46712               board.setPenColorRGBi((unsigned char)(color[0]),
46713                                      (unsigned char)(color[1]),
46714                                      (unsigned char)(color[2]),
46715                                      (unsigned char)(opacity*255));
46716               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46717                                          (float)x1,height() - (float)y1,l1,
46718                                          (float)x2,height() - (float)y2,l2);
46719               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46720                                          (float)x2,height() - (float)y2,l2,
46721                                          (float)x3,height() - (float)y3,l3);
46722             }
46723 #endif
46724           } break;
46725           }
46726         } break;
46727         case 9 : { // Textured triangle
46728           if (!__color) {
46729             if (render_type==5) cimg::mutex(10,0);
46730             throw CImgArgumentException(_cimg_instance
46731                                         "draw_object3d(): Undefined texture for triangle primitive [%u].",
46732                                         cimg_instance,n_primitive);
46733           }
46734           const unsigned int
46735             n0 = (unsigned int)primitive[0],
46736             n1 = (unsigned int)primitive[1],
46737             n2 = (unsigned int)primitive[2];
46738           const int
46739             tx0 = (int)primitive[3], ty0 = (int)primitive[4],
46740             tx1 = (int)primitive[5], ty1 = (int)primitive[6],
46741             tx2 = (int)primitive[7], ty2 = (int)primitive[8],
46742             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46743             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46744             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
46745           const float
46746             z0 = vertices(n0,2) + Z + _focale,
46747             z1 = vertices(n1,2) + Z + _focale,
46748             z2 = vertices(n2,2) + Z + _focale;
46749           switch (render_type) {
46750           case 0 :
46751             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46752                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46753               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46754                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
46755               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
46756                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
46757 #ifdef cimg_use_board
46758             if (pboard) {
46759               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46760               board.drawDot((float)x0,height() - (float)y0);
46761               board.drawDot((float)x1,height() - (float)y1);
46762               board.drawDot((float)x2,height() - (float)y2);
46763             }
46764 #endif
46765             break;
46766           case 1 :
46767             if (zbuffer)
46768               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46769                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
46770                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
46771             else
46772               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46773                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
46774                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
46775 #ifdef cimg_use_board
46776             if (pboard) {
46777               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46778               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46779               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
46780               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46781             }
46782 #endif
46783             break;
46784           case 2 :
46785             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
46786             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
46787 #ifdef cimg_use_board
46788             if (pboard) {
46789               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46790               board.fillTriangle((float)x0,height() - (float)y0,
46791                                  (float)x1,height() - (float)y1,
46792                                  (float)x2,height() - (float)y2);
46793             }
46794 #endif
46795             break;
46796           case 3 :
46797             if (zbuffer)
46798               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
46799             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
46800 #ifdef cimg_use_board
46801             if (pboard) {
46802               const float lp = std::min(lightprops(l),1);
46803               board.setPenColorRGBi((unsigned char)(128*lp),
46804                                     (unsigned char)(128*lp),
46805                                     (unsigned char)(128*lp),
46806                                     (unsigned char)(opacity*255));
46807               board.fillTriangle((float)x0,height() - (float)y0,
46808                                  (float)x1,height() - (float)y1,
46809                                  (float)x2,height() - (float)y2);
46810             }
46811 #endif
46812             break;
46813           case 4 :
46814             if (zbuffer)
46815               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46816                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46817             else
46818               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46819                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46820 #ifdef cimg_use_board
46821             if (pboard) {
46822               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46823               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
46824                                         (float)x1,height() - (float)y1,lightprops(n1),
46825                                         (float)x2,height() - (float)y2,lightprops(n2));
46826             }
46827 #endif
46828             break;
46829           case 5 :
46830             if (zbuffer)
46831               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
46832                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
46833                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
46834                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
46835                             opacity);
46836             else
46837               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
46838                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
46839                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
46840                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
46841                             opacity);
46842 #ifdef cimg_use_board
46843             if (pboard) {
46844               const float
46845                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
46846                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
46847                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
46848                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
46849                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
46850                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
46851               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46852               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46853                                         (float)x1,height() - (float)y1,l1,
46854                                         (float)x2,height() - (float)y2,l2);
46855             }
46856 #endif
46857             break;
46858           }
46859         } break;
46860         case 12 : { // Textured quadrangle
46861           if (!__color) {
46862             if (render_type==5) cimg::mutex(10,0);
46863             throw CImgArgumentException(_cimg_instance
46864                                         "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
46865                                         cimg_instance,n_primitive);
46866           }
46867           const unsigned int
46868             n0 = (unsigned int)primitive[0],
46869             n1 = (unsigned int)primitive[1],
46870             n2 = (unsigned int)primitive[2],
46871             n3 = (unsigned int)primitive[3];
46872           const int
46873             tx0 = (int)primitive[4], ty0 = (int)primitive[5],
46874             tx1 = (int)primitive[6], ty1 = (int)primitive[7],
46875             tx2 = (int)primitive[8], ty2 = (int)primitive[9],
46876             tx3 = (int)primitive[10], ty3 = (int)primitive[11],
46877             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46878             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46879             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
46880             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
46881           const float
46882             z0 = vertices(n0,2) + Z + _focale,
46883             z1 = vertices(n1,2) + Z + _focale,
46884             z2 = vertices(n2,2) + Z + _focale,
46885             z3 = vertices(n3,2) + Z + _focale;
46886 
46887           switch (render_type) {
46888           case 0 :
46889             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46890                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46891               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46892                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
46893               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
46894                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
46895               draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
46896                                                    ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
46897 #ifdef cimg_use_board
46898             if (pboard) {
46899               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46900               board.drawDot((float)x0,height() - (float)y0);
46901               board.drawDot((float)x1,height() - (float)y1);
46902               board.drawDot((float)x2,height() - (float)y2);
46903               board.drawDot((float)x3,height() - (float)y3);
46904             }
46905 #endif
46906             break;
46907           case 1 :
46908             if (zbuffer)
46909               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46910                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
46911                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
46912                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
46913             else
46914               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46915                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
46916                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
46917                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
46918 #ifdef cimg_use_board
46919             if (pboard) {
46920               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46921               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46922               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46923               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
46924               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
46925             }
46926 #endif
46927             break;
46928           case 2 :
46929             if (zbuffer)
46930               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
46931                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
46932             else
46933               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
46934                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
46935 #ifdef cimg_use_board
46936             if (pboard) {
46937               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46938               board.fillTriangle((float)x0,height() - (float)y0,
46939                                  (float)x1,height() - (float)y1,
46940                                  (float)x2,height() - (float)y2);
46941               board.fillTriangle((float)x0,height() - (float)y0,
46942                                  (float)x2,height() - (float)y2,
46943                                  (float)x3,height() - (float)y3);
46944             }
46945 #endif
46946             break;
46947           case 3 :
46948             if (zbuffer)
46949               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
46950                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
46951             else
46952               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
46953                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
46954 #ifdef cimg_use_board
46955             if (pboard) {
46956               const float lp = std::min(lightprops(l),1);
46957               board.setPenColorRGBi((unsigned char)(128*lp),
46958                                      (unsigned char)(128*lp),
46959                                      (unsigned char)(128*lp),
46960                                      (unsigned char)(opacity*255));
46961               board.fillTriangle((float)x0,height() - (float)y0,
46962                                  (float)x1,height() - (float)y1,
46963                                  (float)x2,height() - (float)y2);
46964               board.fillTriangle((float)x0,height() - (float)y0,
46965                                  (float)x2,height() - (float)y2,
46966                                  (float)x3,height() - (float)y3);
46967             }
46968 #endif
46969             break;
46970           case 4 : {
46971             const float
46972               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
46973               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
46974             if (zbuffer)
46975               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46976                             lightprop0,lightprop1,lightprop2,opacity).
46977                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
46978                               lightprop0,lightprop2,lightprop3,opacity);
46979             else
46980               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46981                             lightprop0,lightprop1,lightprop2,opacity).
46982                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
46983                               lightprop0,lightprop2,lightprop3,opacity);
46984 #ifdef cimg_use_board
46985             if (pboard) {
46986               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46987               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
46988                                          (float)x1,height() - (float)y1,lightprop1,
46989                                          (float)x2,height() - (float)y2,lightprop2);
46990               board.fillGouraudTriangle((float)x0,height()  -(float)y0,lightprop0,
46991                                          (float)x2,height() - (float)y2,lightprop2,
46992                                          (float)x3,height() - (float)y3,lightprop3);
46993             }
46994 #endif
46995           } break;
46996           case 5 : {
46997             const unsigned int
46998               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
46999               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
47000               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
47001               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
47002             if (zbuffer)
47003               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
47004                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
47005                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
47006                               light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
47007             else
47008               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
47009                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
47010                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
47011                               light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
47012 #ifdef cimg_use_board
47013             if (pboard) {
47014               const float
47015                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
47016                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
47017                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
47018                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
47019               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
47020               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
47021                                          (float)x1,height() - (float)y1,l1,
47022                                          (float)x2,height() - (float)y2,l2);
47023               board.fillGouraudTriangle((float)x0,height()  -(float)y0,l0,
47024                                          (float)x2,height() - (float)y2,l2,
47025                                          (float)x3,height() - (float)y3,l3);
47026             }
47027 #endif
47028           } break;
47029           }
47030         } break;
47031         }
47032       }
47033 
47034       if (render_type==5) cimg::mutex(10,0);
47035       return *this;
47036     }
47037 
47038     //@}
47039     //---------------------------
47040     //
47041     //! \name Data Input
47042     //@{
47043     //---------------------------
47044 
47045     //! Launch simple interface to select a shape from an image.
47046     /**
47047        \param disp Display window to use.
47048        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
47049        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
47050        \param exit_on_anykey Exit function when any key is pressed.
47051     **/
47052     CImg<T>& select(CImgDisplay &disp,
47053 		    const unsigned int feature_type=2, unsigned int *const XYZ=0,
47054                     const bool exit_on_anykey=false) {
47055       return get_select(disp,feature_type,XYZ,exit_on_anykey).move_to(*this);
47056     }
47057 
47058     //! Simple interface to select a shape from an image \overloading.
47059     CImg<T>& select(const char *const title,
47060 		    const unsigned int feature_type=2, unsigned int *const XYZ=0,
47061                     const bool exit_on_anykey=false) {
47062       return get_select(title,feature_type,XYZ,exit_on_anykey).move_to(*this);
47063     }
47064 
47065     //! Simple interface to select a shape from an image \newinstance.
47066     CImg<intT> get_select(CImgDisplay &disp,
47067 		          const unsigned int feature_type=2, unsigned int *const XYZ=0,
47068                           const bool exit_on_anykey=false) const {
47069       return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false);
47070     }
47071 
47072     //! Simple interface to select a shape from an image \newinstance.
47073     CImg<intT> get_select(const char *const title,
47074     			  const unsigned int feature_type=2, unsigned int *const XYZ=0,
47075                           const bool exit_on_anykey=false) const {
47076       CImgDisplay disp;
47077       return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false);
47078     }
47079 
47080     CImg<intT> _select(CImgDisplay &disp, const char *const title,
47081                        const unsigned int feature_type, unsigned int *const XYZ,
47082                        const int origX, const int origY, const int origZ,
47083                        const bool exit_on_anykey,
47084                        const bool reset_view3d,
47085                        const bool force_display_z_coord) const {
47086       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
47087       if (!disp) {
47088         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
47089         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
47090       } else if (title) disp.set_title("%s",title);
47091 
47092       CImg<T> thumb;
47093       if (width()>disp.screen_width() || height()>disp.screen_height())
47094         get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
47095 
47096       const unsigned int old_normalization = disp.normalization();
47097       bool old_is_resized = disp.is_resized();
47098       disp._normalization = 0;
47099       disp.show().set_key(0).set_wheel().show_mouse();
47100 
47101       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
47102 
47103       int area = 0, starting_area = 0, clicked_area = 0, phase = 0,
47104         X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width),
47105         Y0 = (int)((XYZ?XYZ[1]:(_height - 1)/2)%_height),
47106         Z0 = (int)((XYZ?XYZ[2]:(_depth - 1)/2)%_depth),
47107         X1 =-1, Y1 = -1, Z1 = -1,
47108         X3d = -1, Y3d = -1,
47109         oX3d = X3d, oY3d = -1,
47110         omx = -1, omy = -1;
47111       float X = -1, Y = -1, Z = -1;
47112       unsigned int old_button = 0, key = 0;
47113 
47114       bool shape_selected = false, text_down = false, visible_cursor = true;
47115       static CImg<floatT> pose3d;
47116       static bool is_view3d = false, is_axes = true;
47117       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
47118       CImg<floatT> points3d, opacities3d, sel_opacities3d;
47119       CImgList<uintT> primitives3d, sel_primitives3d;
47120       CImgList<ucharT> colors3d, sel_colors3d;
47121       CImg<ucharT> visu, visu0, view3d;
47122       CImg<charT> text(1024); *text = 0;
47123 
47124       while (!key && !disp.is_closed() && !shape_selected) {
47125 
47126         // Handle mouse motion and selection
47127         int
47128           mx = disp.mouse_x(),
47129           my = disp.mouse_y();
47130 
47131         const float
47132           mX = mx<0?-1.0f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
47133           mY = my<0?-1.0f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
47134 
47135         area = 0;
47136         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
47137         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
47138         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
47139         if (mX>=width() && mY>=height()) area = 4;
47140         if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0;
47141 
47142         CImg<charT> filename(32);
47143 
47144         switch (key = disp.key()) {
47145 #if cimg_OS!=2
47146         case cimg::keyCTRLRIGHT :
47147 #endif
47148         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
47149         case cimg::keyPAGEUP :
47150           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
47151         case cimg::keyPAGEDOWN :
47152           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
47153         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47154             is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
47155           } break;
47156         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47157             disp.set_fullscreen(false).
47158               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
47159                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
47160               _is_resized = true;
47161             disp.set_key(key,false); key = 0; visu0.assign();
47162           } break;
47163         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47164             disp.set_fullscreen(false).
47165               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
47166             disp.set_key(key,false); key = 0; visu0.assign();
47167           } break;
47168         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47169             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
47170             disp.set_key(key,false); key = 0; visu0.assign();
47171           } break;
47172         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47173             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
47174             disp.set_key(key,false); key = 0; visu0.assign();
47175           } break;
47176         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47177             is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
47178           } break;
47179         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47180             static unsigned int snap_number = 0;
47181             std::FILE *file;
47182             do {
47183               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
47184               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47185             } while (file);
47186             if (visu0) {
47187               (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp);
47188               visu0.save(filename);
47189               (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
47190                 display(disp);
47191             }
47192             disp.set_key(key,false); key = 0;
47193           } break;
47194         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47195             static unsigned int snap_number = 0;
47196             std::FILE *file;
47197             do {
47198 #ifdef cimg_use_zlib
47199               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
47200 #else
47201               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
47202 #endif
47203               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47204             } while (file);
47205             (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp);
47206             save(filename);
47207             (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
47208               display(disp);
47209             disp.set_key(key,false); key = 0;
47210           } break;
47211         }
47212 
47213         switch (area) {
47214 
47215         case 0 : // When mouse is out of image range.
47216           mx = my = -1; X = Y = Z = -1;
47217           break;
47218 
47219         case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections.
47220           if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step).
47221             if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
47222             X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
47223           }
47224           if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes).
47225             switch (starting_area) {
47226             case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
47227             case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
47228             case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
47229             }
47230           }
47231           if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume.
47232             if (phase) {
47233               if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
47234               X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
47235             } else {
47236               if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
47237               X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
47238             }
47239           }
47240           if (disp.button()&4) {
47241             X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 0;
47242             visu0.assign();
47243           }
47244           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
47245             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
47246                 !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
47247               switch (area) {
47248               case 1 :
47249                 if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
47250                 visu0.assign(); break;
47251               case 2 :
47252                 if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
47253                 visu0.assign(); break;
47254               case 3 :
47255                 if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
47256                 visu0.assign(); break;
47257               }
47258               disp.set_wheel();
47259             } else key = ~0U;
47260           }
47261           if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released.
47262             switch (phase) {
47263             case 0 :
47264               if (area==clicked_area) {
47265                 X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; starting_area = area; ++phase;
47266               } break;
47267             case 1 :
47268               if (area==starting_area) {
47269                 X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
47270               } else if (!(disp.button()&1)) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
47271               break;
47272             case 2 : ++phase; break;
47273             }
47274             old_button = disp.button()&1;
47275           }
47276           break;
47277 
47278         case 4 : // When mouse is over the 3d view.
47279           if (is_view3d && points3d) {
47280             X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
47281             Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
47282             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
47283             // Left + right buttons: reset.
47284             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
47285             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate.
47286               const float
47287                 R = 0.45f*std::min(view3d._width,view3d._height),
47288                 R2 = R*R,
47289                 u0 = (float)(oX3d - view3d.width()/2),
47290                 v0 = (float)(oY3d - view3d.height()/2),
47291                 u1 = (float)(X3d - view3d.width()/2),
47292                 v1 = (float)(Y3d - view3d.height()/2),
47293                 n0 = cimg::hypot(u0,v0),
47294                 n1 = cimg::hypot(u1,v1),
47295                 nu0 = n0>R?(u0*R/n0):u0,
47296                 nv0 = n0>R?(v0*R/n0):v0,
47297                 nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
47298                 nu1 = n1>R?(u1*R/n1):u1,
47299                 nv1 = n1>R?(v1*R/n1):v1,
47300                 nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
47301                 u = nv0*nw1 - nw0*nv1,
47302                 v = nw0*nu1 - nu0*nw1,
47303                 w = nv0*nu1 - nu0*nv1,
47304                 n = cimg::hypot(u,v,w),
47305                 alpha = (float)std::asin(n/R2)*180/cimg::PI;
47306               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
47307               view3d.assign();
47308             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button: zoom.
47309               pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign();
47310             }
47311             if (disp.wheel()) { // Wheel: zoom
47312               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
47313             }
47314             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift.
47315               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
47316             }
47317             oX3d = X3d; oY3d = Y3d;
47318           }
47319           mx = my = -1; X = Y = Z = -1;
47320           break;
47321         }
47322 
47323         if (phase) {
47324           if (!feature_type) shape_selected = phase?true:false;
47325           else {
47326             if (_depth>1) shape_selected = (phase==3)?true:false;
47327             else shape_selected = (phase==2)?true:false;
47328           }
47329         }
47330 
47331         if (X0<0) X0 = 0;
47332         if (X0>=width()) X0 = width() - 1;
47333         if (Y0<0) Y0 = 0;
47334         if (Y0>=height()) Y0 = height() - 1;
47335         if (Z0<0) Z0 = 0;
47336         if (Z0>=depth()) Z0 = depth() - 1;
47337         if (X1<1) X1 = 0;
47338         if (X1>=width()) X1 = width() - 1;
47339         if (Y1<0) Y1 = 0;
47340         if (Y1>=height()) Y1 = height() - 1;
47341         if (Z1<0) Z1 = 0;
47342         if (Z1>=depth()) Z1 = depth() - 1;
47343 
47344         // Draw visualization image on the display
47345         if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
47346 
47347           if (!visu0) { // Create image of projected planes.
47348             if (thumb) thumb.__get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
47349             else __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
47350             visu0.resize(disp);
47351             view3d.assign();
47352             points3d.assign();
47353           }
47354 
47355           if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
47356             const unsigned int
47357               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
47358               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
47359               x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
47360               y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
47361             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
47362               move_to(view3d);
47363             if (!points3d) {
47364               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
47365               points3d.append(CImg<floatT>(8,3,1,1,
47366                                            0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
47367                                            0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
47368                                            0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
47369               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
47370               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
47371               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
47372               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
47373               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
47374               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
47375               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
47376               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
47377               if (!phase) {
47378                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
47379                 sel_primitives3d.assign();
47380                 sel_colors3d.assign();
47381                 sel_opacities3d.assign();
47382               } else {
47383                 if (feature_type==2) {
47384                   points3d.append(CImg<floatT>(8,3,1,1,
47385                                                X0,X1,X1,X0,X0,X1,X1,X0,
47386                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
47387                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
47388                   sel_primitives3d.assign();
47389                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
47390                   CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
47391                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
47392                   CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
47393                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
47394                   CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
47395                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
47396                   CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
47397                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
47398                   CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
47399                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
47400                   CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
47401                 } else {
47402                   points3d.append(CImg<floatT>(2,3,1,1,
47403                                                X0,X1,
47404                                                Y0,Y1,
47405                                                Z0,Z1),'x');
47406                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
47407                 }
47408                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
47409                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
47410               }
47411               points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
47412               points3d*=0.75f*std::min(view3d._width,view3d._height);
47413             }
47414 
47415             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
47416             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
47417             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
47418             if (sel_primitives3d)
47419               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
47420                                    pose3d(3,1) + 0.5f*view3d._height,
47421                                    pose3d(3,2),
47422                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
47423                                    2,true,500,0,0,0,0,0,zbuffer3d);
47424             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
47425                                  pose3d(3,1) + 0.5f*view3d._height,
47426                                  pose3d(3,2),
47427                                  rotated_points3d,primitives3d,colors3d,opacities3d,
47428                                  2,true,500,0,0,0,0,0,zbuffer3d);
47429             visu0.draw_image(x3d,y3d,view3d);
47430           }
47431           visu = visu0;
47432 
47433           if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
47434           else {
47435             if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
47436             else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
47437             const int d = (depth()>1)?depth():0;
47438             int
47439               _vX = (int)X, _vY = (int)Y, _vZ = (int)Z,
47440               w = disp.width(), W = width() + d,
47441               h = disp.height(), H = height() + d,
47442               _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
47443               _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
47444               _xn = (int)((_vX + 1.0f)*w/W - 1), xn = _xn + ((int)((_xn + 1.0f)*W/w)!=_vX + 1),
47445               _yn = (int)((_vY + 1.0f)*h/H - 1), yn = _yn + ((int)((_yn + 1.0f)*H/h)!=_vY + 1),
47446               _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
47447               _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
47448               _zxn = (int)((_vZ + width() + 1.0f)*w/W - 1),
47449                        zxn = _zxn + ((int)((_zxn + 1.0f)*W/w)!=_vZ + width() + 1),
47450               _zyn = (int)((_vZ + height() + 1.0f)*h/H - 1),
47451                        zyn = _zyn + ((int)((_zyn + 1.0f)*H/h)!=_vZ + height() + 1),
47452               _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.0f)*W/w)!=width()),
47453               _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.0f)*H/h)!=height()),
47454               xc = (xp + xn)/2,
47455               yc = (yp + yn)/2,
47456               zxc = (zxp + zxn)/2,
47457               zyc = (zyp + zyn)/2,
47458               xf = (int)(X*w/W),
47459               yf = (int)(Y*h/H),
47460               zxf = (int)((Z + width())*w/W),
47461               zyf = (int)((Z + height())*h/H);
47462 
47463             if (is_axes) { // Draw axes.
47464               visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
47465                 draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
47466                 draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
47467                 draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
47468               if (_depth>1)
47469                 visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
47470                   draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
47471                   draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
47472                   draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
47473             }
47474 
47475             // Draw box cursor.
47476             if (xn - xp>=4 && yn - yp>=4) visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
47477                                         draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
47478                                         draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
47479             if (_depth>1) {
47480               if (yn - yp>=4 && zxn - zxp>=4) visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
47481                                             draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
47482                                             draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
47483               if (xn - xp>=4 && zyn - zyp>=4) visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
47484                                             draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
47485                                             draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
47486             }
47487 
47488             // Draw selection.
47489             if (phase) {
47490               const int
47491                 _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
47492                 _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
47493                 _xn0 = (int)((X0 + 1.0f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.0f)*W/w)!=X0 + 1),
47494                 _yn0 = (int)((Y0 + 1.0f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.0f)*H/h)!=Y0 + 1),
47495                 _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
47496                 _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
47497                 _zxn0 = (int)((Z0 + width() + 1.0f)*w/W - 1),
47498                 zxn0 = _zxn0 + ((int)((_zxn0 + 1.0f)*W/w)!=Z0 + width() + 1),
47499                 _zyn0 = (int)((Z0 + height() + 1.0f)*h/H - 1),
47500                 zyn0 = _zyn0 + ((int)((_zyn0 + 1.0f)*H/h)!=Z0 + height() + 1),
47501                 xc0 = (xp0 + xn0)/2,
47502                 yc0 = (yp0 + yn0)/2,
47503                 zxc0 = (zxp0 + zxn0)/2,
47504                 zyc0 = (zyp0 + zyn0)/2;
47505 
47506               switch (feature_type) {
47507               case 1 : {
47508                 visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555).
47509                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA);
47510                 if (d) {
47511                   visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555).
47512                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA).
47513                     draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555).
47514                     draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA);
47515                 }
47516               } break;
47517               case 2 : {
47518                 visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
47519                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
47520                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA).
47521                   draw_arrow(xc0,yc0,xc,yc,background_color,0.5f,30,5,0x55555555).
47522                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA);
47523                 if (d) {
47524                   visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
47525                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
47526                                    background_color,0.9f,0x55555555).
47527                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
47528                                    foreground_color,0.9f,0xAAAAAAAA).
47529                     draw_arrow(zxc0,yc0,zxc,yc,background_color,0.5f,30,5,0x55555555).
47530                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA).
47531                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47532                                    background_color,0.2f).
47533                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47534                                    background_color,0.9f,0x55555555).
47535                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47536                                    foreground_color,0.9f,0xAAAAAAAA).
47537                     draw_arrow(xp0,zyp0,xn,zyn,background_color,0.5f,30,5,0x55555555).
47538                     draw_arrow(xp0,zyp0,xn,zyn,foreground_color,0.5f,30,5,0xAAAAAAAA);
47539                 }
47540               } break;
47541               case 3 : {
47542                 visu.draw_ellipse(xc0,yc0,
47543                                   (float)cimg::abs(xc - xc0),
47544                                   (float)cimg::abs(yc - yc0),0,background_color,0.2f).
47545                   draw_ellipse(xc0,yc0,
47546                                (float)cimg::abs(xc - xc0),
47547                                (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
47548                   draw_point(xc0,yc0,foreground_color,0.9f);
47549                 if (d) {
47550                   visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
47551                                     background_color,0.2f).
47552                     draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
47553                                  foreground_color,0.9f,~0U).
47554                     draw_point(zxc0,yc0,foreground_color,0.9f).
47555                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
47556                                  background_color,0.2f).
47557                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
47558                                  foreground_color,0.9f,~0U).
47559                     draw_point(xc0,zyc0,foreground_color,0.9f);
47560                 }
47561               } break;
47562               }
47563             }
47564 
47565             // Draw text info.
47566             if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
47567             if (!feature_type || !phase) {
47568               if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
47569                 if (_depth>1 || force_display_z_coord)
47570                   cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
47571                 else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
47572                 CImg<T> values = get_vector_at(X,Y,Z);
47573                 const bool is_large_spectrum = values._height>16;
47574                 if (is_large_spectrum)
47575                   values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0);
47576                 char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
47577                 for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
47578                   cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
47579                                 cimg::type<T>::format(values[c]));
47580                   ctext += std::strlen(ctext);
47581                   if (c==7 && is_large_spectrum) {
47582                     cimg_snprintf(ctext,24," (...)");
47583                     ctext += std::strlen(ctext);
47584                   }
47585                   *(ctext++) = ' '; *ctext = 0;
47586                 }
47587                 std::strcpy(text._data + std::strlen(text),"] ");
47588               }
47589             } else switch (feature_type) {
47590               case 1 : {
47591                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
47592                   length = cimg::hypot(dX,dY,dZ);
47593                 if (_depth>1 || force_display_z_coord)
47594                   cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
47595                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
47596                 else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
47597                                    origX + X0,origY + Y0,origX + X1,origY + Y1,length);
47598               } break;
47599               case 2 : {
47600                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
47601                   length = cimg::hypot(dX,dY,dZ);
47602                 if (_depth>1 || force_display_z_coord)
47603                   cimg_snprintf(text,text._width," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d), Length = %g ",
47604                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
47605                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
47606                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
47607                 else cimg_snprintf(text,text._width," Box (%d,%d)-(%d,%d), Size = (%d,%d), Length = %g ",
47608                                    origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
47609                                    origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
47610                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
47611               } break;
47612               default :
47613                 if (_depth>1 || force_display_z_coord)
47614                   cimg_snprintf(text,text._width," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
47615                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
47616                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
47617                 else cimg_snprintf(text,text._width," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
47618                                    origX + X0,origY + Y0,origX + X1,origY + Y1,
47619                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
47620               }
47621             if (phase || (mx>=0 && my>=0))
47622               visu.draw_text(0,text_down?visu.height() - 13:0,text,foreground_color,background_color,0.7f,13);
47623           }
47624 
47625           disp.display(visu).wait();
47626         } else if (!shape_selected) disp.wait();
47627         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
47628         omx = mx; omy = my;
47629         if (!exit_on_anykey && key && key!=cimg::keyESC &&
47630             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
47631           key = 0;
47632         }
47633       }
47634 
47635       // Return result.
47636       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
47637       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
47638       if (shape_selected) {
47639         if (feature_type==2) {
47640           if (X0>X1) cimg::swap(X0,X1);
47641           if (Y0>Y1) cimg::swap(Y0,Y1);
47642           if (Z0>Z1) cimg::swap(Z0,Z1);
47643         }
47644 	if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
47645 	switch (feature_type) {
47646 	case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
47647         case 3 :
47648           res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
47649           res[0] = X0; res[1] = Y0; res[2] = Z0;
47650           break;
47651 	default : res[0] = X0; res[1] = Y0; res[2] = Z0;
47652 	}
47653       }
47654       if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
47655       if (!visible_cursor) disp.show_mouse();
47656       disp._normalization = old_normalization;
47657       disp._is_resized = old_is_resized;
47658       if (key!=~0U) disp.set_key(key);
47659       return res;
47660     }
47661 
47662     // Return a visualizable uchar8 image for display routines.
47663     CImg<ucharT> __get_select(const CImgDisplay& disp, const int normalization,
47664                               const int x, const int y, const int z) const {
47665       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
47666       const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
47667       CImg<Tuchar> img2d;
47668       if (_depth>1) {
47669         const int mdisp = std::min(disp.screen_width(),disp.screen_height());
47670         if (depth()>mdisp) {
47671           crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
47672           img2d.projections2d(x,y,z*img2d._depth/_depth);
47673         } else crop.get_projections2d(x,y,z).move_to(img2d);
47674       } else CImg<Tuchar>(crop,false).move_to(img2d);
47675 
47676       // Check for inf and NaN values.
47677       if (cimg::type<T>::is_float() && normalization) {
47678         bool is_inf = false, is_nan = false;
47679         cimg_for(img2d,ptr,Tuchar)
47680           if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
47681           else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
47682         if (is_inf || is_nan) {
47683           Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
47684           if (!normalization) { m0 = 0; M0 = 255; }
47685           else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
47686           else
47687             cimg_for(img2d,ptr,Tuchar)
47688               if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
47689                 if (*ptr<(Tuchar)m0) m0 = *ptr;
47690                 if (*ptr>(Tuchar)M0) M0 = *ptr;
47691               }
47692           const T
47693             val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0),
47694             val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0);
47695           if (is_nan)
47696             cimg_for(img2d,ptr,Tuchar)
47697               if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values.
47698           if (is_inf)
47699             cimg_for(img2d,ptr,Tuchar)
47700               if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values.
47701         }
47702       }
47703 
47704       switch (normalization) {
47705       case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
47706       case 2 : {
47707         const float m = disp._min, M = disp._max;
47708         (img2d-=m)*=255.0f/(M - m>0?M - m:1);
47709       } break;
47710       case 3 :
47711         if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
47712         else {
47713           const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
47714           (img2d-=m)*=255.0f/(M - m>0?M - m:1);
47715         } break;
47716       }
47717       if (img2d.spectrum()==2) img2d.channels(0,2);
47718       return img2d;
47719     }
47720 
47721     //! Select sub-graph in a graph.
47722     CImg<intT> get_select_graph(CImgDisplay &disp,
47723                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
47724                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
47725                                 const char *const labely=0, const double ymin=0, const double ymax=0,
47726                                 const bool exit_on_anykey=false) const {
47727       if (is_empty())
47728         throw CImgInstanceException(_cimg_instance
47729                                     "select_graph(): Empty instance.",
47730                                     cimg_instance);
47731       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
47732                    set_title("CImg<%s>",pixel_type());
47733       const ulongT siz = (ulongT)_width*_height*_depth;
47734       const unsigned int old_normalization = disp.normalization();
47735       disp.show().set_button().set_wheel()._normalization = 0;
47736 
47737       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
47738       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
47739       if (nymin==nymax) { --nymin; ++nymax; }
47740       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
47741 
47742       static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
47743       static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
47744       static unsigned int odimv = 0;
47745       static CImg<ucharT> colormap;
47746       if (odimv!=_spectrum) {
47747         odimv = _spectrum;
47748         colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
47749         if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
47750         else {
47751           colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
47752           if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
47753           if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
47754         }
47755       }
47756 
47757       CImg<ucharT> visu0, visu, graph, text, axes;
47758       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
47759       const unsigned int one = plot_type==3?0U:1U;
47760       unsigned int okey = 0, obutton = 0;
47761       CImg<charT> message(1024);
47762       CImg_3x3(I,unsigned char);
47763 
47764       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
47765         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
47766         const unsigned int key = disp.key(), button = disp.button();
47767 
47768         // Generate graph representation.
47769         if (!visu0) {
47770           visu0.assign(disp.width(),disp.height(),1,3,220);
47771           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
47772           if (gdimx>0 && gdimy>0) {
47773             graph.assign(gdimx,gdimy,1,3,255);
47774             if (siz<32) {
47775               if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
47776                                          false,true,black,0.2f,0x33333333,0x33333333);
47777             } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
47778             cimg_forC(*this,c)
47779               graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
47780                                plot_type,vertex_type,nymax,nymin);
47781 
47782             axes.assign(gdimx,gdimy,1,1,0);
47783             const float
47784               dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
47785               px = (float)std::pow(10.0,(int)std::log10(dx?dx:1) - 2.0),
47786               py = (float)std::pow(10.0,(int)std::log10(dy?dy:1) - 2.0);
47787             const CImg<Tdouble>
47788               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
47789                 CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz).round(px),
47790               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
47791 
47792             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
47793             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
47794             if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero);
47795             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
47796 	    if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
47797 	    if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero);
47798 
47799             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
47800               if (Icc) {
47801                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
47802                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
47803               }
47804               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
47805                 cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
47806 
47807             visu0.draw_image(16,16,graph);
47808 	    visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
47809 	      draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
47810           } else graph.assign();
47811           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
47812           visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
47813           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
47814           visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
47815           visu.assign();
47816         }
47817 
47818         // Generate and display current view.
47819         if (!visu) {
47820           visu.assign(visu0);
47821           if (graph && x0>=0 && x1>=0) {
47822             const int
47823               nx0 = x0<=x1?x0:x1,
47824               nx1 = x0<=x1?x1:x0,
47825               ny0 = y0<=y1?y0:y1,
47826               ny1 = y0<=y1?y1:y0,
47827               sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
47828               sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
47829               sy0 = 16 + ny0,
47830               sy1 = 16 + ny1;
47831             if (y0>=0 && y1>=0)
47832               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
47833             else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
47834                    draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
47835                    draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
47836           }
47837           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
47838             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
47839             const unsigned int
47840               x = (unsigned int)cimg::round((mouse_x - 16.0f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
47841             const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
47842             if (_spectrum>=7)
47843               cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
47844                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
47845                             (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
47846                             (double)(*this)(x,0,0,_spectrum - 1));
47847             else {
47848               cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
47849               cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
47850               cimg_sprintf(message._data + std::strlen(message),")");
47851             }
47852 	    if (x0>=0 && x1>=0) {
47853 	      const unsigned int
47854                 nx0 = (unsigned int)(x0<=x1?x0:x1),
47855                 nx1 = (unsigned int)(x0<=x1?x1:x0),
47856                 ny0 = (unsigned int)(y0<=y1?y0:y1),
47857                 ny1 = (unsigned int)(y0<=y1?y1:y0);
47858 	      const double
47859                 cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
47860                 cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
47861                 cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
47862                 cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
47863 	      if (y0>=0 && y1>=0)
47864 	        cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
47865                              x0,cx0,cy0,x1 + one,cx1,cy1);
47866 	      else
47867 	        cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
47868                              x0,cx0,x1 + one,cx1);
47869 	    }
47870             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
47871             visu.draw_image((visu.width() - text.width())/2,1,~text);
47872           }
47873           visu.display(disp);
47874         }
47875 
47876         // Test keys.
47877         CImg<charT> filename(32);
47878         switch (okey = key) {
47879 #if cimg_OS!=2
47880         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
47881 #endif
47882         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
47883         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47884           disp.set_fullscreen(false).
47885             resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
47886                    CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
47887             _is_resized = true;
47888           disp.set_key(key,false); okey = 0;
47889         } break;
47890         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47891           disp.set_fullscreen(false).
47892             resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
47893           disp.set_key(key,false); okey = 0;
47894         } break;
47895         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47896             disp.set_fullscreen(false).
47897               resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
47898                                     CImgDisplay::screen_height()/2,1),false)._is_resized = true;
47899             disp.set_key(key,false); okey = 0;
47900           } break;
47901         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47902             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
47903             disp.set_key(key,false); okey = 0;
47904           } break;
47905         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47906             static unsigned int snap_number = 0;
47907             if (visu || visu0) {
47908               CImg<ucharT> &screen = visu?visu:visu0;
47909               std::FILE *file;
47910               do {
47911                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
47912                 if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47913               } while (file);
47914               (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
47915               screen.save(filename);
47916               (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename._data).display(disp);
47917             }
47918             disp.set_key(key,false); okey = 0;
47919           } break;
47920         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47921             static unsigned int snap_number = 0;
47922             if (visu || visu0) {
47923               CImg<ucharT> &screen = visu?visu:visu0;
47924               std::FILE *file;
47925               do {
47926 #ifdef cimg_use_zlib
47927                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
47928 #else
47929                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
47930 #endif
47931                 if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47932               } while (file);
47933               (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
47934               save(filename);
47935               (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename._data).display(disp);
47936             }
47937             disp.set_key(key,false); okey = 0;
47938           } break;
47939         }
47940 
47941         // Handle mouse motion and mouse buttons
47942         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
47943           visu.assign();
47944           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
47945             const int
47946               mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
47947               cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
47948               my = mouse_y - 16,
47949               cy = cimg::cut(my,0,disp.height() - 32);
47950 	    if (button&1) {
47951               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
47952             }
47953 	    else if (button&2) {
47954               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
47955             }
47956             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
47957           } else if (!button && obutton) selected = true;
47958           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
47959         }
47960         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
47961         if (visu && visu0) disp.wait();
47962         if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
47963             (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
47964           disp.set_key(key,false);
47965           okey = 0;
47966         }
47967       }
47968 
47969       disp._normalization = old_normalization;
47970       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
47971       if (y1<y0) cimg::swap(y0,y1);
47972       disp.set_key(okey);
47973       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
47974     }
47975 
47976     //! Load image from a file.
47977     /**
47978        \param filename Filename, as a C-string.
47979        \note The extension of \c filename defines the file format. If no filename
47980        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
47981     **/
47982     CImg<T>& load(const char *const filename) {
47983       if (!filename)
47984         throw CImgArgumentException(_cimg_instance
47985                                     "load(): Specified filename is (null).",
47986                                     cimg_instance);
47987 
47988       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
47989         CImg<charT> filename_local(256);
47990         load(cimg::load_network(filename,filename_local));
47991         std::remove(filename_local);
47992         return *this;
47993       }
47994 
47995       const char *const ext = cimg::split_filename(filename);
47996       const unsigned int omode = cimg::exception_mode();
47997       cimg::exception_mode(0);
47998       bool is_loaded = true;
47999       try {
48000 #ifdef cimg_load_plugin
48001         cimg_load_plugin(filename);
48002 #endif
48003 #ifdef cimg_load_plugin1
48004         cimg_load_plugin1(filename);
48005 #endif
48006 #ifdef cimg_load_plugin2
48007         cimg_load_plugin2(filename);
48008 #endif
48009 #ifdef cimg_load_plugin3
48010         cimg_load_plugin3(filename);
48011 #endif
48012 #ifdef cimg_load_plugin4
48013         cimg_load_plugin4(filename);
48014 #endif
48015 #ifdef cimg_load_plugin5
48016         cimg_load_plugin5(filename);
48017 #endif
48018 #ifdef cimg_load_plugin6
48019         cimg_load_plugin6(filename);
48020 #endif
48021 #ifdef cimg_load_plugin7
48022         cimg_load_plugin7(filename);
48023 #endif
48024 #ifdef cimg_load_plugin8
48025         cimg_load_plugin8(filename);
48026 #endif
48027         // Ascii formats
48028         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
48029         else if (!cimg::strcasecmp(ext,"dlm") ||
48030                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
48031 
48032         // 2d binary formats
48033         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
48034         else if (!cimg::strcasecmp(ext,"jpg") ||
48035                  !cimg::strcasecmp(ext,"jpeg") ||
48036                  !cimg::strcasecmp(ext,"jpe") ||
48037                  !cimg::strcasecmp(ext,"jfif") ||
48038                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
48039         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
48040         else if (!cimg::strcasecmp(ext,"ppm") ||
48041                  !cimg::strcasecmp(ext,"pgm") ||
48042                  !cimg::strcasecmp(ext,"pnm") ||
48043                  !cimg::strcasecmp(ext,"pbm") ||
48044                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
48045         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
48046         else if (!cimg::strcasecmp(ext,"tif") ||
48047                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
48048         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
48049         else if (!cimg::strcasecmp(ext,"cr2") ||
48050                  !cimg::strcasecmp(ext,"crw") ||
48051                  !cimg::strcasecmp(ext,"dcr") ||
48052                  !cimg::strcasecmp(ext,"mrw") ||
48053                  !cimg::strcasecmp(ext,"nef") ||
48054                  !cimg::strcasecmp(ext,"orf") ||
48055                  !cimg::strcasecmp(ext,"pix") ||
48056                  !cimg::strcasecmp(ext,"ptx") ||
48057                  !cimg::strcasecmp(ext,"raf") ||
48058                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
48059         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
48060 
48061         // 3d binary formats
48062         else if (!cimg::strcasecmp(ext,"dcm") ||
48063                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
48064         else if (!cimg::strcasecmp(ext,"hdr") ||
48065                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
48066         else if (!cimg::strcasecmp(ext,"par") ||
48067                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
48068         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
48069         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
48070         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
48071         else if (!cimg::strcasecmp(ext,"cimg") ||
48072                  !cimg::strcasecmp(ext,"cimgz") ||
48073                  !*ext)  return load_cimg(filename);
48074 
48075         // Archive files
48076         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
48077 
48078         // Image sequences
48079         else if (!cimg::strcasecmp(ext,"avi") ||
48080                  !cimg::strcasecmp(ext,"mov") ||
48081                  !cimg::strcasecmp(ext,"asf") ||
48082                  !cimg::strcasecmp(ext,"divx") ||
48083                  !cimg::strcasecmp(ext,"flv") ||
48084                  !cimg::strcasecmp(ext,"mpg") ||
48085                  !cimg::strcasecmp(ext,"m1v") ||
48086                  !cimg::strcasecmp(ext,"m2v") ||
48087                  !cimg::strcasecmp(ext,"m4v") ||
48088                  !cimg::strcasecmp(ext,"mjp") ||
48089                  !cimg::strcasecmp(ext,"mp4") ||
48090                  !cimg::strcasecmp(ext,"mkv") ||
48091                  !cimg::strcasecmp(ext,"mpe") ||
48092                  !cimg::strcasecmp(ext,"movie") ||
48093                  !cimg::strcasecmp(ext,"ogm") ||
48094                  !cimg::strcasecmp(ext,"ogg") ||
48095                  !cimg::strcasecmp(ext,"ogv") ||
48096                  !cimg::strcasecmp(ext,"qt") ||
48097                  !cimg::strcasecmp(ext,"rm") ||
48098                  !cimg::strcasecmp(ext,"vob") ||
48099                  !cimg::strcasecmp(ext,"wmv") ||
48100                  !cimg::strcasecmp(ext,"xvid") ||
48101                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
48102         else is_loaded = false;
48103       } catch (CImgIOException&) { is_loaded = false; }
48104 
48105       // If nothing loaded, try to guess file format from magic number in file.
48106       if (!is_loaded) {
48107         std::FILE *file = std_fopen(filename,"rb");
48108         if (!file) {
48109           cimg::exception_mode(omode);
48110           throw CImgIOException(_cimg_instance
48111                                 "load(): Failed to open file '%s'.",
48112                                 cimg_instance,
48113                                 filename);
48114         }
48115 
48116         const char *const f_type = cimg::ftype(file,filename);
48117         std::fclose(file);
48118         is_loaded = true;
48119         try {
48120           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
48121           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
48122           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
48123           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
48124           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
48125           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
48126           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
48127           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
48128           else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
48129           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
48130           else is_loaded = false;
48131         } catch (CImgIOException&) { is_loaded = false; }
48132       }
48133 
48134       // If nothing loaded, try to load file with other means.
48135       if (!is_loaded) {
48136         try {
48137           load_other(filename);
48138         } catch (CImgIOException&) {
48139           cimg::exception_mode(omode);
48140           throw CImgIOException(_cimg_instance
48141                                 "load(): Failed to recognize format of file '%s'.",
48142                                 cimg_instance,
48143                                 filename);
48144         }
48145       }
48146       cimg::exception_mode(omode);
48147       return *this;
48148     }
48149 
48150     //! Load image from a file \newinstance.
48151     static CImg<T> get_load(const char *const filename) {
48152       return CImg<T>().load(filename);
48153     }
48154 
48155     //! Load image from an ascii file.
48156     /**
48157        \param filename Filename, as a C -string.
48158     **/
48159     CImg<T>& load_ascii(const char *const filename) {
48160       return _load_ascii(0,filename);
48161     }
48162 
48163     //! Load image from an ascii file \inplace.
48164     static CImg<T> get_load_ascii(const char *const filename) {
48165       return CImg<T>().load_ascii(filename);
48166     }
48167 
48168     //! Load image from an ascii file \overloading.
48169     CImg<T>& load_ascii(std::FILE *const file) {
48170       return _load_ascii(file,0);
48171     }
48172 
48173     //! Loadimage from an ascii file \newinstance.
48174     static CImg<T> get_load_ascii(std::FILE *const file) {
48175       return CImg<T>().load_ascii(file);
48176     }
48177 
48178     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
48179       if (!file && !filename)
48180         throw CImgArgumentException(_cimg_instance
48181                                     "load_ascii(): Specified filename is (null).",
48182                                     cimg_instance);
48183 
48184       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48185       CImg<charT> line(256); *line = 0;
48186       int err = std::fscanf(nfile,"%255[^\n]",line._data);
48187       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
48188       cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
48189       err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
48190       if (!dx || !dy || !dz || !dc) {
48191         if (!file) cimg::fclose(nfile);
48192         throw CImgIOException(_cimg_instance
48193                               "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
48194                               "to (%u,%u,%u,%u).",
48195                               cimg_instance,
48196                               filename?filename:"(FILE*)",dx,dy,dz,dc);
48197       }
48198       assign(dx,dy,dz,dc);
48199       const ulongT siz = size();
48200       ulongT off = 0;
48201       double val;
48202       T *ptr = _data;
48203       for (err = 1, off = 0; off<siz && err==1; ++off) {
48204         err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
48205         *(ptr++) = (T)val;
48206       }
48207       if (err!=1)
48208         cimg::warn(_cimg_instance
48209                    "load_ascii(): Only %lu/%lu values read from file '%s'.",
48210                    cimg_instance,
48211                    off - 1,siz,filename?filename:"(FILE*)");
48212 
48213       if (!file) cimg::fclose(nfile);
48214       return *this;
48215     }
48216 
48217     //! Load image from a DLM file.
48218     /**
48219       \param filename Filename, as a C-string.
48220     **/
48221     CImg<T>& load_dlm(const char *const filename) {
48222       return _load_dlm(0,filename);
48223     }
48224 
48225     //! Load image from a DLM file \newinstance.
48226     static CImg<T> get_load_dlm(const char *const filename) {
48227       return CImg<T>().load_dlm(filename);
48228     }
48229 
48230     //! Load image from a DLM file \overloading.
48231     CImg<T>& load_dlm(std::FILE *const file) {
48232       return _load_dlm(file,0);
48233     }
48234 
48235     //! Load image from a DLM file \newinstance.
48236     static CImg<T> get_load_dlm(std::FILE *const file) {
48237       return CImg<T>().load_dlm(file);
48238     }
48239 
48240     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
48241       if (!file && !filename)
48242         throw CImgArgumentException(_cimg_instance
48243                                     "load_dlm(): Specified filename is (null).",
48244                                     cimg_instance);
48245 
48246       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
48247       CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
48248       unsigned int cdx = 0, dx = 0, dy = 0;
48249       int err = 0;
48250       double val;
48251       assign(256,256,1,1,(T)0);
48252       while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
48253         if (err>0) (*this)(cdx++,dy) = (T)val;
48254         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
48255         char c = 0;
48256         if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
48257           dx = std::max(cdx,dx);
48258           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
48259           cdx = 0;
48260         }
48261       }
48262       if (cdx && err==1) { dx = cdx; ++dy; }
48263       if (!dx || !dy) {
48264         if (!file) cimg::fclose(nfile);
48265         throw CImgIOException(_cimg_instance
48266                               "load_dlm(): Invalid DLM file '%s'.",
48267                               cimg_instance,
48268                               filename?filename:"(FILE*)");
48269       }
48270       resize(dx,dy,1,1,0);
48271       if (!file) cimg::fclose(nfile);
48272       return *this;
48273     }
48274 
48275     //! Load image from a BMP file.
48276     /**
48277        \param filename Filename, as a C-string.
48278     **/
48279     CImg<T>& load_bmp(const char *const filename) {
48280       return _load_bmp(0,filename);
48281     }
48282 
48283     //! Load image from a BMP file \newinstance.
48284     static CImg<T> get_load_bmp(const char *const filename) {
48285       return CImg<T>().load_bmp(filename);
48286     }
48287 
48288     //! Load image from a BMP file \overloading.
48289     CImg<T>& load_bmp(std::FILE *const file) {
48290       return _load_bmp(file,0);
48291     }
48292 
48293     //! Load image from a BMP file \newinstance.
48294     static CImg<T> get_load_bmp(std::FILE *const file) {
48295       return CImg<T>().load_bmp(file);
48296     }
48297 
48298     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
48299       if (!file && !filename)
48300         throw CImgArgumentException(_cimg_instance
48301                                     "load_bmp(): Specified filename is (null).",
48302                                     cimg_instance);
48303 
48304       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48305       CImg<ucharT> header(54);
48306       cimg::fread(header._data,54,nfile);
48307       if (*header!='B' || header[1]!='M') {
48308         if (!file) cimg::fclose(nfile);
48309         throw CImgIOException(_cimg_instance
48310                               "load_bmp(): Invalid BMP file '%s'.",
48311                               cimg_instance,
48312                               filename?filename:"(FILE*)");
48313       }
48314 
48315       // Read header and pixel buffer
48316       int
48317         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
48318         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
48319         header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
48320         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
48321         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
48322         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
48323         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
48324         bpp = header[0x1C] + (header[0x1D]<<8);
48325 
48326       if (!file_size || file_size==offset) {
48327         cimg::fseek(nfile,0,SEEK_END);
48328         file_size = (int)cimg::ftell(nfile);
48329         cimg::fseek(nfile,54,SEEK_SET);
48330       }
48331       if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
48332 
48333       const int
48334         dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
48335         align_bytes = (4 - dx_bytes%4)%4;
48336       const ulongT
48337         cimg_iobuffer = (ulongT)24*1024*1024,
48338         buf_size = std::min((ulongT)cimg::abs(dy)*(dx_bytes + align_bytes),(ulongT)file_size - offset);
48339 
48340       CImg<intT> colormap;
48341       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
48342       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
48343       const int xoffset = offset - 14 - header_size - 4*nb_colors;
48344       if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
48345 
48346       CImg<ucharT> buffer;
48347       if (buf_size<cimg_iobuffer) {
48348         buffer.assign(cimg::abs(dy)*(dx_bytes + align_bytes),1,1,1,0);
48349         cimg::fread(buffer._data,buf_size,nfile);
48350       } else buffer.assign(dx_bytes + align_bytes);
48351       unsigned char *ptrs = buffer;
48352 
48353       // Decompress buffer (if necessary)
48354       if (compression) {
48355         if (file)
48356           throw CImgIOException(_cimg_instance
48357                                 "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
48358                                 cimg_instance);
48359         else {
48360           if (!file) cimg::fclose(nfile);
48361           return load_other(filename);
48362         }
48363       }
48364 
48365       // Read pixel data
48366       assign(dx,cimg::abs(dy),1,3,0);
48367       switch (bpp) {
48368       case 1 : { // Monochrome
48369         if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
48370           if (buf_size>=cimg_iobuffer) {
48371             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48372             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48373           }
48374           unsigned char mask = 0x80, val = 0;
48375           cimg_forX(*this,x) {
48376             if (mask==0x80) val = *(ptrs++);
48377             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
48378             (*this)(x,y,2) = (T)*(col++);
48379             (*this)(x,y,1) = (T)*(col++);
48380             (*this)(x,y,0) = (T)*(col++);
48381             mask = cimg::ror(mask);
48382           }
48383           ptrs+=align_bytes;
48384         }
48385       } break;
48386       case 4 : { // 16 colors
48387         if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
48388           if (buf_size>=cimg_iobuffer) {
48389             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48390             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48391           }
48392           unsigned char mask = 0xF0, val = 0;
48393           cimg_forX(*this,x) {
48394             if (mask==0xF0) val = *(ptrs++);
48395             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
48396             const unsigned char *col = (unsigned char*)(colormap._data + color);
48397             (*this)(x,y,2) = (T)*(col++);
48398             (*this)(x,y,1) = (T)*(col++);
48399             (*this)(x,y,0) = (T)*(col++);
48400             mask = cimg::ror(mask,4);
48401           }
48402           ptrs+=align_bytes;
48403         }
48404       } break;
48405       case 8 : { // 256 colors
48406         if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
48407           if (buf_size>=cimg_iobuffer) {
48408             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48409             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48410           }
48411           cimg_forX(*this,x) {
48412             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
48413             (*this)(x,y,2) = (T)*(col++);
48414             (*this)(x,y,1) = (T)*(col++);
48415             (*this)(x,y,0) = (T)*(col++);
48416           }
48417           ptrs+=align_bytes;
48418         }
48419       } break;
48420       case 16 : { // 16 bits colors
48421         for (int y = height() - 1; y>=0; --y) {
48422           if (buf_size>=cimg_iobuffer) {
48423             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48424             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48425           }
48426           cimg_forX(*this,x) {
48427             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
48428             const unsigned short col = (unsigned short)(c1|(c2<<8));
48429             (*this)(x,y,2) = (T)(col&0x1F);
48430             (*this)(x,y,1) = (T)((col>>5)&0x1F);
48431             (*this)(x,y,0) = (T)((col>>10)&0x1F);
48432           }
48433           ptrs+=align_bytes;
48434         }
48435       } break;
48436       case 24 : { // 24 bits colors
48437         for (int y = height() - 1; y>=0; --y) {
48438           if (buf_size>=cimg_iobuffer) {
48439             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48440             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48441           }
48442           cimg_forX(*this,x) {
48443             (*this)(x,y,2) = (T)*(ptrs++);
48444             (*this)(x,y,1) = (T)*(ptrs++);
48445             (*this)(x,y,0) = (T)*(ptrs++);
48446           }
48447           ptrs+=align_bytes;
48448         }
48449       } break;
48450       case 32 : { // 32 bits colors
48451         for (int y = height() - 1; y>=0; --y) {
48452           if (buf_size>=cimg_iobuffer) {
48453             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48454             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48455           }
48456           cimg_forX(*this,x) {
48457             (*this)(x,y,2) = (T)*(ptrs++);
48458             (*this)(x,y,1) = (T)*(ptrs++);
48459             (*this)(x,y,0) = (T)*(ptrs++);
48460             ++ptrs;
48461           }
48462           ptrs+=align_bytes;
48463         }
48464       } break;
48465       }
48466       if (dy<0) mirror('y');
48467       if (!file) cimg::fclose(nfile);
48468       return *this;
48469     }
48470 
48471     //! Load image from a JPEG file.
48472     /**
48473        \param filename Filename, as a C-string.
48474     **/
48475     CImg<T>& load_jpeg(const char *const filename) {
48476       return _load_jpeg(0,filename);
48477     }
48478 
48479     //! Load image from a JPEG file \newinstance.
48480     static CImg<T> get_load_jpeg(const char *const filename) {
48481       return CImg<T>().load_jpeg(filename);
48482     }
48483 
48484     //! Load image from a JPEG file \overloading.
48485     CImg<T>& load_jpeg(std::FILE *const file) {
48486       return _load_jpeg(file,0);
48487     }
48488 
48489     //! Load image from a JPEG file \newinstance.
48490     static CImg<T> get_load_jpeg(std::FILE *const file) {
48491       return CImg<T>().load_jpeg(file);
48492     }
48493 
48494     // Custom error handler for libjpeg.
48495 #ifdef cimg_use_jpeg
48496     struct _cimg_error_mgr {
48497       struct jpeg_error_mgr original;
48498       jmp_buf setjmp_buffer;
48499       char message[JMSG_LENGTH_MAX];
48500     };
48501 
48502     typedef struct _cimg_error_mgr *_cimg_error_ptr;
48503 
48504     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
48505       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
48506       (*cinfo->err->format_message)(cinfo,c_err->message);
48507       jpeg_destroy(cinfo);  // Clean memory and temp files.
48508       longjmp(c_err->setjmp_buffer,1);
48509     }
48510 #endif
48511 
48512     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
48513       if (!file && !filename)
48514         throw CImgArgumentException(_cimg_instance
48515                                     "load_jpeg(): Specified filename is (null).",
48516                                     cimg_instance);
48517 
48518 #ifndef cimg_use_jpeg
48519       if (file)
48520         throw CImgIOException(_cimg_instance
48521                               "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
48522                               cimg_instance);
48523       else return load_other(filename);
48524 #else
48525 
48526       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48527       struct jpeg_decompress_struct cinfo;
48528       struct _cimg_error_mgr jerr;
48529       cinfo.err = jpeg_std_error(&jerr.original);
48530       jerr.original.error_exit = _cimg_jpeg_error_exit;
48531       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
48532         if (!file) cimg::fclose(nfile);
48533         throw CImgIOException(_cimg_instance
48534                              "load_jpeg(): Error message returned by libjpeg: %s.",
48535                              cimg_instance,jerr.message);
48536       }
48537 
48538       jpeg_create_decompress(&cinfo);
48539       jpeg_stdio_src(&cinfo,nfile);
48540       jpeg_read_header(&cinfo,TRUE);
48541       jpeg_start_decompress(&cinfo);
48542 
48543       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
48544         if (!file) {
48545           cimg::fclose(nfile);
48546           return load_other(filename);
48547         } else
48548           throw CImgIOException(_cimg_instance
48549                                 "load_jpeg(): Failed to load JPEG data from file '%s'.",
48550                                 cimg_instance,filename?filename:"(FILE*)");
48551       }
48552       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
48553       JSAMPROW row_pointer[1];
48554       try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
48555       catch (...) { if (!file) cimg::fclose(nfile); throw; }
48556       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
48557         *ptr_a = _data + 3UL*_width*_height;
48558       while (cinfo.output_scanline<cinfo.output_height) {
48559         *row_pointer = buffer._data;
48560         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
48561           cimg::warn(_cimg_instance
48562                      "load_jpeg(): Incomplete data in file '%s'.",
48563                      cimg_instance,filename?filename:"(FILE*)");
48564           break;
48565         }
48566         const unsigned char *ptrs = buffer._data;
48567         switch (_spectrum) {
48568         case 1 : {
48569           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
48570         } break;
48571         case 3 : {
48572           cimg_forX(*this,x) {
48573             *(ptr_r++) = (T)*(ptrs++);
48574             *(ptr_g++) = (T)*(ptrs++);
48575             *(ptr_b++) = (T)*(ptrs++);
48576           }
48577         } break;
48578         case 4 : {
48579           cimg_forX(*this,x) {
48580             *(ptr_r++) = (T)*(ptrs++);
48581             *(ptr_g++) = (T)*(ptrs++);
48582             *(ptr_b++) = (T)*(ptrs++);
48583             *(ptr_a++) = (T)*(ptrs++);
48584           }
48585         } break;
48586         }
48587       }
48588       jpeg_finish_decompress(&cinfo);
48589       jpeg_destroy_decompress(&cinfo);
48590       if (!file) cimg::fclose(nfile);
48591       return *this;
48592 #endif
48593     }
48594 
48595     //! Load image from a file, using Magick++ library.
48596     /**
48597        \param filename Filename, as a C-string.
48598     **/
48599     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
48600     //   This is experimental code, not much tested, use with care.
48601     CImg<T>& load_magick(const char *const filename) {
48602       if (!filename)
48603         throw CImgArgumentException(_cimg_instance
48604                                     "load_magick(): Specified filename is (null).",
48605                                     cimg_instance);
48606 #ifdef cimg_use_magick
48607       Magick::Image image(filename);
48608       const unsigned int W = image.size().width(), H = image.size().height();
48609       switch (image.type()) {
48610       case Magick::PaletteMatteType :
48611       case Magick::TrueColorMatteType :
48612       case Magick::ColorSeparationType : {
48613         assign(W,H,1,4);
48614         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);
48615         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48616         for (ulongT off = (ulongT)W*H; off; --off) {
48617           *(ptr_r++) = (T)(pixels->red);
48618           *(ptr_g++) = (T)(pixels->green);
48619           *(ptr_b++) = (T)(pixels->blue);
48620           *(ptr_a++) = (T)(pixels->opacity);
48621           ++pixels;
48622         }
48623       } break;
48624       case Magick::PaletteType :
48625       case Magick::TrueColorType : {
48626         assign(W,H,1,3);
48627         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
48628         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48629         for (ulongT off = (ulongT)W*H; off; --off) {
48630           *(ptr_r++) = (T)(pixels->red);
48631           *(ptr_g++) = (T)(pixels->green);
48632           *(ptr_b++) = (T)(pixels->blue);
48633           ++pixels;
48634         }
48635       } break;
48636       case Magick::GrayscaleMatteType : {
48637         assign(W,H,1,2);
48638         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
48639         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48640         for (ulongT off = (ulongT)W*H; off; --off) {
48641           *(ptr_r++) = (T)(pixels->red);
48642           *(ptr_a++) = (T)(pixels->opacity);
48643           ++pixels;
48644         }
48645       } break;
48646       default : {
48647         assign(W,H,1,1);
48648         T *ptr_r = data(0,0,0,0);
48649         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48650         for (ulongT off = (ulongT)W*H; off; --off) {
48651           *(ptr_r++) = (T)(pixels->red);
48652           ++pixels;
48653         }
48654       }
48655       }
48656       return *this;
48657 #else
48658       throw CImgIOException(_cimg_instance
48659                             "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
48660                             cimg_instance,
48661                             filename);
48662 #endif
48663     }
48664 
48665     //! Load image from a file, using Magick++ library \newinstance.
48666     static CImg<T> get_load_magick(const char *const filename) {
48667       return CImg<T>().load_magick(filename);
48668     }
48669 
48670     //! Load image from a PNG file.
48671     /**
48672        \param filename Filename, as a C-string.
48673        \param[out] bits_per_pixel Number of bits per pixels used to store pixel values in the image file.
48674     **/
48675     CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
48676       return _load_png(0,filename,bits_per_pixel);
48677     }
48678 
48679     //! Load image from a PNG file \newinstance.
48680     static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
48681       return CImg<T>().load_png(filename,bits_per_pixel);
48682     }
48683 
48684     //! Load image from a PNG file \overloading.
48685     CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
48686       return _load_png(file,0,bits_per_pixel);
48687     }
48688 
48689     //! Load image from a PNG file \newinstance.
48690     static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
48691       return CImg<T>().load_png(file,bits_per_pixel);
48692     }
48693 
48694     // (Note: Most of this function has been written by Eric Fausett)
48695     CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) {
48696       if (!file && !filename)
48697         throw CImgArgumentException(_cimg_instance
48698                                     "load_png(): Specified filename is (null).",
48699                                     cimg_instance);
48700 
48701 #ifndef cimg_use_png
48702       cimg::unused(bits_per_pixel);
48703       if (file)
48704         throw CImgIOException(_cimg_instance
48705                               "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
48706                               cimg_instance);
48707 
48708       else return load_other(filename);
48709 #else
48710       // Open file and check for PNG validity
48711 #if defined __GNUC__
48712       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
48713       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
48714 #else
48715       const char *nfilename = filename;
48716       std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
48717 #endif
48718       unsigned char pngCheck[8] = { 0 };
48719       cimg::fread(pngCheck,8,(std::FILE*)nfile);
48720       if (png_sig_cmp(pngCheck,0,8)) {
48721         if (!file) cimg::fclose(nfile);
48722         throw CImgIOException(_cimg_instance
48723                               "load_png(): Invalid PNG file '%s'.",
48724                               cimg_instance,
48725                               nfilename?nfilename:"(FILE*)");
48726       }
48727 
48728       // Setup PNG structures for read
48729       png_voidp user_error_ptr = 0;
48730       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
48731       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
48732       if (!png_ptr) {
48733         if (!file) cimg::fclose(nfile);
48734         throw CImgIOException(_cimg_instance
48735                               "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
48736                               cimg_instance,
48737                               nfilename?nfilename:"(FILE*)");
48738       }
48739       png_infop info_ptr = png_create_info_struct(png_ptr);
48740       if (!info_ptr) {
48741         if (!file) cimg::fclose(nfile);
48742         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
48743         throw CImgIOException(_cimg_instance
48744                               "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
48745                               cimg_instance,
48746                               nfilename?nfilename:"(FILE*)");
48747       }
48748       png_infop end_info = png_create_info_struct(png_ptr);
48749       if (!end_info) {
48750         if (!file) cimg::fclose(nfile);
48751         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
48752         throw CImgIOException(_cimg_instance
48753                               "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
48754                               cimg_instance,
48755                               nfilename?nfilename:"(FILE*)");
48756       }
48757 
48758       // Error handling callback for png file reading
48759       if (setjmp(png_jmpbuf(png_ptr))) {
48760         if (!file) cimg::fclose((std::FILE*)nfile);
48761         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
48762         throw CImgIOException(_cimg_instance
48763                               "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
48764                               cimg_instance,
48765                               nfilename?nfilename:"(FILE*)");
48766       }
48767       png_init_io(png_ptr, nfile);
48768       png_set_sig_bytes(png_ptr, 8);
48769 
48770       // Get PNG Header Info up to data block
48771       png_read_info(png_ptr,info_ptr);
48772       png_uint_32 W, H;
48773       int bit_depth, color_type, interlace_type;
48774       bool is_gray = false;
48775       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
48776       if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth;
48777 
48778       // Transforms to unify image data
48779       if (color_type==PNG_COLOR_TYPE_PALETTE) {
48780         png_set_palette_to_rgb(png_ptr);
48781         color_type = PNG_COLOR_TYPE_RGB;
48782         bit_depth = 8;
48783       }
48784       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
48785         png_set_expand_gray_1_2_4_to_8(png_ptr);
48786         is_gray = true;
48787         bit_depth = 8;
48788       }
48789       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
48790         png_set_tRNS_to_alpha(png_ptr);
48791         color_type |= PNG_COLOR_MASK_ALPHA;
48792       }
48793       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
48794         png_set_gray_to_rgb(png_ptr);
48795         color_type |= PNG_COLOR_MASK_COLOR;
48796         is_gray = true;
48797       }
48798       if (color_type==PNG_COLOR_TYPE_RGB)
48799         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
48800 
48801       png_read_update_info(png_ptr,info_ptr);
48802       if (bit_depth!=8 && bit_depth!=16) {
48803         if (!file) cimg::fclose(nfile);
48804         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
48805         throw CImgIOException(_cimg_instance
48806                               "load_png(): Invalid bit depth %u in file '%s'.",
48807                               cimg_instance,
48808                               bit_depth,nfilename?nfilename:"(FILE*)");
48809       }
48810       const int byte_depth = bit_depth>>3;
48811 
48812       // Allocate Memory for Image Read
48813       png_bytep *const imgData = new png_bytep[H];
48814       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
48815       png_read_image(png_ptr,imgData);
48816       png_read_end(png_ptr,end_info);
48817 
48818       // Read pixel data
48819       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
48820         if (!file) cimg::fclose(nfile);
48821         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
48822         throw CImgIOException(_cimg_instance
48823                               "load_png(): Invalid color coding type %u in file '%s'.",
48824                               cimg_instance,
48825                               color_type,nfilename?nfilename:"(FILE*)");
48826       }
48827       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
48828       try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
48829       catch (...) { if (!file) cimg::fclose(nfile); throw; }
48830       T
48831         *ptr_r = data(0,0,0,0),
48832         *ptr_g = is_gray?0:data(0,0,0,1),
48833         *ptr_b = is_gray?0:data(0,0,0,2),
48834         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
48835       switch (bit_depth) {
48836       case 8 : {
48837         cimg_forY(*this,y) {
48838           const unsigned char *ptrs = (unsigned char*)imgData[y];
48839           cimg_forX(*this,x) {
48840             *(ptr_r++) = (T)*(ptrs++);
48841             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
48842             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
48843             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
48844           }
48845         }
48846       } break;
48847       case 16 : {
48848         cimg_forY(*this,y) {
48849           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
48850           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
48851           cimg_forX(*this,x) {
48852             *(ptr_r++) = (T)*(ptrs++);
48853             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
48854             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
48855             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
48856           }
48857         }
48858       } break;
48859       }
48860       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
48861 
48862       // Deallocate Image Read Memory
48863       cimg_forY(*this,n) delete[] imgData[n];
48864       delete[] imgData;
48865       if (!file) cimg::fclose(nfile);
48866       return *this;
48867 #endif
48868     }
48869 
48870     //! Load image from a PNM file.
48871     /**
48872       \param filename Filename, as a C-string.
48873     **/
48874     CImg<T>& load_pnm(const char *const filename) {
48875       return _load_pnm(0,filename);
48876     }
48877 
48878     //! Load image from a PNM file \newinstance.
48879     static CImg<T> get_load_pnm(const char *const filename) {
48880       return CImg<T>().load_pnm(filename);
48881     }
48882 
48883     //! Load image from a PNM file \overloading.
48884     CImg<T>& load_pnm(std::FILE *const file) {
48885       return _load_pnm(file,0);
48886     }
48887 
48888     //! Load image from a PNM file \newinstance.
48889     static CImg<T> get_load_pnm(std::FILE *const file) {
48890       return CImg<T>().load_pnm(file);
48891     }
48892 
48893     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
48894       if (!file && !filename)
48895         throw CImgArgumentException(_cimg_instance
48896                                     "load_pnm(): Specified filename is (null).",
48897                                     cimg_instance);
48898 
48899       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48900       unsigned int ppm_type, W, H, D = 1, colormax = 255;
48901       CImg<charT> item(16384,1,1,1,0);
48902       int err, rval, gval, bval;
48903       const longT cimg_iobuffer = (longT)24*1024*1024;
48904       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48905       if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
48906         if (!file) cimg::fclose(nfile);
48907         throw CImgIOException(_cimg_instance
48908                               "load_pnm(): PNM header not found in file '%s'.",
48909                               cimg_instance,
48910                               filename?filename:"(FILE*)");
48911       }
48912       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48913       if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
48914         if (!file) cimg::fclose(nfile);
48915         throw CImgIOException(_cimg_instance
48916                               "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
48917                               cimg_instance,
48918                               filename?filename:"(FILE*)");
48919       }
48920       if (ppm_type!=1 && ppm_type!=4) {
48921         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
48922           while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48923           if (cimg_sscanf(item,"%u",&colormax)!=1)
48924             cimg::warn(_cimg_instance
48925                        "load_pnm(): COLORMAX field is undefined in file '%s'.",
48926                        cimg_instance,
48927                        filename?filename:"(FILE*)");
48928         } else { colormax = D; D = 1; }
48929       }
48930       std::fgetc(nfile);
48931 
48932       switch (ppm_type) {
48933       case 1 : { // 2d b&w ascii.
48934         assign(W,H,1,1);
48935         T* ptrd = _data;
48936         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
48937       } break;
48938       case 2 : { // 2d grey ascii.
48939         assign(W,H,1,1);
48940         T* ptrd = _data;
48941         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
48942       } break;
48943       case 3 : { // 2d color ascii.
48944         assign(W,H,1,3);
48945         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
48946         cimg_forXY(*this,x,y) {
48947           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
48948             *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
48949           } else break;
48950         }
48951       } break;
48952       case 4 : { // 2d b&w binary (support 3D PINK extension).
48953         CImg<ucharT> raw;
48954         assign(W,H,D,1);
48955         T *ptrd = data(0,0,0,0);
48956         unsigned int w = 0, h = 0, d = 0;
48957         for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
48958           raw.assign(std::min(to_read,cimg_iobuffer));
48959           cimg::fread(raw._data,raw._width,nfile);
48960           to_read-=raw._width;
48961           const unsigned char *ptrs = raw._data;
48962           unsigned char mask = 0, val = 0;
48963           for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
48964             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
48965             *(ptrd++) = (T)((val&mask)?0:255);
48966             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
48967           }
48968         }
48969       } break;
48970       case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
48971         if (colormax<256) { // 8 bits.
48972           CImg<ucharT> raw;
48973           assign(W,H,D,1);
48974           T *ptrd = data(0,0,0,0);
48975           for (longT to_read = (longT)size(); to_read>0; ) {
48976             raw.assign(std::min(to_read,cimg_iobuffer));
48977             cimg::fread(raw._data,raw._width,nfile);
48978             to_read-=raw._width;
48979             const unsigned char *ptrs = raw._data;
48980             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
48981           }
48982         } else { // 16 bits.
48983           CImg<ushortT> raw;
48984           assign(W,H,D,1);
48985           T *ptrd = data(0,0,0,0);
48986           for (longT to_read = (longT)size(); to_read>0; ) {
48987             raw.assign(std::min(to_read,cimg_iobuffer/2));
48988             cimg::fread(raw._data,raw._width,nfile);
48989 	    if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
48990             to_read-=raw._width;
48991             const unsigned short *ptrs = raw._data;
48992             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
48993           }
48994         }
48995       } break;
48996       case 6 : { // 2d color binary.
48997         if (colormax<256) { // 8 bits.
48998           CImg<ucharT> raw;
48999           assign(W,H,1,3);
49000           T
49001             *ptr_r = data(0,0,0,0),
49002             *ptr_g = data(0,0,0,1),
49003             *ptr_b = data(0,0,0,2);
49004           for (longT to_read = (longT)size(); to_read>0; ) {
49005             raw.assign(std::min(to_read,cimg_iobuffer));
49006             cimg::fread(raw._data,raw._width,nfile);
49007             to_read-=raw._width;
49008             const unsigned char *ptrs = raw._data;
49009             for (ulongT off = (ulongT)raw._width/3; off; --off) {
49010               *(ptr_r++) = (T)*(ptrs++);
49011               *(ptr_g++) = (T)*(ptrs++);
49012               *(ptr_b++) = (T)*(ptrs++);
49013             }
49014           }
49015         } else { // 16 bits.
49016           CImg<ushortT> raw;
49017           assign(W,H,1,3);
49018           T
49019             *ptr_r = data(0,0,0,0),
49020             *ptr_g = data(0,0,0,1),
49021             *ptr_b = data(0,0,0,2);
49022           for (longT to_read = (longT)size(); to_read>0; ) {
49023             raw.assign(std::min(to_read,cimg_iobuffer/2));
49024             cimg::fread(raw._data,raw._width,nfile);
49025             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
49026             to_read-=raw._width;
49027             const unsigned short *ptrs = raw._data;
49028             for (ulongT off = (ulongT)raw._width/3; off; --off) {
49029               *(ptr_r++) = (T)*(ptrs++);
49030               *(ptr_g++) = (T)*(ptrs++);
49031               *(ptr_b++) = (T)*(ptrs++);
49032             }
49033           }
49034         }
49035       } break;
49036       case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
49037         CImg<intT> raw;
49038         assign(W,H,D,1);
49039         T *ptrd = data(0,0,0,0);
49040         for (longT to_read = (longT)size(); to_read>0; ) {
49041           raw.assign(std::min(to_read,cimg_iobuffer));
49042           cimg::fread(raw._data,raw._width,nfile);
49043           to_read-=raw._width;
49044           const int *ptrs = raw._data;
49045           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49046         }
49047       } break;
49048       case 9 : { // 2d/3d grey binary with float values (PINK extension).
49049         CImg<floatT> raw;
49050         assign(W,H,D,1);
49051         T *ptrd = data(0,0,0,0);
49052         for (longT to_read = (longT)size(); to_read>0; ) {
49053           raw.assign(std::min(to_read,cimg_iobuffer));
49054           cimg::fread(raw._data,raw._width,nfile);
49055           to_read-=raw._width;
49056           const float *ptrs = raw._data;
49057           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49058         }
49059       } break;
49060       default :
49061         assign();
49062         if (!file) cimg::fclose(nfile);
49063         throw CImgIOException(_cimg_instance
49064                               "load_pnm(): PNM type 'P%d' found, but type is not supported.",
49065                               cimg_instance,
49066                               filename?filename:"(FILE*)",ppm_type);
49067       }
49068       if (!file) cimg::fclose(nfile);
49069       return *this;
49070     }
49071 
49072     //! Load image from a PFM file.
49073     /**
49074       \param filename Filename, as a C-string.
49075     **/
49076     CImg<T>& load_pfm(const char *const filename) {
49077       return _load_pfm(0,filename);
49078     }
49079 
49080     //! Load image from a PFM file \newinstance.
49081     static CImg<T> get_load_pfm(const char *const filename) {
49082       return CImg<T>().load_pfm(filename);
49083     }
49084 
49085     //! Load image from a PFM file \overloading.
49086     CImg<T>& load_pfm(std::FILE *const file) {
49087       return _load_pfm(file,0);
49088     }
49089 
49090     //! Load image from a PFM file \newinstance.
49091     static CImg<T> get_load_pfm(std::FILE *const file) {
49092       return CImg<T>().load_pfm(file);
49093     }
49094 
49095     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
49096       if (!file && !filename)
49097         throw CImgArgumentException(_cimg_instance
49098                                     "load_pfm(): Specified filename is (null).",
49099                                     cimg_instance);
49100 
49101       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49102       char pfm_type;
49103       CImg<charT> item(16384,1,1,1,0);
49104       int W = 0, H = 0, err = 0;
49105       double scale = 0;
49106       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49107       if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
49108         if (!file) cimg::fclose(nfile);
49109         throw CImgIOException(_cimg_instance
49110                               "load_pfm(): PFM header not found in file '%s'.",
49111                               cimg_instance,
49112                               filename?filename:"(FILE*)");
49113       }
49114       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49115       if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
49116         if (!file) cimg::fclose(nfile);
49117         throw CImgIOException(_cimg_instance
49118                               "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
49119                               cimg_instance,
49120                               filename?filename:"(FILE*)");
49121       }
49122       if (err==2) {
49123         while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49124         if (cimg_sscanf(item,"%lf",&scale)!=1)
49125           cimg::warn(_cimg_instance
49126                      "load_pfm(): SCALE field is undefined in file '%s'.",
49127                      cimg_instance,
49128                      filename?filename:"(FILE*)");
49129       }
49130       std::fgetc(nfile);
49131       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
49132       if (is_color) {
49133         assign(W,H,1,3,(T)0);
49134         CImg<floatT> buf(3*W);
49135         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
49136         cimg_forY(*this,y) {
49137           cimg::fread(buf._data,3*W,nfile);
49138           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
49139           const float *ptrs = buf._data;
49140           cimg_forX(*this,x) {
49141             *(ptr_r++) = (T)*(ptrs++);
49142             *(ptr_g++) = (T)*(ptrs++);
49143             *(ptr_b++) = (T)*(ptrs++);
49144           }
49145         }
49146       } else {
49147         assign(W,H,1,1,(T)0);
49148         CImg<floatT> buf(W);
49149         T *ptrd = data(0,0,0,0);
49150         cimg_forY(*this,y) {
49151           cimg::fread(buf._data,W,nfile);
49152           if (is_inverted) cimg::invert_endianness(buf._data,W);
49153           const float *ptrs = buf._data;
49154           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
49155         }
49156       }
49157       if (!file) cimg::fclose(nfile);
49158       return mirror('y');  // Most of the .pfm files are flipped along the y-axis.
49159     }
49160 
49161     //! Load image from a RGB file.
49162     /**
49163       \param filename Filename, as a C-string.
49164       \param dimw Width of the image buffer.
49165       \param dimh Height of the image buffer.
49166     **/
49167     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49168       return _load_rgb(0,filename,dimw,dimh);
49169     }
49170 
49171     //! Load image from a RGB file \newinstance.
49172     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49173       return CImg<T>().load_rgb(filename,dimw,dimh);
49174     }
49175 
49176     //! Load image from a RGB file \overloading.
49177     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49178       return _load_rgb(file,0,dimw,dimh);
49179     }
49180 
49181     //! Load image from a RGB file \newinstance.
49182     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49183       return CImg<T>().load_rgb(file,dimw,dimh);
49184     }
49185 
49186     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
49187                        const unsigned int dimw, const unsigned int dimh) {
49188       if (!file && !filename)
49189         throw CImgArgumentException(_cimg_instance
49190                                     "load_rgb(): Specified filename is (null).",
49191                                     cimg_instance);
49192 
49193       if (!dimw || !dimh) return assign();
49194       const longT cimg_iobuffer = (longT)24*1024*1024;
49195       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49196       CImg<ucharT> raw;
49197       assign(dimw,dimh,1,3);
49198       T
49199         *ptr_r = data(0,0,0,0),
49200         *ptr_g = data(0,0,0,1),
49201         *ptr_b = data(0,0,0,2);
49202       for (longT to_read = (longT)size(); to_read>0; ) {
49203         raw.assign(std::min(to_read,cimg_iobuffer));
49204         cimg::fread(raw._data,raw._width,nfile);
49205         to_read-=raw._width;
49206         const unsigned char *ptrs = raw._data;
49207         for (ulongT off = raw._width/3UL; off; --off) {
49208           *(ptr_r++) = (T)*(ptrs++);
49209           *(ptr_g++) = (T)*(ptrs++);
49210           *(ptr_b++) = (T)*(ptrs++);
49211         }
49212       }
49213       if (!file) cimg::fclose(nfile);
49214       return *this;
49215     }
49216 
49217     //! Load image from a RGBA file.
49218     /**
49219        \param filename Filename, as a C-string.
49220        \param dimw Width of the image buffer.
49221        \param dimh Height of the image buffer.
49222     **/
49223     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49224       return _load_rgba(0,filename,dimw,dimh);
49225     }
49226 
49227     //! Load image from a RGBA file \newinstance.
49228     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49229       return CImg<T>().load_rgba(filename,dimw,dimh);
49230     }
49231 
49232     //! Load image from a RGBA file \overloading.
49233     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49234       return _load_rgba(file,0,dimw,dimh);
49235     }
49236 
49237     //! Load image from a RGBA file \newinstance.
49238     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49239       return CImg<T>().load_rgba(file,dimw,dimh);
49240     }
49241 
49242     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
49243                         const unsigned int dimw, const unsigned int dimh) {
49244       if (!file && !filename)
49245         throw CImgArgumentException(_cimg_instance
49246                                     "load_rgba(): Specified filename is (null).",
49247                                     cimg_instance);
49248 
49249       if (!dimw || !dimh) return assign();
49250       const longT cimg_iobuffer = (longT)24*1024*1024;
49251       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49252       CImg<ucharT> raw;
49253       assign(dimw,dimh,1,4);
49254       T
49255         *ptr_r = data(0,0,0,0),
49256         *ptr_g = data(0,0,0,1),
49257         *ptr_b = data(0,0,0,2),
49258         *ptr_a = data(0,0,0,3);
49259       for (longT to_read = (longT)size(); to_read>0; ) {
49260         raw.assign(std::min(to_read,cimg_iobuffer));
49261         cimg::fread(raw._data,raw._width,nfile);
49262         to_read-=raw._width;
49263         const unsigned char *ptrs = raw._data;
49264         for (ulongT off = raw._width/4UL; off; --off) {
49265           *(ptr_r++) = (T)*(ptrs++);
49266           *(ptr_g++) = (T)*(ptrs++);
49267           *(ptr_b++) = (T)*(ptrs++);
49268           *(ptr_a++) = (T)*(ptrs++);
49269         }
49270       }
49271       if (!file) cimg::fclose(nfile);
49272       return *this;
49273     }
49274 
49275     //! Load image from a TIFF file.
49276     /**
49277        \param filename Filename, as a C-string.
49278        \param first_frame First frame to read (for multi-pages tiff).
49279        \param last_frame Last frame to read (for multi-pages tiff).
49280        \param step_frame Step value of frame reading.
49281        \param[out] voxel_size Voxel size, as stored in the filename.
49282        \param[out] description Description, as stored in the filename.
49283        \note
49284        - libtiff support is enabled by defining the precompilation
49285         directive \c cimg_use_tif.
49286        - When libtiff is enabled, 2D and 3D (multipage) several
49287         channel per pixel are supported for
49288         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
49289        - If \c cimg_use_tif is not defined at compile time the
49290         function uses CImg<T>& load_other(const char*).
49291      **/
49292     CImg<T>& load_tiff(const char *const filename,
49293 		       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
49294 		       const unsigned int step_frame=1,
49295                        float *const voxel_size=0,
49296                        CImg<charT> *const description=0) {
49297       if (!filename)
49298         throw CImgArgumentException(_cimg_instance
49299                                     "load_tiff(): Specified filename is (null).",
49300                                     cimg_instance);
49301 
49302       const unsigned int
49303 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
49304 	nstep_frame = step_frame?step_frame:1;
49305       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
49306 
49307 #ifndef cimg_use_tiff
49308       cimg::unused(voxel_size,description);
49309       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
49310         throw CImgArgumentException(_cimg_instance
49311                                     "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
49312                                     cimg_instance,
49313                                     filename);
49314       return load_other(filename);
49315 #else
49316 #if cimg_verbosity<3
49317       TIFFSetWarningHandler(0);
49318       TIFFSetErrorHandler(0);
49319 #endif
49320       TIFF *tif = TIFFOpen(filename,"r");
49321       if (tif) {
49322         unsigned int nb_images = 0;
49323         do ++nb_images; while (TIFFReadDirectory(tif));
49324         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
49325           cimg::warn(_cimg_instance
49326                      "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
49327                      cimg_instance,
49328                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
49329 
49330         if (nfirst_frame>=nb_images) return assign();
49331         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
49332         TIFFSetDirectory(tif,0);
49333         CImg<T> frame;
49334         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
49335           frame._load_tiff(tif,l,voxel_size,description);
49336           if (l==nfirst_frame)
49337             assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
49338           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
49339             resize(std::max(frame._width,_width),
49340                    std::max(frame._height,_height),-100,
49341                    std::max(frame._spectrum,_spectrum),0);
49342           draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
49343         }
49344         TIFFClose(tif);
49345       } else throw CImgIOException(_cimg_instance
49346                                    "load_tiff(): Failed to open file '%s'.",
49347                                    cimg_instance,
49348                                    filename);
49349       return *this;
49350 #endif
49351     }
49352 
49353     //! Load image from a TIFF file \newinstance.
49354     static CImg<T> get_load_tiff(const char *const filename,
49355 				 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
49356 				 const unsigned int step_frame=1,
49357                                  float *const voxel_size=0,
49358                                  CImg<charT> *const description=0) {
49359       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
49360     }
49361 
49362     // (Original contribution by Jerome Boulanger).
49363 #ifdef cimg_use_tiff
49364     template<typename t>
49365     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel,
49366                                  const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
49367       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
49368       if (buf) {
49369         for (unsigned int row = 0; row<ny; row+=th)
49370           for (unsigned int col = 0; col<nx; col+=tw) {
49371             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
49372               _TIFFfree(buf); TIFFClose(tif);
49373               throw CImgIOException(_cimg_instance
49374                                     "load_tiff(): Invalid tile in file '%s'.",
49375                                     cimg_instance,
49376                                     TIFFFileName(tif));
49377             }
49378             const t *ptr = buf;
49379             for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
49380               for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
49381                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49382                   (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
49383           }
49384         _TIFFfree(buf);
49385       }
49386     }
49387 
49388     template<typename t>
49389     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel,
49390                                    const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
49391       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
49392       if (buf) {
49393         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49394           for (unsigned int row = 0; row<ny; row+=th)
49395             for (unsigned int col = 0; col<nx; col+=tw) {
49396               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
49397                 _TIFFfree(buf); TIFFClose(tif);
49398                 throw CImgIOException(_cimg_instance
49399                                       "load_tiff(): Invalid tile in file '%s'.",
49400                                       cimg_instance,
49401                                       TIFFFileName(tif));
49402               }
49403               const t *ptr = buf;
49404               for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
49405                 for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
49406                   (*this)(cc,rr,vv) = (T)*(ptr++);
49407             }
49408         _TIFFfree(buf);
49409       }
49410     }
49411 
49412     template<typename t>
49413     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
49414       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
49415       if (buf) {
49416         uint32 row, rowsperstrip = (uint32)-1;
49417         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
49418         for (row = 0; row<ny; row+= rowsperstrip) {
49419           uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
49420           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
49421           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
49422             _TIFFfree(buf); TIFFClose(tif);
49423             throw CImgIOException(_cimg_instance
49424                                   "load_tiff(): Invalid strip in file '%s'.",
49425                                   cimg_instance,
49426                                   TIFFFileName(tif));
49427           }
49428           const t *ptr = buf;
49429           for (unsigned int rr = 0; rr<nrow; ++rr)
49430             for (unsigned int cc = 0; cc<nx; ++cc)
49431               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
49432         }
49433         _TIFFfree(buf);
49434       }
49435     }
49436 
49437     template<typename t>
49438     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
49439       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
49440       if (buf) {
49441         uint32 row, rowsperstrip = (uint32)-1;
49442         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
49443         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49444           for (row = 0; row<ny; row+= rowsperstrip) {
49445             uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
49446             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
49447             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
49448               _TIFFfree(buf); TIFFClose(tif);
49449               throw CImgIOException(_cimg_instance
49450                                     "load_tiff(): Invalid strip in file '%s'.",
49451                                     cimg_instance,
49452                                     TIFFFileName(tif));
49453             }
49454             const t *ptr = buf;
49455             for (unsigned int rr = 0;rr<nrow; ++rr)
49456               for (unsigned int cc = 0; cc<nx; ++cc)
49457                 (*this)(cc,row + rr,vv) = (T)*(ptr++);
49458           }
49459         _TIFFfree(buf);
49460       }
49461     }
49462 
49463     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory,
49464                         float *const voxel_size, CImg<charT> *const description) {
49465       if (!TIFFSetDirectory(tif,directory)) return assign();
49466       uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
49467       uint16 sampleformat = 1;
49468       uint32 nx = 1, ny = 1;
49469       const char *const filename = TIFFFileName(tif);
49470       const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
49471       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
49472       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
49473       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
49474       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
49475       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
49476       if (voxel_size) {
49477         const char *s_description = 0;
49478         float vx = 0, vy = 0, vz = 0;
49479         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
49480           const char *s_desc = std::strstr(s_description,"VX=");
49481           if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format.
49482             voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
49483           }
49484           s_desc = std::strstr(s_description,"spacing=");
49485           if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format.
49486             voxel_size[2] = vz;
49487           }
49488         }
49489         TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
49490         TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
49491         voxel_size[0] = 1.0f/voxel_size[0];
49492         voxel_size[1] = 1.0f/voxel_size[1];
49493       }
49494       if (description) {
49495         const char *s_description = 0;
49496         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
49497           CImg<charT>::string(s_description).move_to(*description);
49498       }
49499       const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
49500       assign(nx,ny,1,spectrum);
49501 
49502       if ((photo>=3 && sampleformat==1 &&
49503            (bitspersample==4 || bitspersample==8) &&
49504            (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
49505           (bitspersample==1 && samplesperpixel==1)) {
49506         // Special case for unsigned color images.
49507         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
49508         if (!raster) {
49509           _TIFFfree(raster); TIFFClose(tif);
49510           throw CImgException(_cimg_instance
49511                               "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
49512                               cimg_instance,
49513                               cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
49514         }
49515         TIFFReadRGBAImage(tif,nx,ny,raster,0);
49516         switch (spectrum) {
49517         case 1 :
49518           cimg_forXY(*this,x,y)
49519             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
49520           break;
49521         case 3 :
49522           cimg_forXY(*this,x,y) {
49523             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
49524             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]);
49525             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]);
49526           }
49527           break;
49528         case 4 :
49529           cimg_forXY(*this,x,y) {
49530             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
49531             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
49532             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
49533             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
49534           }
49535           break;
49536         }
49537         _TIFFfree(raster);
49538       } else { // Other cases.
49539         uint16 config;
49540         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
49541         if (TIFFIsTiled(tif)) {
49542           uint32 tw = 1, th = 1;
49543           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
49544           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
49545           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
49546             case 8 :
49547               if (sampleformat==SAMPLEFORMAT_UINT)
49548                 _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
49549               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
49550               break;
49551             case 16 :
49552               if (sampleformat==SAMPLEFORMAT_UINT)
49553                 _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
49554               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
49555               break;
49556             case 32 :
49557               if (sampleformat==SAMPLEFORMAT_UINT)
49558                 _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
49559               else if (sampleformat==SAMPLEFORMAT_INT)
49560                 _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
49561               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
49562               break;
49563             case 64 :
49564               if (sampleformat==SAMPLEFORMAT_UINT)
49565                 _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
49566               else if (sampleformat==SAMPLEFORMAT_INT)
49567                 _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
49568               else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
49569               break;
49570             } else switch (bitspersample) {
49571             case 8 :
49572               if (sampleformat==SAMPLEFORMAT_UINT)
49573                 _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
49574               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
49575               break;
49576             case 16 :
49577               if (sampleformat==SAMPLEFORMAT_UINT)
49578                 _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
49579               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
49580               break;
49581             case 32 :
49582               if (sampleformat==SAMPLEFORMAT_UINT)
49583                 _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
49584               else if (sampleformat==SAMPLEFORMAT_INT)
49585                 _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
49586               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
49587               break;
49588             case 64 :
49589               if (sampleformat==SAMPLEFORMAT_UINT)
49590                 _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
49591               else if (sampleformat==SAMPLEFORMAT_INT)
49592                 _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
49593               else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
49594               break;
49595             }
49596         } else {
49597           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
49598             case 8 :
49599               if (sampleformat==SAMPLEFORMAT_UINT)
49600                 _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
49601               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
49602               break;
49603             case 16 :
49604               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
49605               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
49606               break;
49607             case 32 :
49608               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
49609               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
49610               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
49611               break;
49612             case 64 :
49613               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
49614               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
49615               else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
49616               break;
49617             } else switch (bitspersample) {
49618             case 8 :
49619               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
49620               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
49621               break;
49622             case 16 :
49623               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
49624               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
49625               break;
49626             case 32 :
49627               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
49628               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
49629               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
49630               break;
49631             case 64 :
49632               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
49633               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
49634               else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
49635               break;
49636             }
49637         }
49638       }
49639       return *this;
49640     }
49641 #endif
49642 
49643     //! Load image from a MINC2 file.
49644     /**
49645         \param filename Filename, as a C-string.
49646     **/
49647     // (Original code by Haz-Edine Assemlal).
49648     CImg<T>& load_minc2(const char *const filename) {
49649       if (!filename)
49650         throw CImgArgumentException(_cimg_instance
49651                                     "load_minc2(): Specified filename is (null).",
49652                                     cimg_instance);
49653 #ifndef cimg_use_minc2
49654       return load_other(filename);
49655 #else
49656       minc::minc_1_reader rdr;
49657       rdr.open(filename);
49658       assign(rdr.ndim(1)?rdr.ndim(1):1,
49659              rdr.ndim(2)?rdr.ndim(2):1,
49660              rdr.ndim(3)?rdr.ndim(3):1,
49661              rdr.ndim(4)?rdr.ndim(4):1);
49662       if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
49663         rdr.setup_read_byte();
49664       else if (cimg::type<T>::string()==cimg::type<int>::string())
49665         rdr.setup_read_int();
49666       else if (cimg::type<T>::string()==cimg::type<double>::string())
49667         rdr.setup_read_double();
49668       else
49669         rdr.setup_read_float();
49670       minc::load_standard_volume(rdr,this->_data);
49671       return *this;
49672 #endif
49673     }
49674 
49675     //! Load image from a MINC2 file \newinstance.
49676     static CImg<T> get_load_minc2(const char *const filename) {
49677       return CImg<T>().load_analyze(filename);
49678     }
49679 
49680     //! Load image from an ANALYZE7.5/NIFTI file.
49681     /**
49682        \param filename Filename, as a C-string.
49683        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
49684     **/
49685     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
49686       return _load_analyze(0,filename,voxel_size);
49687     }
49688 
49689     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
49690     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
49691       return CImg<T>().load_analyze(filename,voxel_size);
49692     }
49693 
49694     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
49695     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
49696       return _load_analyze(file,0,voxel_size);
49697     }
49698 
49699     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
49700     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
49701       return CImg<T>().load_analyze(file,voxel_size);
49702     }
49703 
49704     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
49705       if (!file && !filename)
49706         throw CImgArgumentException(_cimg_instance
49707                                     "load_analyze(): Specified filename is (null).",
49708                                     cimg_instance);
49709 
49710       std::FILE *nfile_header = 0, *nfile = 0;
49711       if (!file) {
49712         CImg<charT> body(1024);
49713         const char *const ext = cimg::split_filename(filename,body);
49714         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
49715           nfile_header = cimg::fopen(filename,"rb");
49716           cimg_sprintf(body._data + std::strlen(body),".img");
49717           nfile = cimg::fopen(body,"rb");
49718         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
49719           nfile = cimg::fopen(filename,"rb");
49720           cimg_sprintf(body._data + std::strlen(body),".hdr");
49721           nfile_header = cimg::fopen(body,"rb");
49722         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
49723       } else nfile_header = nfile = file; // File is a Niftii file.
49724       if (!nfile || !nfile_header)
49725         throw CImgIOException(_cimg_instance
49726                               "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
49727                               cimg_instance,
49728                               filename?filename:"(FILE*)");
49729 
49730       // Read header.
49731       bool endian = false;
49732       unsigned int header_size;
49733       cimg::fread(&header_size,1,nfile_header);
49734       if (!header_size)
49735         throw CImgIOException(_cimg_instance
49736                               "load_analyze(): Invalid zero-size header in file '%s'.",
49737                               cimg_instance,
49738                               filename?filename:"(FILE*)");
49739       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
49740 
49741       unsigned char *const header = new unsigned char[header_size];
49742       cimg::fread(header + 4,header_size - 4,nfile_header);
49743       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
49744       if (endian) {
49745         cimg::invert_endianness((short*)(header + 40),5);
49746         cimg::invert_endianness((short*)(header + 70),1);
49747         cimg::invert_endianness((short*)(header + 72),1);
49748         cimg::invert_endianness((float*)(header + 76),4);
49749         cimg::invert_endianness((float*)(header + 108),1);
49750         cimg::invert_endianness((float*)(header + 112),1);
49751       }
49752 
49753       if (nfile_header==nfile) {
49754         const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
49755         std::fseek(nfile,vox_offset,SEEK_SET);
49756       }
49757 
49758       unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
49759       if (!dim[0])
49760         cimg::warn(_cimg_instance
49761                    "load_analyze(): File '%s' defines an image with zero dimensions.",
49762                    cimg_instance,
49763                    filename?filename:"(FILE*)");
49764 
49765       if (dim[0]>4)
49766         cimg::warn(_cimg_instance
49767                    "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
49768                    cimg_instance,
49769                    filename?filename:"(FILE*)",dim[0]);
49770 
49771       if (dim[0]>=1) dimx = dim[1];
49772       if (dim[0]>=2) dimy = dim[2];
49773       if (dim[0]>=3) dimz = dim[3];
49774       if (dim[0]>=4) dimv = dim[4];
49775       float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
49776       const unsigned short datatype = *(unsigned short*)(header + 70);
49777       if (voxel_size) {
49778         const float *vsize = (float*)(header + 76);
49779         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
49780       }
49781       delete[] header;
49782 
49783       // Read pixel data.
49784       assign(dimx,dimy,dimz,dimv);
49785       const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
49786       switch (datatype) {
49787       case 2 : {
49788         unsigned char *const buffer = new unsigned char[pdim];
49789         cimg::fread(buffer,pdim,nfile);
49790         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49791         delete[] buffer;
49792       } break;
49793       case 4 : {
49794         short *const buffer = new short[pdim];
49795         cimg::fread(buffer,pdim,nfile);
49796         if (endian) cimg::invert_endianness(buffer,pdim);
49797         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49798         delete[] buffer;
49799       } break;
49800       case 8 : {
49801         int *const buffer = new int[pdim];
49802         cimg::fread(buffer,pdim,nfile);
49803         if (endian) cimg::invert_endianness(buffer,pdim);
49804         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49805         delete[] buffer;
49806       } break;
49807       case 16 : {
49808         float *const buffer = new float[pdim];
49809         cimg::fread(buffer,pdim,nfile);
49810         if (endian) cimg::invert_endianness(buffer,pdim);
49811         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49812         delete[] buffer;
49813       } break;
49814       case 64 : {
49815         double *const buffer = new double[pdim];
49816         cimg::fread(buffer,pdim,nfile);
49817         if (endian) cimg::invert_endianness(buffer,pdim);
49818         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49819         delete[] buffer;
49820       } break;
49821       default :
49822         if (!file) cimg::fclose(nfile);
49823         throw CImgIOException(_cimg_instance
49824                               "load_analyze(): Unable to load datatype %d in file '%s'",
49825                               cimg_instance,
49826                               datatype,filename?filename:"(FILE*)");
49827       }
49828       if (!file) cimg::fclose(nfile);
49829       return *this;
49830     }
49831 
49832     //! Load image from a .cimg[z] file.
49833     /**
49834       \param filename Filename, as a C-string.
49835       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
49836       \param align Appending alignment.
49837     **/
49838     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
49839       CImgList<T> list;
49840       list.load_cimg(filename);
49841       if (list._width==1) return list[0].move_to(*this);
49842       return assign(list.get_append(axis,align));
49843     }
49844 
49845     //! Load image from a .cimg[z] file \newinstance
49846     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
49847       return CImg<T>().load_cimg(filename,axis,align);
49848     }
49849 
49850     //! Load image from a .cimg[z] file \overloading.
49851     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
49852       CImgList<T> list;
49853       list.load_cimg(file);
49854       if (list._width==1) return list[0].move_to(*this);
49855       return assign(list.get_append(axis,align));
49856     }
49857 
49858     //! Load image from a .cimg[z] file \newinstance
49859     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
49860       return CImg<T>().load_cimg(file,axis,align);
49861     }
49862 
49863     //! Load sub-images of a .cimg file.
49864     /**
49865       \param filename Filename, as a C-string.
49866       \param n0 Starting frame.
49867       \param n1 Ending frame (~0U for max).
49868       \param x0 X-coordinate of the starting sub-image vertex.
49869       \param y0 Y-coordinate of the starting sub-image vertex.
49870       \param z0 Z-coordinate of the starting sub-image vertex.
49871       \param c0 C-coordinate of the starting sub-image vertex.
49872       \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
49873       \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
49874       \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
49875       \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
49876       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
49877       \param align Appending alignment.
49878     **/
49879     CImg<T>& load_cimg(const char *const filename,
49880                        const unsigned int n0, const unsigned int n1,
49881                        const unsigned int x0, const unsigned int y0,
49882                        const unsigned int z0, const unsigned int c0,
49883                        const unsigned int x1, const unsigned int y1,
49884                        const unsigned int z1, const unsigned int c1,
49885                        const char axis='z', const float align=0) {
49886       CImgList<T> list;
49887       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
49888       if (list._width==1) return list[0].move_to(*this);
49889       return assign(list.get_append(axis,align));
49890     }
49891 
49892     //! Load sub-images of a .cimg file \newinstance.
49893     static CImg<T> get_load_cimg(const char *const filename,
49894                                  const unsigned int n0, const unsigned int n1,
49895                                  const unsigned int x0, const unsigned int y0,
49896                                  const unsigned int z0, const unsigned int c0,
49897                                  const unsigned int x1, const unsigned int y1,
49898                                  const unsigned int z1, const unsigned int c1,
49899                                  const char axis='z', const float align=0) {
49900       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
49901     }
49902 
49903     //! Load sub-images of a .cimg file \overloading.
49904     CImg<T>& load_cimg(std::FILE *const file,
49905                        const unsigned int n0, const unsigned int n1,
49906                        const unsigned int x0, const unsigned int y0,
49907                        const unsigned int z0, const unsigned int c0,
49908                        const unsigned int x1, const unsigned int y1,
49909                        const unsigned int z1, const unsigned int c1,
49910                        const char axis='z', const float align=0) {
49911       CImgList<T> list;
49912       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
49913       if (list._width==1) return list[0].move_to(*this);
49914       return assign(list.get_append(axis,align));
49915     }
49916 
49917     //! Load sub-images of a .cimg file \newinstance.
49918     static CImg<T> get_load_cimg(std::FILE *const file,
49919                                  const unsigned int n0, const unsigned int n1,
49920                                  const unsigned int x0, const unsigned int y0,
49921                                  const unsigned int z0, const unsigned int c0,
49922                                  const unsigned int x1, const unsigned int y1,
49923                                  const unsigned int z1, const unsigned int c1,
49924                                  const char axis='z', const float align=0) {
49925       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
49926     }
49927 
49928     //! Load image from an INRIMAGE-4 file.
49929     /**
49930        \param filename Filename, as a C-string.
49931        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
49932     **/
49933     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
49934       return _load_inr(0,filename,voxel_size);
49935     }
49936 
49937     //! Load image from an INRIMAGE-4 file \newinstance.
49938     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
49939       return CImg<T>().load_inr(filename,voxel_size);
49940     }
49941 
49942     //! Load image from an INRIMAGE-4 file \overloading.
49943     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
49944       return _load_inr(file,0,voxel_size);
49945     }
49946 
49947     //! Load image from an INRIMAGE-4 file \newinstance.
49948     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
49949       return CImg<T>().load_inr(file,voxel_size);
49950     }
49951 
49952     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
49953       CImg<charT> item(1024), tmp1(64), tmp2(64);
49954       *item = *tmp1 = *tmp2 = 0;
49955       out[0] = std::fscanf(file,"%63s",item._data);
49956       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
49957       if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
49958         throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
49959                               pixel_type());
49960 
49961       while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
49962         cimg_sscanf(item," XDIM%*[^0-9]%d",out);
49963         cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
49964         cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
49965         cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
49966         cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
49967         if (voxel_size) {
49968           cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
49969           cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
49970           cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
49971         }
49972         if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
49973         switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
49974         case 0 : break;
49975         case 2 :
49976           out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
49977           std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
49978         case 1 :
49979           if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
49980           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
49981           if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
49982           if (out[4]>=0) break; // fallthrough
49983         default :
49984           throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
49985                                 pixel_type(),
49986                                 tmp2._data);
49987         }
49988       }
49989       if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
49990         throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
49991                               pixel_type(),
49992                               out[0],out[1],out[2],out[3]);
49993       if (out[4]<0 || out[5]<0)
49994         throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
49995                               pixel_type());
49996       if (out[6]<0)
49997         throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
49998                               pixel_type());
49999       if (out[7]<0)
50000         throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
50001                               pixel_type());
50002     }
50003 
50004     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
50005 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
50006      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
50007         Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
50008         cimg_forYZ(*this,y,z) { \
50009             cimg::fread(val,fopt[0]*fopt[3],nfile); \
50010             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
50011             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
50012           } \
50013         delete[] val; \
50014         loaded = true; \
50015       }
50016 
50017       if (!file && !filename)
50018         throw CImgArgumentException(_cimg_instance
50019                                     "load_inr(): Specified filename is (null).",
50020                                     cimg_instance);
50021 
50022       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50023       int fopt[8], endian = cimg::endianness()?1:0;
50024       bool loaded = false;
50025       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
50026       _load_inr_header(nfile,fopt,voxel_size);
50027       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
50028       _cimg_load_inr_case(0,0,8,unsigned char);
50029       _cimg_load_inr_case(0,1,8,char);
50030       _cimg_load_inr_case(0,0,16,unsigned short);
50031       _cimg_load_inr_case(0,1,16,short);
50032       _cimg_load_inr_case(0,0,32,unsigned int);
50033       _cimg_load_inr_case(0,1,32,int);
50034       _cimg_load_inr_case(1,0,32,float);
50035       _cimg_load_inr_case(1,1,32,float);
50036       _cimg_load_inr_case(1,0,64,double);
50037       _cimg_load_inr_case(1,1,64,double);
50038       if (!loaded) {
50039         if (!file) cimg::fclose(nfile);
50040         throw CImgIOException(_cimg_instance
50041                               "load_inr(): Unknown pixel type defined in file '%s'.",
50042                               cimg_instance,
50043                               filename?filename:"(FILE*)");
50044       }
50045       if (!file) cimg::fclose(nfile);
50046       return *this;
50047     }
50048 
50049     //! Load image from a EXR file.
50050     /**
50051       \param filename Filename, as a C-string.
50052     **/
50053     CImg<T>& load_exr(const char *const filename) {
50054       if (!filename)
50055         throw CImgArgumentException(_cimg_instance
50056                                     "load_exr(): Specified filename is (null).",
50057                                     cimg_instance);
50058 #if defined(cimg_use_openexr)
50059       Imf::RgbaInputFile file(filename);
50060       Imath::Box2i dw = file.dataWindow();
50061       const int
50062         inwidth = dw.max.x - dw.min.x + 1,
50063         inheight = dw.max.y - dw.min.y + 1;
50064       Imf::Array2D<Imf::Rgba> pixels;
50065       pixels.resizeErase(inheight,inwidth);
50066       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
50067       file.readPixels(dw.min.y, dw.max.y);
50068       assign(inwidth,inheight,1,4);
50069       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);
50070       cimg_forXY(*this,x,y) {
50071         *(ptr_r++) = (T)pixels[y][x].r;
50072         *(ptr_g++) = (T)pixels[y][x].g;
50073         *(ptr_b++) = (T)pixels[y][x].b;
50074         *(ptr_a++) = (T)pixels[y][x].a;
50075       }
50076 #elif defined(cimg_use_tinyexr)
50077       float *res;
50078       const char *err = 0;
50079       int width = 0, height = 0;
50080       const int ret = LoadEXR(&res,&width,&height,filename,&err);
50081       if (ret) throw CImgIOException(_cimg_instance
50082                                      "load_exr(): Unable to load EXR file '%s'.",
50083                                      cimg_instance,filename);
50084       CImg<floatT>(out,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
50085       std::free(res);
50086 #else
50087       return load_other(filename);
50088 #endif
50089       return *this;
50090     }
50091 
50092     //! Load image from a EXR file \newinstance.
50093     static CImg<T> get_load_exr(const char *const filename) {
50094       return CImg<T>().load_exr(filename);
50095     }
50096 
50097     //! Load image from a PANDORE-5 file.
50098     /**
50099       \param filename Filename, as a C-string.
50100     **/
50101     CImg<T>& load_pandore(const char *const filename) {
50102       return _load_pandore(0,filename);
50103     }
50104 
50105     //! Load image from a PANDORE-5 file \newinstance.
50106     static CImg<T> get_load_pandore(const char *const filename) {
50107       return CImg<T>().load_pandore(filename);
50108     }
50109 
50110     //! Load image from a PANDORE-5 file \overloading.
50111     CImg<T>& load_pandore(std::FILE *const file) {
50112       return _load_pandore(file,0);
50113     }
50114 
50115     //! Load image from a PANDORE-5 file \newinstance.
50116     static CImg<T> get_load_pandore(std::FILE *const file) {
50117       return CImg<T>().load_pandore(file);
50118     }
50119 
50120     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
50121 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
50122         cimg::fread(dims,nbdim,nfile); \
50123         if (endian) cimg::invert_endianness(dims,nbdim); \
50124         assign(nwidth,nheight,ndepth,ndim); \
50125         const size_t siz = size(); \
50126         stype *buffer = new stype[siz]; \
50127         cimg::fread(buffer,siz,nfile); \
50128         if (endian) cimg::invert_endianness(buffer,siz); \
50129         T *ptrd = _data; \
50130         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
50131         buffer-=siz; \
50132         delete[] buffer
50133 
50134 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
50135         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
50136         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
50137         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
50138         else throw CImgIOException(_cimg_instance \
50139                                    "load_pandore(): Unknown pixel datatype in file '%s'.", \
50140                                    cimg_instance, \
50141                                    filename?filename:"(FILE*)"); }
50142       if (!file && !filename)
50143         throw CImgArgumentException(_cimg_instance
50144                                     "load_pandore(): Specified filename is (null).",
50145                                     cimg_instance);
50146 
50147       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50148       CImg<charT> header(32);
50149       cimg::fread(header._data,12,nfile);
50150       if (cimg::strncasecmp("PANDORE",header,7)) {
50151         if (!file) cimg::fclose(nfile);
50152         throw CImgIOException(_cimg_instance
50153                               "load_pandore(): PANDORE header not found in file '%s'.",
50154                               cimg_instance,
50155                               filename?filename:"(FILE*)");
50156       }
50157       unsigned int imageid, dims[8] = { 0 };
50158       int ptbuf[4] = { 0 };
50159       cimg::fread(&imageid,1,nfile);
50160       const bool endian = imageid>255;
50161       if (endian) cimg::invert_endianness(imageid);
50162       cimg::fread(header._data,20,nfile);
50163 
50164       switch (imageid) {
50165       case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
50166       case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
50167       case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
50168       case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
50169       case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
50170       case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
50171       case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
50172       case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
50173       case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
50174       case 11 : { // Region 1d
50175         cimg::fread(dims,3,nfile);
50176         if (endian) cimg::invert_endianness(dims,3);
50177         assign(dims[1],1,1,1);
50178         const unsigned siz = size();
50179         if (dims[2]<256) {
50180           unsigned char *buffer = new unsigned char[siz];
50181           cimg::fread(buffer,siz,nfile);
50182           T *ptrd = _data;
50183           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50184           buffer-=siz;
50185           delete[] buffer;
50186         } else {
50187           if (dims[2]<65536) {
50188             unsigned short *buffer = new unsigned short[siz];
50189             cimg::fread(buffer,siz,nfile);
50190             if (endian) cimg::invert_endianness(buffer,siz);
50191             T *ptrd = _data;
50192             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50193             buffer-=siz;
50194             delete[] buffer;
50195           } else {
50196             unsigned int *buffer = new unsigned int[siz];
50197             cimg::fread(buffer,siz,nfile);
50198             if (endian) cimg::invert_endianness(buffer,siz);
50199             T *ptrd = _data;
50200             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50201             buffer-=siz;
50202             delete[] buffer;
50203           }
50204         }
50205       }
50206         break;
50207       case 12 : { // Region 2d
50208         cimg::fread(dims,4,nfile);
50209         if (endian) cimg::invert_endianness(dims,4);
50210         assign(dims[2],dims[1],1,1);
50211         const size_t siz = size();
50212         if (dims[3]<256) {
50213           unsigned char *buffer = new unsigned char[siz];
50214           cimg::fread(buffer,siz,nfile);
50215           T *ptrd = _data;
50216           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50217           buffer-=siz;
50218           delete[] buffer;
50219         } else {
50220           if (dims[3]<65536) {
50221             unsigned short *buffer = new unsigned short[siz];
50222             cimg::fread(buffer,siz,nfile);
50223             if (endian) cimg::invert_endianness(buffer,siz);
50224             T *ptrd = _data;
50225             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50226             buffer-=siz;
50227             delete[] buffer;
50228           } else {
50229             unsigned int *buffer = new unsigned int[siz];
50230             cimg::fread(buffer,siz,nfile);
50231             if (endian) cimg::invert_endianness(buffer,siz);
50232             T *ptrd = _data;
50233             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50234             buffer-=siz;
50235             delete[] buffer;
50236           }
50237         }
50238       }
50239         break;
50240       case 13 : { // Region 3d
50241         cimg::fread(dims,5,nfile);
50242         if (endian) cimg::invert_endianness(dims,5);
50243         assign(dims[3],dims[2],dims[1],1);
50244         const size_t siz = size();
50245         if (dims[4]<256) {
50246           unsigned char *buffer = new unsigned char[siz];
50247           cimg::fread(buffer,siz,nfile);
50248           T *ptrd = _data;
50249           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50250           buffer-=siz;
50251           delete[] buffer;
50252         } else {
50253           if (dims[4]<65536) {
50254             unsigned short *buffer = new unsigned short[siz];
50255             cimg::fread(buffer,siz,nfile);
50256             if (endian) cimg::invert_endianness(buffer,siz);
50257             T *ptrd = _data;
50258             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50259             buffer-=siz;
50260             delete[] buffer;
50261           } else {
50262             unsigned int *buffer = new unsigned int[siz];
50263             cimg::fread(buffer,siz,nfile);
50264             if (endian) cimg::invert_endianness(buffer,siz);
50265             T *ptrd = _data;
50266             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50267             buffer-=siz;
50268             delete[] buffer;
50269           }
50270         }
50271       }
50272         break;
50273       case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
50274       case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
50275       case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
50276       case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
50277       case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
50278       case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
50279       case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
50280       case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
50281       case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
50282       case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
50283       case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
50284       case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
50285       case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
50286       case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
50287       case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
50288         break;
50289       case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
50290       case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
50291         break;
50292       case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
50293       case 34 : { // Points 1d
50294         cimg::fread(ptbuf,1,nfile);
50295         if (endian) cimg::invert_endianness(ptbuf,1);
50296         assign(1); (*this)(0) = (T)ptbuf[0];
50297       } break;
50298       case 35 : { // Points 2d
50299         cimg::fread(ptbuf,2,nfile);
50300         if (endian) cimg::invert_endianness(ptbuf,2);
50301         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
50302       } break;
50303       case 36 : { // Points 3d
50304         cimg::fread(ptbuf,3,nfile);
50305         if (endian) cimg::invert_endianness(ptbuf,3);
50306         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
50307       } break;
50308       default :
50309         if (!file) cimg::fclose(nfile);
50310         throw CImgIOException(_cimg_instance
50311                               "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
50312                               cimg_instance,
50313                               imageid,filename?filename:"(FILE*)");
50314       }
50315       if (!file) cimg::fclose(nfile);
50316       return *this;
50317     }
50318 
50319     //! Load image from a PAR-REC (Philips) file.
50320     /**
50321       \param filename Filename, as a C-string.
50322       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50323       \param align Appending alignment.
50324     **/
50325     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
50326       CImgList<T> list;
50327       list.load_parrec(filename);
50328       if (list._width==1) return list[0].move_to(*this);
50329       return assign(list.get_append(axis,align));
50330     }
50331 
50332     //! Load image from a PAR-REC (Philips) file \newinstance.
50333     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
50334       return CImg<T>().load_parrec(filename,axis,align);
50335     }
50336 
50337     //! Load image from a raw binary file.
50338     /**
50339       \param filename Filename, as a C-string.
50340       \param size_x Width of the image buffer.
50341       \param size_y Height of the image buffer.
50342       \param size_z Depth of the image buffer.
50343       \param size_c Spectrum of the image buffer.
50344       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
50345       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
50346       \param offset Starting offset of the read in the specified file.
50347     **/
50348     CImg<T>& load_raw(const char *const filename,
50349                       const unsigned int size_x=0, const unsigned int size_y=1,
50350                       const unsigned int size_z=1, const unsigned int size_c=1,
50351                       const bool is_multiplexed=false, const bool invert_endianness=false,
50352                       const ulongT offset=0) {
50353       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50354     }
50355 
50356     //! Load image from a raw binary file \newinstance.
50357     static CImg<T> get_load_raw(const char *const filename,
50358                                 const unsigned int size_x=0, const unsigned int size_y=1,
50359                                 const unsigned int size_z=1, const unsigned int size_c=1,
50360                                 const bool is_multiplexed=false, const bool invert_endianness=false,
50361                                 const ulongT offset=0) {
50362       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50363     }
50364 
50365     //! Load image from a raw binary file \overloading.
50366     CImg<T>& load_raw(std::FILE *const file,
50367                       const unsigned int size_x=0, const unsigned int size_y=1,
50368                       const unsigned int size_z=1, const unsigned int size_c=1,
50369                       const bool is_multiplexed=false, const bool invert_endianness=false,
50370                       const ulongT offset=0) {
50371       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50372     }
50373 
50374     //! Load image from a raw binary file \newinstance.
50375     static CImg<T> get_load_raw(std::FILE *const file,
50376                                 const unsigned int size_x=0, const unsigned int size_y=1,
50377                                 const unsigned int size_z=1, const unsigned int size_c=1,
50378                                 const bool is_multiplexed=false, const bool invert_endianness=false,
50379                                 const ulongT offset=0) {
50380       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50381     }
50382 
50383     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
50384 		       const unsigned int size_x, const unsigned int size_y,
50385 		       const unsigned int size_z, const unsigned int size_c,
50386 		       const bool is_multiplexed, const bool invert_endianness,
50387                        const ulongT offset) {
50388       if (!file && !filename)
50389         throw CImgArgumentException(_cimg_instance
50390                                     "load_raw(): Specified filename is (null).",
50391                                     cimg_instance);
50392       if (cimg::is_directory(filename))
50393         throw CImgArgumentException(_cimg_instance
50394                                     "load_raw(): Specified filename '%s' is a directory.",
50395                                     cimg_instance,filename);
50396 
50397       ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
50398       unsigned int
50399         _size_x = size_x,
50400         _size_y = size_y,
50401         _size_z = size_z,
50402         _size_c = size_c;
50403       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50404       if (!siz) {  // Retrieve file size.
50405         const longT fpos = cimg::ftell(nfile);
50406         if (fpos<0) throw CImgArgumentException(_cimg_instance
50407                                                 "load_raw(): Cannot determine size of input file '%s'.",
50408                                                 cimg_instance,filename?filename:"(FILE*)");
50409         cimg::fseek(nfile,0,SEEK_END);
50410         siz = cimg::ftell(nfile)/sizeof(T);
50411 		_size_y = (unsigned int)siz;
50412         _size_x = _size_z = _size_c = 1;
50413         cimg::fseek(nfile,fpos,SEEK_SET);
50414       }
50415       cimg::fseek(nfile,offset,SEEK_SET);
50416       assign(_size_x,_size_y,_size_z,_size_c,0);
50417       if (siz && (!is_multiplexed || size_c==1)) {
50418         cimg::fread(_data,siz,nfile);
50419         if (invert_endianness) cimg::invert_endianness(_data,siz);
50420       } else if (siz) {
50421         CImg<T> buf(1,1,1,_size_c);
50422         cimg_forXYZ(*this,x,y,z) {
50423           cimg::fread(buf._data,_size_c,nfile);
50424           if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
50425           set_vector_at(buf,x,y,z);
50426         }
50427       }
50428       if (!file) cimg::fclose(nfile);
50429       return *this;
50430     }
50431 
50432     //! Load image sequence from a YUV file.
50433     /**
50434       \param filename Filename, as a C-string.
50435       \param size_x Width of the frames.
50436       \param size_y Height of the frames.
50437       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
50438       \param first_frame Index of the first frame to read.
50439       \param last_frame Index of the last frame to read.
50440       \param step_frame Step value for frame reading.
50441       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
50442       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50443     **/
50444     CImg<T>& load_yuv(const char *const filename,
50445                       const unsigned int size_x, const unsigned int size_y=1,
50446                       const unsigned int chroma_subsampling=444,
50447                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50448                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50449       return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
50450                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
50451     }
50452 
50453     //! Load image sequence from a YUV file \newinstance.
50454     static CImg<T> get_load_yuv(const char *const filename,
50455                                 const unsigned int size_x, const unsigned int size_y=1,
50456                                 const unsigned int chroma_subsampling=444,
50457                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50458                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50459       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
50460                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
50461     }
50462 
50463     //! Load image sequence from a YUV file \overloading.
50464     CImg<T>& load_yuv(std::FILE *const file,
50465                       const unsigned int size_x, const unsigned int size_y=1,
50466                       const unsigned int chroma_subsampling=444,
50467                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50468 		      const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50469       return get_load_yuv(file,size_x,size_y,chroma_subsampling,
50470                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
50471     }
50472 
50473     //! Load image sequence from a YUV file \newinstance.
50474     static CImg<T> get_load_yuv(std::FILE *const file,
50475                                 const unsigned int size_x, const unsigned int size_y=1,
50476                                 const unsigned int chroma_subsampling=444,
50477                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50478 				const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50479       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
50480                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
50481     }
50482 
50483     //! Load 3d object from a .OFF file.
50484     /**
50485         \param[out] primitives Primitives data of the 3d object.
50486         \param[out] colors Colors data of the 3d object.
50487         \param filename Filename, as a C-string.
50488     **/
50489     template<typename tf, typename tc>
50490     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
50491       return _load_off(primitives,colors,0,filename);
50492     }
50493 
50494     //! Load 3d object from a .OFF file \newinstance.
50495     template<typename tf, typename tc>
50496     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
50497       return CImg<T>().load_off(primitives,colors,filename);
50498     }
50499 
50500     //! Load 3d object from a .OFF file \overloading.
50501     template<typename tf, typename tc>
50502     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
50503       return _load_off(primitives,colors,file,0);
50504     }
50505 
50506     //! Load 3d object from a .OFF file \newinstance.
50507     template<typename tf, typename tc>
50508     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
50509       return CImg<T>().load_off(primitives,colors,file);
50510     }
50511 
50512     template<typename tf, typename tc>
50513     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
50514                        std::FILE *const file, const char *const filename) {
50515       if (!file && !filename)
50516         throw CImgArgumentException(_cimg_instance
50517                                     "load_off(): Specified filename is (null).",
50518                                     cimg_instance);
50519 
50520       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
50521       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
50522       CImg<charT> line(256); *line = 0;
50523       int err;
50524 
50525       // Skip comments, and read magic string OFF
50526       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50527       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
50528         if (!file) cimg::fclose(nfile);
50529         throw CImgIOException(_cimg_instance
50530                               "load_off(): OFF header not found in file '%s'.",
50531                               cimg_instance,
50532                               filename?filename:"(FILE*)");
50533       }
50534       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50535       if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
50536         if (!file) cimg::fclose(nfile);
50537         throw CImgIOException(_cimg_instance
50538                               "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
50539                               cimg_instance,
50540                               filename?filename:"(FILE*)");
50541       }
50542 
50543       // Read points data
50544       assign(nb_points,3);
50545       float X = 0, Y = 0, Z = 0;
50546       cimg_forX(*this,l) {
50547         do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50548         if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
50549           if (!file) cimg::fclose(nfile);
50550           throw CImgIOException(_cimg_instance
50551                                 "load_off(): Failed to read vertex %u/%u in file '%s'.",
50552                                 cimg_instance,
50553                                 l + 1,nb_points,filename?filename:"(FILE*)");
50554         }
50555         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
50556       }
50557 
50558       // Read primitive data
50559       primitives.assign();
50560       colors.assign();
50561       bool stop_flag = false;
50562       while (!stop_flag) {
50563         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
50564         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
50565         *line = 0;
50566         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
50567         else {
50568           ++nb_read;
50569           switch (prim) {
50570           case 1 : {
50571             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
50572               cimg::warn(_cimg_instance
50573                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50574                          cimg_instance,
50575                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50576 
50577               err = std::fscanf(nfile,"%*[^\n] ");
50578             } else {
50579               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50580               CImg<tf>::vector(i0).move_to(primitives);
50581               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50582             }
50583           } break;
50584           case 2 : {
50585             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
50586               cimg::warn(_cimg_instance
50587                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50588                          cimg_instance,
50589                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50590 
50591               err = std::fscanf(nfile,"%*[^\n] ");
50592             } else {
50593               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50594               CImg<tf>::vector(i0,i1).move_to(primitives);
50595               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50596             }
50597           } break;
50598           case 3 : {
50599             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
50600               cimg::warn(_cimg_instance
50601                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50602                          cimg_instance,
50603                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50604 
50605               err = std::fscanf(nfile,"%*[^\n] ");
50606             } else {
50607               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50608               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
50609               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50610             }
50611           } break;
50612           case 4 : {
50613             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
50614               cimg::warn(_cimg_instance
50615                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50616                          cimg_instance,
50617                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50618 
50619               err = std::fscanf(nfile,"%*[^\n] ");
50620             } else {
50621               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50622               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50623               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50624             }
50625           } break;
50626           case 5 : {
50627             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
50628               cimg::warn(_cimg_instance
50629                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50630                          cimg_instance,
50631                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50632 
50633               err = std::fscanf(nfile,"%*[^\n] ");
50634             } else {
50635               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50636               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50637               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
50638               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50639               ++nb_primitives;
50640             }
50641           } break;
50642           case 6 : {
50643             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
50644               cimg::warn(_cimg_instance
50645                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50646                          cimg_instance,
50647                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50648 
50649               err = std::fscanf(nfile,"%*[^\n] ");
50650             } else {
50651               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50652               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50653               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
50654               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50655               ++nb_primitives;
50656             }
50657           } break;
50658           case 7 : {
50659             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
50660               cimg::warn(_cimg_instance
50661                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50662                          cimg_instance,
50663                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50664 
50665               err = std::fscanf(nfile,"%*[^\n] ");
50666             } else {
50667               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50668               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
50669               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
50670               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
50671               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50672               ++(++nb_primitives);
50673             }
50674           } break;
50675           case 8 : {
50676             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) {
50677               cimg::warn(_cimg_instance
50678                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50679                          cimg_instance,
50680                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50681 
50682               err = std::fscanf(nfile,"%*[^\n] ");
50683             } else {
50684               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50685               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50686               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
50687               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
50688               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50689               ++(++nb_primitives);
50690             }
50691           } break;
50692           default :
50693             cimg::warn(_cimg_instance
50694                        "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
50695                        cimg_instance,
50696                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
50697 
50698             err = std::fscanf(nfile,"%*[^\n] ");
50699           }
50700         }
50701       }
50702       if (!file) cimg::fclose(nfile);
50703       if (primitives._width!=nb_primitives)
50704         cimg::warn(_cimg_instance
50705                    "load_off(): Only %u/%u primitives read from file '%s'.",
50706                    cimg_instance,
50707                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
50708       return *this;
50709     }
50710 
50711     //! Load image sequence from a video file, using OpenCV library.
50712     /**
50713       \param filename Filename, as a C-string.
50714       \param first_frame Index of the first frame to read.
50715       \param last_frame Index of the last frame to read.
50716       \param step_frame Step value for frame reading.
50717       \param axis Alignment axis.
50718       \param align Apending alignment.
50719     **/
50720     CImg<T>& load_video(const char *const filename,
50721                         const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50722                         const unsigned int step_frame=1,
50723                         const char axis='z', const float align=0) {
50724       return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
50725     }
50726 
50727     //! Load image sequence from a video file, using OpenCV library \newinstance.
50728     static CImg<T> get_load_video(const char *const filename,
50729                                   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50730                                   const unsigned int step_frame=1,
50731                                   const char axis='z', const float align=0) {
50732       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
50733     }
50734 
50735     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
50736     /**
50737       \param filename Filename, as a C-string.
50738       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50739       \param align Appending alignment.
50740     **/
50741     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
50742       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
50743     }
50744 
50745     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
50746     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
50747       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
50748     }
50749 
50750     //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
50751     /**
50752       \param filename Filename, as a C-string.
50753       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50754       \param align Appending alignment.
50755     **/
50756     CImg<T>& load_gif_external(const char *const filename,
50757                                const char axis='z', const float align=0) {
50758       return get_load_gif_external(filename,axis,align).move_to(*this);
50759     }
50760 
50761     //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
50762     static CImg<T> get_load_gif_external(const char *const filename,
50763                                          const char axis='z', const float align=0) {
50764       return CImgList<T>().load_gif_external(filename).get_append(axis,align);
50765     }
50766 
50767     //! Load image using GraphicsMagick's external tool 'gm'.
50768     /**
50769        \param filename Filename, as a C-string.
50770     **/
50771     CImg<T>& load_graphicsmagick_external(const char *const filename) {
50772       if (!filename)
50773         throw CImgArgumentException(_cimg_instance
50774                                     "load_graphicsmagick_external(): Specified filename is (null).",
50775                                     cimg_instance);
50776       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50777       CImg<charT> command(1024), filename_tmp(256);
50778       std::FILE *file = 0;
50779       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
50780 #if cimg_OS==1
50781       if (!cimg::system("which gm")) {
50782         cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-",
50783                       cimg::graphicsmagick_path(),s_filename.data());
50784         file = popen(command,"r");
50785         if (file) {
50786           const unsigned int omode = cimg::exception_mode();
50787           cimg::exception_mode(0);
50788           try { load_pnm(file); } catch (...) {
50789             pclose(file);
50790             cimg::exception_mode(omode);
50791             throw CImgIOException(_cimg_instance
50792                                   "load_graphicsmagick_external(): Failed to load file '%s' "
50793                                   "with external command 'gm'.",
50794                                   cimg_instance,
50795                                   filename);
50796           }
50797           pclose(file);
50798           return *this;
50799         }
50800       }
50801 #endif
50802       do {
50803         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
50804                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50805         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50806       } while (file);
50807       cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"",
50808                     cimg::graphicsmagick_path(),s_filename.data(),
50809                     CImg<charT>::string(filename_tmp)._system_strescape().data());
50810       cimg::system(command,cimg::graphicsmagick_path());
50811       if (!(file = std_fopen(filename_tmp,"rb"))) {
50812         cimg::fclose(cimg::fopen(filename,"r"));
50813         throw CImgIOException(_cimg_instance
50814                               "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
50815                               cimg_instance,
50816                               filename);
50817 
50818       } else cimg::fclose(file);
50819       load_pnm(filename_tmp);
50820       std::remove(filename_tmp);
50821       return *this;
50822     }
50823 
50824     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
50825     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
50826       return CImg<T>().load_graphicsmagick_external(filename);
50827     }
50828 
50829     //! Load gzipped image file, using external tool 'gunzip'.
50830     /**
50831        \param filename Filename, as a C-string.
50832     **/
50833     CImg<T>& load_gzip_external(const char *const filename) {
50834       if (!filename)
50835         throw CImgIOException(_cimg_instance
50836                               "load_gzip_external(): Specified filename is (null).",
50837                               cimg_instance);
50838       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50839       CImg<charT> command(1024), filename_tmp(256), body(256);
50840       const char
50841         *const ext = cimg::split_filename(filename,body),
50842         *const ext2 = cimg::split_filename(body,0);
50843 
50844       std::FILE *file = 0;
50845       do {
50846         if (!cimg::strcasecmp(ext,"gz")) {
50847           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
50848                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
50849           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
50850                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50851         } else {
50852           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
50853                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
50854           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
50855                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50856         }
50857         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50858       } while (file);
50859       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
50860                     cimg::gunzip_path(),
50861                     CImg<charT>::string(filename)._system_strescape().data(),
50862                     CImg<charT>::string(filename_tmp)._system_strescape().data());
50863       cimg::system(command);
50864       if (!(file = std_fopen(filename_tmp,"rb"))) {
50865         cimg::fclose(cimg::fopen(filename,"r"));
50866         throw CImgIOException(_cimg_instance
50867                               "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
50868                               cimg_instance,
50869                               filename);
50870 
50871       } else cimg::fclose(file);
50872       load(filename_tmp);
50873       std::remove(filename_tmp);
50874       return *this;
50875     }
50876 
50877     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
50878     static CImg<T> get_load_gzip_external(const char *const filename) {
50879       return CImg<T>().load_gzip_external(filename);
50880     }
50881 
50882     //! Load image using ImageMagick's external tool 'convert'.
50883     /**
50884        \param filename Filename, as a C-string.
50885     **/
50886     CImg<T>& load_imagemagick_external(const char *const filename) {
50887       if (!filename)
50888         throw CImgArgumentException(_cimg_instance
50889                                     "load_imagemagick_external(): Specified filename is (null).",
50890                                     cimg_instance);
50891       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50892       CImg<charT> command(1024), filename_tmp(256);
50893       std::FILE *file = 0;
50894       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
50895 #if cimg_OS==1
50896       if (!cimg::system("which convert")) {
50897         cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-",
50898                       cimg::imagemagick_path(),
50899                       !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
50900                       s_filename.data());
50901         file = popen(command,"r");
50902         if (file) {
50903           const unsigned int omode = cimg::exception_mode();
50904           cimg::exception_mode(0);
50905           try { load_pnm(file); } catch (...) {
50906             pclose(file);
50907             cimg::exception_mode(omode);
50908             throw CImgIOException(_cimg_instance
50909                                   "load_imagemagick_external(): Failed to load file '%s' with "
50910                                   "external command 'magick/convert'.",
50911                                   cimg_instance,
50912                                   filename);
50913           }
50914           pclose(file);
50915           return *this;
50916         }
50917       }
50918 #endif
50919       do {
50920         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
50921                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50922         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50923       } while (file);
50924       cimg_snprintf(command,command._width,"%s%s \"%s\" \"%s\"",
50925                     cimg::imagemagick_path(),
50926                     !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
50927                     s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
50928       cimg::system(command,cimg::imagemagick_path());
50929       if (!(file = std_fopen(filename_tmp,"rb"))) {
50930         cimg::fclose(cimg::fopen(filename,"r"));
50931         throw CImgIOException(_cimg_instance
50932                               "load_imagemagick_external(): Failed to load file '%s' with "
50933                               "external command 'magick/convert'.",
50934                               cimg_instance,
50935                               filename);
50936 
50937       } else cimg::fclose(file);
50938       load_pnm(filename_tmp);
50939       std::remove(filename_tmp);
50940       return *this;
50941     }
50942 
50943     //! Load image using ImageMagick's external tool 'convert' \newinstance.
50944     static CImg<T> get_load_imagemagick_external(const char *const filename) {
50945       return CImg<T>().load_imagemagick_external(filename);
50946     }
50947 
50948     //! Load image from a DICOM file, using XMedcon's external tool 'medcon'.
50949     /**
50950        \param filename Filename, as a C-string.
50951     **/
50952     CImg<T>& load_medcon_external(const char *const filename) {
50953       if (!filename)
50954         throw CImgArgumentException(_cimg_instance
50955                                     "load_medcon_external(): Specified filename is (null).",
50956                                     cimg_instance);
50957       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50958       CImg<charT> command(1024), filename_tmp(256), body(256);
50959       cimg::fclose(cimg::fopen(filename,"r"));
50960       std::FILE *file = 0;
50961       do {
50962         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
50963         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50964       } while (file);
50965       cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"",
50966                     cimg::medcon_path(),
50967                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
50968                     CImg<charT>::string(filename)._system_strescape().data());
50969       cimg::system(command);
50970       cimg::split_filename(filename_tmp,body);
50971 
50972       cimg_snprintf(command,command._width,"%s.hdr",body._data);
50973       file = std_fopen(command,"rb");
50974       if (!file) {
50975         cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
50976         file = std_fopen(command,"rb");
50977         if (!file) {
50978           throw CImgIOException(_cimg_instance
50979                                 "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
50980                                 cimg_instance,
50981                                 filename);
50982         }
50983       }
50984       cimg::fclose(file);
50985       load_analyze(command);
50986       std::remove(command);
50987       cimg::split_filename(command,body);
50988       cimg_snprintf(command,command._width,"%s.img",body._data);
50989       std::remove(command);
50990       return *this;
50991     }
50992 
50993     //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance.
50994     static CImg<T> get_load_medcon_external(const char *const filename) {
50995       return CImg<T>().load_medcon_external(filename);
50996     }
50997 
50998     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
50999     /**
51000        \param filename Filename, as a C-string.
51001     **/
51002     CImg<T>& load_dcraw_external(const char *const filename) {
51003       if (!filename)
51004         throw CImgArgumentException(_cimg_instance
51005                                     "load_dcraw_external(): Specified filename is (null).",
51006                                     cimg_instance);
51007       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
51008       CImg<charT> command(1024), filename_tmp(256);
51009       std::FILE *file = 0;
51010       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
51011 #if cimg_OS==1
51012       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
51013                     cimg::dcraw_path(),s_filename.data());
51014       file = popen(command,"r");
51015       if (file) {
51016         const unsigned int omode = cimg::exception_mode();
51017         cimg::exception_mode(0);
51018         try { load_pnm(file); } catch (...) {
51019           pclose(file);
51020           cimg::exception_mode(omode);
51021           throw CImgIOException(_cimg_instance
51022                                 "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
51023                                 cimg_instance,
51024                                 filename);
51025         }
51026         pclose(file);
51027         return *this;
51028       }
51029 #endif
51030       do {
51031         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
51032                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
51033         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
51034       } while (file);
51035       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"",
51036                     cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
51037       cimg::system(command,cimg::dcraw_path());
51038       if (!(file = std_fopen(filename_tmp,"rb"))) {
51039         cimg::fclose(cimg::fopen(filename,"r"));
51040         throw CImgIOException(_cimg_instance
51041                               "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
51042                               cimg_instance,
51043                               filename);
51044 
51045       } else cimg::fclose(file);
51046       load_pnm(filename_tmp);
51047       std::remove(filename_tmp);
51048       return *this;
51049     }
51050 
51051     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
51052     static CImg<T> get_load_dcraw_external(const char *const filename) {
51053       return CImg<T>().load_dcraw_external(filename);
51054     }
51055 
51056     //! Load image from a camera stream, using OpenCV.
51057     /**
51058        \param camera_index Index of the camera to capture images from.
51059        \param skip_frames Number of frames to skip before the capture.
51060        \param release_camera Tells if the camera ressource must be released at the end of the method.
51061        \param capture_width Width of the desired image.
51062        \param capture_height Height of the desired image.
51063     **/
51064     CImg<T>& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
51065                          const bool release_camera=true, const unsigned int capture_width=0,
51066                          const unsigned int capture_height=0) {
51067 #ifdef cimg_use_opencv
51068       if (camera_index>99)
51069         throw CImgArgumentException(_cimg_instance
51070                                     "load_camera(): Invalid request for camera #%u "
51071                                     "(no more than 100 cameras can be managed simultaneously).",
51072                                     cimg_instance,
51073                                     camera_index);
51074       static CvCapture *capture[100] = { 0 };
51075       static unsigned int capture_w[100], capture_h[100];
51076       if (release_camera) {
51077         cimg::mutex(9);
51078         if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index]));
51079         capture[camera_index] = 0;
51080         capture_w[camera_index] = capture_h[camera_index] = 0;
51081         cimg::mutex(9,0);
51082         return *this;
51083       }
51084       if (!capture[camera_index]) {
51085         cimg::mutex(9);
51086         capture[camera_index] = cvCreateCameraCapture(camera_index);
51087         capture_w[camera_index] = 0;
51088         capture_h[camera_index] = 0;
51089         cimg::mutex(9,0);
51090         if (!capture[camera_index]) {
51091           throw CImgIOException(_cimg_instance
51092                                 "load_camera(): Failed to initialize camera #%u.",
51093                                 cimg_instance,
51094                                 camera_index);
51095         }
51096       }
51097       cimg::mutex(9);
51098       if (capture_width!=capture_w[camera_index]) {
51099         cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width);
51100         capture_w[camera_index] = capture_width;
51101       }
51102       if (capture_height!=capture_h[camera_index]) {
51103         cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height);
51104         capture_h[camera_index] = capture_height;
51105       }
51106       const IplImage *img = 0;
51107       for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[camera_index]);
51108       img = cvQueryFrame(capture[camera_index]);
51109       if (img) {
51110         const int step = (int)(img->widthStep - 3*img->width);
51111         assign(img->width,img->height,1,3);
51112         const unsigned char* ptrs = (unsigned char*)img->imageData;
51113         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
51114         if (step>0) cimg_forY(*this,y) {
51115             cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
51116             ptrs+=step;
51117           } else for (ulongT siz = (ulongT)img->width*img->height; siz; --siz) {
51118             *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
51119           }
51120       }
51121       cimg::mutex(9,0);
51122       return *this;
51123 #else
51124       cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
51125       throw CImgIOException(_cimg_instance
51126                             "load_camera(): This function requires the OpenCV library to run "
51127                             "(macro 'cimg_use_opencv' must be defined).",
51128                             cimg_instance);
51129 #endif
51130     }
51131 
51132     //! Load image from a camera stream, using OpenCV \newinstance.
51133     static CImg<T> get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
51134                                    const bool release_camera=true,
51135                                    const unsigned int capture_width=0, const unsigned int capture_height=0) {
51136       return CImg<T>().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height);
51137     }
51138 
51139     //! Load image using various non-native ways.
51140     /**
51141        \param filename Filename, as a C-string.
51142     **/
51143     CImg<T>& load_other(const char *const filename) {
51144       if (!filename)
51145         throw CImgArgumentException(_cimg_instance
51146                                     "load_other(): Specified filename is (null).",
51147                                     cimg_instance);
51148 
51149       const unsigned int omode = cimg::exception_mode();
51150       cimg::exception_mode(0);
51151       try { load_magick(filename); }
51152       catch (CImgException&) {
51153         try { load_imagemagick_external(filename); }
51154         catch (CImgException&) {
51155           try { load_graphicsmagick_external(filename); }
51156           catch (CImgException&) {
51157             try { load_cimg(filename); }
51158             catch (CImgException&) {
51159               try {
51160                 std::fclose(cimg::fopen(filename,"rb"));
51161               } catch (CImgException&) {
51162                 cimg::exception_mode(omode);
51163                 throw CImgIOException(_cimg_instance
51164                                       "load_other(): Failed to open file '%s'.",
51165                                       cimg_instance,
51166                                       filename);
51167               }
51168               cimg::exception_mode(omode);
51169               throw CImgIOException(_cimg_instance
51170                                     "load_other(): Failed to recognize format of file '%s'.",
51171                                     cimg_instance,
51172                                     filename);
51173             }
51174           }
51175         }
51176       }
51177       cimg::exception_mode(omode);
51178       return *this;
51179     }
51180 
51181     //! Load image using various non-native ways \newinstance.
51182     static CImg<T> get_load_other(const char *const filename) {
51183       return CImg<T>().load_other(filename);
51184     }
51185 
51186     //@}
51187     //---------------------------
51188     //
51189     //! \name Data Output
51190     //@{
51191     //---------------------------
51192 
51193     //! Display information about the image data.
51194     /**
51195        \param title Name for the considered image.
51196        \param display_stats Tells to compute and display image statistics.
51197     **/
51198     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
51199 
51200       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
51201       CImg<doubleT> st;
51202       if (!is_empty() && display_stats) {
51203         st = get_stats();
51204         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
51205         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
51206       }
51207 
51208       const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
51209         mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
51210 
51211       CImg<charT> _title(64);
51212       if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
51213 
51214       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
51215                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
51216                    cimg::t_bold,cimg::t_normal,(void*)this,
51217                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
51218                    (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
51219                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
51220                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
51221       if (_data)
51222         std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
51223       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
51224 
51225       if (!is_empty()) cimg_foroff(*this,off) {
51226         std::fprintf(cimg::output(),"%g",(double)_data[off]);
51227         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
51228         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
51229       }
51230       if (!is_empty() && display_stats)
51231 	std::fprintf(cimg::output(),
51232                      " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
51233                      "%scoords_max%s = (%u,%u,%u,%u).\n",
51234 		     cimg::t_bold,cimg::t_normal,st[0],
51235                      cimg::t_bold,cimg::t_normal,st[1],
51236                      cimg::t_bold,cimg::t_normal,st[2],
51237                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
51238                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
51239                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
51240       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
51241       std::fflush(cimg::output());
51242       return *this;
51243     }
51244 
51245     //! Display image into a CImgDisplay window.
51246     /**
51247        \param disp Display window.
51248     **/
51249     const CImg<T>& display(CImgDisplay& disp) const {
51250       disp.display(*this);
51251       return *this;
51252     }
51253 
51254     //! Display image into a CImgDisplay window, in an interactive way.
51255     /**
51256         \param disp Display window.
51257         \param display_info Tells if image information are displayed on the standard output.
51258         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
51259         \param exit_on_anykey Exit function when any key is pressed.
51260     **/
51261     const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
51262                            const bool exit_on_anykey=false) const {
51263       return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
51264     }
51265 
51266     //! Display image into an interactive window.
51267     /**
51268         \param title Window title
51269         \param display_info Tells if image information are displayed on the standard output.
51270         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
51271         \param exit_on_anykey Exit function when any key is pressed.
51272     **/
51273     const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
51274                            const bool exit_on_anykey=false) const {
51275       CImgDisplay disp;
51276       return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
51277     }
51278 
51279     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
51280                             unsigned int *const XYZ, const bool exit_on_anykey,
51281                             const bool exit_on_simpleclick) const {
51282       unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
51283       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
51284         old_mouse_x = -1, old_mouse_y = -1;
51285 
51286       if (!disp) {
51287         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
51288         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
51289         else disp.set_title("%s",title);
51290       } else if (title) disp.set_title("%s",title);
51291       disp.show().flush();
51292 
51293       const CImg<char> dtitle = CImg<char>::string(disp.title());
51294       if (display_info) print(dtitle);
51295 
51296       CImg<T> zoom;
51297       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
51298         if (reset_view) {
51299           if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
51300           else {
51301             _XYZ[0] = (unsigned int)(x0 + x1)/2;
51302             _XYZ[1] = (unsigned int)(y0 + y1)/2;
51303             _XYZ[2] = (unsigned int)(z0 + z1)/2;
51304           }
51305           x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
51306           oldw = disp._width; oldh = disp._height;
51307           reset_view = false;
51308         }
51309         if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
51310           if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
51311         } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
51312 
51313         const CImg<T>& visu = zoom?zoom:*this;
51314         const unsigned int
51315           dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
51316           tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
51317         if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
51318           const unsigned int
51319             ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
51320             dM = std::max(ttw,tth), diM = (unsigned int)std::max(disp.width(),disp.height()),
51321             imgw = std::max(16U,ttw*diM/dM), imgh = std::max(16U,tth*diM/dM);
51322           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
51323           resize_disp = false;
51324         }
51325         oldw = tw; oldh = th;
51326 
51327         bool
51328           go_up = false, go_down = false, go_left = false, go_right = false,
51329           go_inc = false, go_dec = false, go_in = false, go_out = false,
51330           go_in_center = false;
51331 
51332         disp.set_title("%s",dtitle._data);
51333         if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
51334         if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
51335         if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
51336 
51337         disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
51338         CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1);
51339         old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
51340         is_first_select = false;
51341 
51342         if (disp.wheel()) {
51343           if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
51344               (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
51345             go_left = !(go_right = disp.wheel()>0);
51346           } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
51347             go_down = !(go_up = disp.wheel()>0);
51348           } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51349             go_out = !(go_in = disp.wheel()>0); go_in_center = false;
51350           }
51351           disp.set_wheel();
51352         }
51353 
51354         if (disp.is_keyCTRLLEFT()) { // Alternative way for zooming and selection.
51355           if (selection[2]==selection[5]) { selection[2] = 0; selection[5] = visu.depth() - 1; }
51356           else if (selection[1]==selection[4]) { selection[1] = 0; selection[4] = visu.height() - 1; }
51357           else if (selection[0]==selection[3]) { selection[0] = 0; selection[3] = visu.width() - 1; }
51358         }
51359 
51360         const int
51361           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
51362           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
51363         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
51364           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
51365           x0+=sx0; y0+=sy0; z0+=sz0;
51366           if (sx0==sx1 && sy0==sy1 && sz0==sz1) {
51367             if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true;
51368           }
51369           resize_disp = true;
51370         } else switch (key = disp.key()) {
51371 #if cimg_OS!=2
51372           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
51373 #endif
51374           case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
51375           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
51376               // Special mode: play stack of frames
51377               const unsigned int
51378                 w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
51379                 h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
51380               float frame_timing = 5;
51381               bool is_stopped = false;
51382               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
51383               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
51384                 if (disp.is_resized()) disp.resize(false);
51385                 if (!timer) {
51386                   visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
51387                   (++_XYZ[2])%=visu._depth;
51388                 }
51389                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
51390                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
51391                 switch (key = disp.key()) {
51392 #if cimg_OS!=2
51393                 case cimg::keyCTRLRIGHT :
51394 #endif
51395                 case cimg::keyCTRLLEFT : key = 0; break;
51396                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
51397                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
51398                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
51399                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
51400                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
51401                   (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
51402                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51403                     disp.set_fullscreen(false).
51404                       resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51405                              CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
51406                     disp.set_key(key,false); key = 0;
51407                   } break;
51408                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51409                     disp.set_fullscreen(false).
51410                       resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
51411                   } break;
51412                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51413                     disp.set_fullscreen(false).
51414                       resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
51415                   } break;
51416                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51417                     disp.resize(disp.screen_width(),disp.screen_height(),false).
51418                       toggle_fullscreen().set_key(key,false); key = 0;
51419                   } break;
51420                 }
51421                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
51422                 disp.wait(20);
51423               }
51424               const unsigned int
51425                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
51426                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
51427               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
51428               key = 0;
51429             } break;
51430           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
51431           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
51432           case cimg::keyPADSUB : go_out = true; key = 0; break;
51433           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
51434           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
51435           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
51436           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
51437           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
51438           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
51439           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
51440           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
51441           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
51442           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
51443           }
51444         if (go_in) {
51445           const int
51446             mx = go_in_center?disp.width()/2:disp.mouse_x(),
51447             my = go_in_center?disp.height()/2:disp.mouse_y(),
51448             mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
51449             mY = my*(height() + (depth()>1?depth():0))/disp.height();
51450           int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
51451           if (mX<width() && mY<height())  {
51452             X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
51453           }
51454           if (mX<width() && mY>=height()) {
51455             X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
51456           }
51457           if (mX>=width() && mY<height()) {
51458             Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
51459           }
51460           if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
51461           if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
51462           if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
51463         }
51464         if (go_out) {
51465           const int
51466             delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
51467             ndelta_x = delta_x?delta_x:(_width>1),
51468             ndelta_y = delta_y?delta_y:(_height>1),
51469             ndelta_z = delta_z?delta_z:(_depth>1);
51470           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
51471           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
51472           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
51473           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
51474           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
51475           if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
51476           if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
51477           if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
51478           const float
51479             ratio = (float)(x1-x0)/(y1-y0),
51480             ratiow = (float)disp._width/disp._height,
51481             sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
51482           if (sub>0.01) resize_disp = true;
51483         }
51484         if (go_left) {
51485           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
51486           if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
51487           else { x1-=x0; x0 = 0; }
51488         }
51489         if (go_right) {
51490           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
51491           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
51492           else { x0+=(width() - 1 - x1); x1 = width() - 1; }
51493         }
51494         if (go_up) {
51495           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
51496           if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
51497           else { y1-=y0; y0 = 0; }
51498         }
51499         if (go_down) {
51500           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
51501           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
51502           else { y0+=(height() - 1 - y1); y1 = height() - 1; }
51503         }
51504         if (go_inc) {
51505           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
51506           if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
51507           else { z1-=z0; z0 = 0; }
51508         }
51509         if (go_dec) {
51510           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
51511           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
51512           else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
51513         }
51514         disp.wait(100);
51515         if (!exit_on_anykey && key && key!=cimg::keyESC &&
51516             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51517           key = 0;
51518         }
51519       }
51520       disp.set_key(key);
51521       if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
51522       return *this;
51523     }
51524 
51525     //! Display object 3d in an interactive window.
51526     /**
51527        \param disp Display window.
51528        \param vertices Vertices data of the 3d object.
51529        \param primitives Primitives data of the 3d object.
51530        \param colors Colors data of the 3d object.
51531        \param opacities Opacities data of the 3d object.
51532        \param centering Tells if the 3d object must be centered for the display.
51533        \param render_static Rendering mode.
51534        \param render_motion Rendering mode, when the 3d object is moved.
51535        \param is_double_sided Tells if the object primitives are double-sided.
51536        \param focale Focale
51537        \param light_x X-coordinate of the light source.
51538        \param light_y Y-coordinate of the light source.
51539        \param light_z Z-coordinate of the light source.
51540        \param specular_lightness Amount of specular light.
51541        \param specular_shininess Shininess of the object material.
51542        \param display_axes Tells if the 3d axes are displayed.
51543        \param pose_matrix Pointer to 12 values, defining a 3d pose (as a 4x3 matrix).
51544        \param exit_on_anykey Exit function when any key is pressed.
51545     **/
51546     template<typename tp, typename tf, typename tc, typename to>
51547     const CImg<T>& display_object3d(CImgDisplay& disp,
51548                                     const CImg<tp>& vertices,
51549                                     const CImgList<tf>& primitives,
51550                                     const CImgList<tc>& colors,
51551                                     const to& opacities,
51552                                     const bool centering=true,
51553                                     const int render_static=4, const int render_motion=1,
51554                                     const bool is_double_sided=true, const float focale=700,
51555                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51556                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51557                                     const bool display_axes=true, float *const pose_matrix=0,
51558                                     const bool exit_on_anykey=false) const {
51559       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
51560 			       render_motion,is_double_sided,focale,
51561                                light_x,light_y,light_z,specular_lightness,specular_shininess,
51562 			       display_axes,pose_matrix,exit_on_anykey);
51563     }
51564 
51565     //! Display object 3d in an interactive window \simplification.
51566     template<typename tp, typename tf, typename tc, typename to>
51567     const CImg<T>& display_object3d(const char *const title,
51568                                     const CImg<tp>& vertices,
51569                                     const CImgList<tf>& primitives,
51570                                     const CImgList<tc>& colors,
51571                                     const to& opacities,
51572                                     const bool centering=true,
51573                                     const int render_static=4, const int render_motion=1,
51574                                     const bool is_double_sided=true, const float focale=700,
51575                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51576                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51577                                     const bool display_axes=true, float *const pose_matrix=0,
51578                                     const bool exit_on_anykey=false) const {
51579       CImgDisplay disp;
51580       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
51581 			       render_motion,is_double_sided,focale,
51582                                light_x,light_y,light_z,specular_lightness,specular_shininess,
51583 			       display_axes,pose_matrix,exit_on_anykey);
51584     }
51585 
51586     //! Display object 3d in an interactive window \simplification.
51587     template<typename tp, typename tf, typename tc>
51588     const CImg<T>& display_object3d(CImgDisplay &disp,
51589                                     const CImg<tp>& vertices,
51590                                     const CImgList<tf>& primitives,
51591                                     const CImgList<tc>& colors,
51592                                     const bool centering=true,
51593                                     const int render_static=4, const int render_motion=1,
51594                                     const bool is_double_sided=true, const float focale=700,
51595                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51596                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51597                                     const bool display_axes=true, float *const pose_matrix=0,
51598                                     const bool exit_on_anykey=false) const {
51599       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
51600 			      render_static,render_motion,is_double_sided,focale,
51601                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51602 			      display_axes,pose_matrix,exit_on_anykey);
51603     }
51604 
51605     //! Display object 3d in an interactive window \simplification.
51606     template<typename tp, typename tf, typename tc>
51607     const CImg<T>& display_object3d(const char *const title,
51608 				    const CImg<tp>& vertices,
51609                                     const CImgList<tf>& primitives,
51610                                     const CImgList<tc>& colors,
51611                                     const bool centering=true,
51612                                     const int render_static=4, const int render_motion=1,
51613                                     const bool is_double_sided=true, const float focale=700,
51614                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51615                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51616                                     const bool display_axes=true, float *const pose_matrix=0,
51617                                     const bool exit_on_anykey=false) const {
51618       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
51619                               render_static,render_motion,is_double_sided,focale,
51620                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51621                               display_axes,pose_matrix,exit_on_anykey);
51622     }
51623 
51624     //! Display object 3d in an interactive window \simplification.
51625     template<typename tp, typename tf>
51626     const CImg<T>& display_object3d(CImgDisplay &disp,
51627                                     const CImg<tp>& vertices,
51628                                     const CImgList<tf>& primitives,
51629                                     const bool centering=true,
51630                                     const int render_static=4, const int render_motion=1,
51631                                     const bool is_double_sided=true, const float focale=700,
51632                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51633                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51634                                     const bool display_axes=true, float *const pose_matrix=0,
51635                                     const bool exit_on_anykey=false) const {
51636       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
51637                               render_static,render_motion,is_double_sided,focale,
51638                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51639                               display_axes,pose_matrix,exit_on_anykey);
51640     }
51641 
51642 
51643     //! Display object 3d in an interactive window \simplification.
51644     template<typename tp, typename tf>
51645     const CImg<T>& display_object3d(const char *const title,
51646 				    const CImg<tp>& vertices,
51647                                     const CImgList<tf>& primitives,
51648                                     const bool centering=true,
51649                                     const int render_static=4, const int render_motion=1,
51650                                     const bool is_double_sided=true, const float focale=700,
51651                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51652                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51653                                     const bool display_axes=true, float *const pose_matrix=0,
51654                                     const bool exit_on_anykey=false) const {
51655       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
51656                               render_static,render_motion,is_double_sided,focale,
51657                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51658                               display_axes,pose_matrix,exit_on_anykey);
51659     }
51660 
51661     //! Display object 3d in an interactive window \simplification.
51662     template<typename tp>
51663     const CImg<T>& display_object3d(CImgDisplay &disp,
51664                                     const CImg<tp>& vertices,
51665                                     const bool centering=true,
51666                                     const int render_static=4, const int render_motion=1,
51667                                     const bool is_double_sided=true, const float focale=700,
51668                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51669                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51670                                     const bool display_axes=true, float *const pose_matrix=0,
51671                                     const bool exit_on_anykey=false) const {
51672       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
51673                               render_static,render_motion,is_double_sided,focale,
51674                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51675                               display_axes,pose_matrix,exit_on_anykey);
51676     }
51677 
51678     //! Display object 3d in an interactive window \simplification.
51679     template<typename tp>
51680     const CImg<T>& display_object3d(const char *const title,
51681 				    const CImg<tp>& vertices,
51682                                     const bool centering=true,
51683                                     const int render_static=4, const int render_motion=1,
51684                                     const bool is_double_sided=true, const float focale=700,
51685                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51686                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51687                                     const bool display_axes=true, float *const pose_matrix=0,
51688                                     const bool exit_on_anykey=false) const {
51689       return display_object3d(title,vertices,CImgList<uintT>(),centering,
51690                               render_static,render_motion,is_double_sided,focale,
51691                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51692                               display_axes,pose_matrix,exit_on_anykey);
51693     }
51694 
51695     template<typename tp, typename tf, typename tc, typename to>
51696     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
51697 				     const CImg<tp>& vertices,
51698 				     const CImgList<tf>& primitives,
51699 				     const CImgList<tc>& colors,
51700                                      const to& opacities,
51701 				     const bool centering,
51702 				     const int render_static, const int render_motion,
51703 				     const bool is_double_sided, const float focale,
51704                                      const float light_x, const float light_y, const float light_z,
51705 				     const float specular_lightness, const float specular_shininess,
51706 				     const bool display_axes, float *const pose_matrix,
51707                                      const bool exit_on_anykey) const {
51708       typedef typename cimg::superset<tp,float>::type tpfloat;
51709 
51710       // Check input arguments
51711       if (is_empty()) {
51712 	if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
51713 		    _display_object3d(disp,title,vertices,primitives,colors,opacities,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 	else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
51718                                                                   CImgDisplay::screen_height()/2,1),
51719                                                    1,(colors && colors[0].size()==1)?1:3,3).
51720                _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
51721 				 render_static,render_motion,is_double_sided,focale,
51722                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
51723 				 display_axes,pose_matrix,exit_on_anykey);
51724       } else { if (disp) disp.resize(*this,false); }
51725       CImg<charT> error_message(1024);
51726       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
51727         throw CImgArgumentException(_cimg_instance
51728                                     "display_object3d(): Invalid specified 3d object (%u,%u) (%s).",
51729                                     cimg_instance,vertices._width,primitives._width,error_message.data());
51730       if (vertices._width && !primitives) {
51731         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
51732         cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
51733         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
51734 				 render_static,render_motion,is_double_sided,focale,
51735                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
51736 				 display_axes,pose_matrix,exit_on_anykey);
51737       }
51738       if (!disp) {
51739 	disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
51740         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
51741                                    pixel_type(),vertices._width,primitives._width);
51742       } else if (title) disp.set_title("%s",title);
51743 
51744       // Init 3d objects and compute object statistics
51745       CImg<floatT>
51746         pose,
51747         rotated_vertices(vertices._width,3),
51748         bbox_vertices, rotated_bbox_vertices,
51749         axes_vertices, rotated_axes_vertices,
51750         bbox_opacities, axes_opacities;
51751       CImgList<uintT> bbox_primitives, axes_primitives;
51752       CImgList<tf> reverse_primitives;
51753       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
51754       unsigned int ns_width = 0, ns_height = 0;
51755       int _is_double_sided = (int)is_double_sided;
51756       bool ndisplay_axes = display_axes;
51757       const CImg<T>
51758         background_color(1,1,1,_spectrum,0),
51759         foreground_color(1,1,1,_spectrum,255);
51760       float
51761         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
51762         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
51763         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
51764         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
51765       const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
51766 
51767       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
51768                                                    xm,xM,xM,xm,xm,xM,xM,xm,
51769                                                    ym,ym,yM,yM,ym,ym,yM,yM,
51770                                                    zm,zm,zm,zm,zM,zM,zM,zM);
51771       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);
51772       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
51773       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
51774       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
51775 
51776       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
51777                                                    0,20,0,0,22,-6,-6,
51778                                                    0,0,20,0,-6,22,-6,
51779                                                    0,0,0,20,0,0,22);
51780       axes_opacities.assign(3,1,1,1,1);
51781       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
51782       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
51783 
51784       // Begin user interaction loop
51785       CImg<T> visu0(*this,false), visu;
51786       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
51787       bool init_pose = true, clicked = false, redraw = true;
51788       unsigned int key = 0;
51789       int
51790         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
51791         nrender_static = render_static,
51792         nrender_motion = render_motion;
51793       disp.show().flush();
51794 
51795       while (!disp.is_closed() && !key) {
51796 
51797         // Init object pose
51798         if (init_pose) {
51799           const float
51800             ratio = delta>0?(2.0f*std::min(disp.width(),disp.height())/(3.0f*delta)):1,
51801             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
51802           if (centering)
51803             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
51804           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
51805           if (pose_matrix) {
51806             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
51807             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
51808             pose0(3,3) = pose(3,3) = 1;
51809             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
51810             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
51811           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
51812           init_pose = false;
51813           redraw = true;
51814         }
51815 
51816         // Rotate and draw 3d object
51817         if (redraw) {
51818           const float
51819             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
51820             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
51821             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
51822           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
51823             cimg_forX(vertices,l) {
51824               const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
51825               rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
51826               rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
51827               rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
51828             }
51829           else cimg_forX(bbox_vertices,l) {
51830               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
51831               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
51832               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
51833               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
51834             }
51835 
51836           // Draw objects
51837           const bool render_with_zbuffer = !clicked && nrender_static>0;
51838           visu = visu0;
51839           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
51840             visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51841                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
51842               draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51843                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
51844           else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
51845                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51846                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
51847                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
51848                                    width()/2.0f + light_x,height()/2.0f + light_y,light_z + Zoff,
51849                                    specular_lightness,specular_shininess,sprite_scale);
51850           // Draw axes
51851           if (ndisplay_axes) {
51852             const float
51853               n = 1e-8f + cimg::hypot(r00,r01,r02),
51854               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
51855               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
51856               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
51857               Xaxes = 25, Yaxes = visu._height - 38.0f;
51858             cimg_forX(axes_vertices,l) {
51859               const float
51860                 x = axes_vertices(l,0),
51861                 y = axes_vertices(l,1),
51862                 z = axes_vertices(l,2);
51863               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
51864               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
51865               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
51866             }
51867             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
51868             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
51869             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
51870             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
51871                                axes_colors,axes_opacities,1,false,focale).
51872               draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
51873                         (int)(Yaxes + rotated_axes_vertices(4,1)),
51874                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
51875               draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
51876                         (int)(Yaxes + rotated_axes_vertices(5,1)),
51877                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
51878               draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
51879                         (int)(Yaxes + rotated_axes_vertices(6,1)),
51880                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
51881           }
51882           visu.display(disp);
51883           if (!clicked || nrender_motion==nrender_static) redraw = false;
51884         }
51885 
51886         // Handle user interaction
51887         disp.wait();
51888         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
51889           redraw = true;
51890           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
51891           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
51892           if (disp.button()&1) {
51893             const float
51894               R = 0.45f*std::min(disp.width(),disp.height()),
51895               R2 = R*R,
51896               u0 = (float)(x0 - disp.width()/2),
51897               v0 = (float)(y0 - disp.height()/2),
51898               u1 = (float)(x1 - disp.width()/2),
51899               v1 = (float)(y1 - disp.height()/2),
51900               n0 = cimg::hypot(u0,v0),
51901               n1 = cimg::hypot(u1,v1),
51902               nu0 = n0>R?(u0*R/n0):u0,
51903               nv0 = n0>R?(v0*R/n0):v0,
51904               nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
51905               nu1 = n1>R?(u1*R/n1):u1,
51906               nv1 = n1>R?(v1*R/n1):v1,
51907               nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
51908               u = nv0*nw1 - nw0*nv1,
51909               v = nw0*nu1 - nu0*nw1,
51910               w = nv0*nu1 - nu0*nv1,
51911               n = cimg::hypot(u,v,w),
51912               alpha = (float)std::asin(n/R2)*180/cimg::PI;
51913             (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
51914             x0 = x1; y0 = y1;
51915           }
51916           if (disp.button()&2) {
51917             if (focale>0) Zoff-=(y0 - y1)*focale/400;
51918             else { const float s = std::exp((y0 - y1)/400.0f); pose*=s; sprite_scale*=s; }
51919             x0 = x1; y0 = y1;
51920           }
51921           if (disp.wheel()) {
51922             if (focale>0) Zoff-=disp.wheel()*focale/20;
51923             else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
51924             disp.set_wheel();
51925           }
51926           if (disp.button()&4) { Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; }
51927           if ((disp.button()&1) && (disp.button()&2)) {
51928             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
51929             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
51930           }
51931         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
51932 
51933         CImg<charT> filename(32);
51934         switch (key = disp.key()) {
51935 #if cimg_OS!=2
51936         case cimg::keyCTRLRIGHT :
51937 #endif
51938         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
51939         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51940             disp.set_fullscreen(false).
51941               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51942                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
51943               _is_resized = true;
51944             disp.set_key(key,false); key = 0;
51945           } break;
51946         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51947             disp.set_fullscreen(false).
51948               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
51949             disp.set_key(key,false); key = 0;
51950           } break;
51951         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51952             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
51953             disp.set_key(key,false); key = 0;
51954           } break;
51955         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51956             if (!ns_width || !ns_height ||
51957                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
51958               ns_width = disp.screen_width()*3U/4;
51959               ns_height = disp.screen_height()*3U/4;
51960             }
51961             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
51962             else {
51963               ns_width = disp._width; ns_height = disp._height;
51964               disp.resize(disp.screen_width(),disp.screen_height(),false);
51965             }
51966             disp.toggle_fullscreen()._is_resized = true;
51967             disp.set_key(key,false); key = 0;
51968           } break;
51969         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51970             // Switch single/double-sided primitives.
51971             if (--_is_double_sided==-2) _is_double_sided = 1;
51972             if (_is_double_sided>=0) reverse_primitives.assign();
51973             else primitives.get_reverse_object3d().move_to(reverse_primitives);
51974             disp.set_key(key,false); key = 0; redraw = true;
51975           } break;
51976         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
51977             if (zbuffer) zbuffer.assign();
51978             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
51979             disp.set_key(key,false); key = 0; redraw = true;
51980           } break;
51981         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
51982             ndisplay_axes = !ndisplay_axes;
51983             disp.set_key(key,false); key = 0; redraw = true;
51984           } break;
51985         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
51986             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
51987             disp.set_key(key,false); key = 0; redraw = true;
51988           } break;
51989         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
51990             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
51991             disp.set_key(key,false); key = 0; redraw = true;
51992           } break;
51993         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
51994             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
51995             disp.set_key(key,false); key = 0; redraw = true;
51996           } break;
51997         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
51998             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
51999             disp.set_key(key,false); key = 0; redraw = true;
52000           } break;
52001         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52002             // Set rendering mode to gouraud-shaded.
52003             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
52004             disp.set_key(key,false); key = 0; redraw = true;
52005           } break;
52006         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
52007             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
52008             disp.set_key(key,false); key = 0; redraw = true;
52009           } break;
52010         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
52011             static unsigned int snap_number = 0;
52012             std::FILE *file;
52013             do {
52014               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
52015               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52016             } while (file);
52017             (+visu).draw_text(0,0," Saving snapshot... ",
52018                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52019             visu.save(filename);
52020             (+visu).draw_text(0,0," Snapshot '%s' saved. ",
52021                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52022             disp.set_key(key,false); key = 0;
52023           } break;
52024         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
52025             static unsigned int snap_number = 0;
52026             std::FILE *file;
52027             do {
52028               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
52029               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52030             } while (file);
52031             (+visu).draw_text(0,0," Saving object... ",
52032                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52033             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
52034             (+visu).draw_text(0,0," Object '%s' saved. ",
52035                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52036             disp.set_key(key,false); key = 0;
52037           } break;
52038         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
52039             static unsigned int snap_number = 0;
52040             std::FILE *file;
52041             do {
52042 #ifdef cimg_use_zlib
52043               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
52044 #else
52045               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
52046 #endif
52047               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52048             } while (file);
52049             (+visu).draw_text(0,0," Saving object... ",
52050                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52051             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
52052               save(filename);
52053             (+visu).draw_text(0,0," Object '%s' saved. ",
52054                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52055             disp.set_key(key,false); key = 0;
52056           } break;
52057 #ifdef cimg_use_board
52058         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
52059             static unsigned int snap_number = 0;
52060             std::FILE *file;
52061             do {
52062               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
52063               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52064             } while (file);
52065             (+visu).draw_text(0,0," Saving EPS snapshot... ",
52066                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52067             LibBoard::Board board;
52068             (+visu)._draw_object3d(&board,zbuffer.fill(0),
52069                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
52070                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
52071                                    colors,opacities,clicked?nrender_motion:nrender_static,
52072                                    _is_double_sided==1,focale,
52073                                    visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
52074                                    specular_lightness,specular_shininess,
52075                                    sprite_scale);
52076             board.saveEPS(filename);
52077             (+visu).draw_text(0,0," Object '%s' saved. ",
52078                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52079             disp.set_key(key,false); key = 0;
52080           } break;
52081         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
52082             static unsigned int snap_number = 0;
52083             std::FILE *file;
52084             do {
52085               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
52086               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52087             } while (file);
52088             (+visu).draw_text(0,0," Saving SVG snapshot... ",
52089                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52090             LibBoard::Board board;
52091             (+visu)._draw_object3d(&board,zbuffer.fill(0),
52092                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
52093                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
52094                                    colors,opacities,clicked?nrender_motion:nrender_static,
52095                                    _is_double_sided==1,focale,
52096                                    visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
52097                                    specular_lightness,specular_shininess,
52098                                    sprite_scale);
52099             board.saveSVG(filename);
52100             (+visu).draw_text(0,0," Object '%s' saved. ",
52101                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52102             disp.set_key(key,false); key = 0;
52103           } break;
52104 #endif
52105         }
52106         if (disp.is_resized()) {
52107           disp.resize(false); visu0 = get_resize(disp,1);
52108           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
52109           redraw = true;
52110         }
52111         if (!exit_on_anykey && key && key!=cimg::keyESC &&
52112             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52113           key = 0;
52114         }
52115       }
52116       if (pose_matrix) {
52117         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
52118         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
52119       }
52120       disp.set_button().set_key(key);
52121       return *this;
52122     }
52123 
52124     //! Display 1d graph in an interactive window.
52125     /**
52126        \param disp Display window.
52127        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
52128        \param vertex_type Vertex type.
52129        \param labelx Title for the horizontal axis, as a C-string.
52130        \param xmin Minimum value along the X-axis.
52131        \param xmax Maximum value along the X-axis.
52132        \param labely Title for the vertical axis, as a C-string.
52133        \param ymin Minimum value along the X-axis.
52134        \param ymax Maximum value along the X-axis.
52135        \param exit_on_anykey Exit function when any key is pressed.
52136     **/
52137     const CImg<T>& display_graph(CImgDisplay &disp,
52138                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
52139                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
52140                                  const char *const labely=0, const double ymin=0, const double ymax=0,
52141                                  const bool exit_on_anykey=false) const {
52142       return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
52143     }
52144 
52145     //! Display 1d graph in an interactive window \overloading.
52146     const CImg<T>& display_graph(const char *const title=0,
52147                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
52148                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
52149                                  const char *const labely=0, const double ymin=0, const double ymax=0,
52150                                  const bool exit_on_anykey=false) const {
52151       CImgDisplay disp;
52152       return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
52153     }
52154 
52155     const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
52156                                   const unsigned int plot_type=1, const unsigned int vertex_type=1,
52157                                   const char *const labelx=0, const double xmin=0, const double xmax=0,
52158                                   const char *const labely=0, const double ymin=0, const double ymax=0,
52159                                   const bool exit_on_anykey=false) const {
52160       if (is_empty())
52161         throw CImgInstanceException(_cimg_instance
52162                                     "display_graph(): Empty instance.",
52163                                     cimg_instance);
52164       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
52165                    set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
52166       const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
52167       const unsigned int old_normalization = disp.normalization();
52168       disp.show().flush()._normalization = 0;
52169 
52170       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
52171       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
52172       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
52173 
52174       for (bool reset_view = true; !key && !disp.is_closed(); ) {
52175         if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
52176         CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
52177         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
52178         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
52179         if (y0==y1) { --y0; ++y1; }
52180 
52181         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
52182         					           labelx,
52183                                                            nxmin + x0*(nxmax - nxmin)/siz1,
52184                                                            nxmin + x1*(nxmax - nxmin)/siz1,
52185                                                            labely,y0,y1,true);
52186 	const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
52187         if (selection[0]>=0) {
52188           if (selection[2]<0) reset_view = true;
52189           else {
52190             x1 = x0 + selection[2]; x0+=selection[0];
52191             if (selection[1]>=0 && selection[3]>=0) {
52192               y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
52193               y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
52194             }
52195           }
52196         } else {
52197           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
52198           switch (key = (int)disp.key()) {
52199           case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
52200           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
52201           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
52202           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
52203             break;
52204           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
52205             break;
52206           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
52207           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
52208           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
52209           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
52210           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
52211           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
52212           }
52213           if (disp.wheel()) {
52214             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
52215             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
52216             else go_out = !(go_in = disp.wheel()>0);
52217             key = 0;
52218           }
52219 
52220           if (go_in) {
52221             const int
52222               xsiz = x1 - x0,
52223               mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
52224               cx = x0 + cimg::cut(mx,0,xsiz);
52225             if (x1 - x0>4) {
52226               x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
52227               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52228                 const double
52229                   ysiz = y1 - y0,
52230                   my = (mouse_y - 16)*ysiz/(disp.height() - 32),
52231                   cy = y1 - cimg::cut(my,0.0,ysiz);
52232                 y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
52233               } else y0 = y1 = 0;
52234             }
52235           }
52236           if (go_out) {
52237             if (x0>0 || x1<(int)siz1) {
52238               const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
52239               const double ndelta_y = (y1 - y0)/8;
52240               x0-=ndelta_x; x1+=ndelta_x;
52241               y0-=ndelta_y; y1+=ndelta_y;
52242               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
52243               if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
52244             }
52245           }
52246           if (go_left) {
52247             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
52248             if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
52249             else { x1-=x0; x0 = 0; }
52250             go_left = false;
52251           }
52252           if (go_right) {
52253             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
52254             if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
52255             else { x0+=(siz1 - x1); x1 = (int)siz1; }
52256             go_right = false;
52257           }
52258           if (go_up) {
52259             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
52260             y0+=ndelta; y1+=ndelta;
52261             go_up = false;
52262           }
52263           if (go_down) {
52264             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
52265             y0-=ndelta; y1-=ndelta;
52266             go_down = false;
52267           }
52268         }
52269         if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
52270             (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52271           disp.set_key(key,false);
52272           key = 0;
52273         }
52274       }
52275       disp._normalization = old_normalization;
52276       return *this;
52277     }
52278 
52279     //! Save image as a file.
52280     /**
52281        \param filename Filename, as a C-string.
52282        \param number When positive, represents an index added to the filename. Otherwise, no number is added.
52283        \param digits Number of digits used for adding the number to the filename.
52284        \note
52285        - The used file format is defined by the file extension in the filename \p filename.
52286        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
52287 
52288     **/
52289     const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
52290       if (!filename)
52291         throw CImgArgumentException(_cimg_instance
52292                                     "save(): Specified filename is (null).",
52293                                     cimg_instance);
52294       // Do not test for empty instances, since .cimg format is able to manage empty instances.
52295       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
52296       const char *const ext = cimg::split_filename(filename);
52297       CImg<charT> nfilename(1024);
52298       const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
52299         filename;
52300 
52301 #ifdef cimg_save_plugin
52302       cimg_save_plugin(fn);
52303 #endif
52304 #ifdef cimg_save_plugin1
52305       cimg_save_plugin1(fn);
52306 #endif
52307 #ifdef cimg_save_plugin2
52308       cimg_save_plugin2(fn);
52309 #endif
52310 #ifdef cimg_save_plugin3
52311       cimg_save_plugin3(fn);
52312 #endif
52313 #ifdef cimg_save_plugin4
52314       cimg_save_plugin4(fn);
52315 #endif
52316 #ifdef cimg_save_plugin5
52317       cimg_save_plugin5(fn);
52318 #endif
52319 #ifdef cimg_save_plugin6
52320       cimg_save_plugin6(fn);
52321 #endif
52322 #ifdef cimg_save_plugin7
52323       cimg_save_plugin7(fn);
52324 #endif
52325 #ifdef cimg_save_plugin8
52326       cimg_save_plugin8(fn);
52327 #endif
52328       // Ascii formats
52329       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
52330       else if (!cimg::strcasecmp(ext,"dlm") ||
52331                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
52332       else if (!cimg::strcasecmp(ext,"cpp") ||
52333                !cimg::strcasecmp(ext,"hpp") ||
52334                !cimg::strcasecmp(ext,"h") ||
52335                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
52336 
52337       // 2d binary formats
52338       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
52339       else if (!cimg::strcasecmp(ext,"jpg") ||
52340                !cimg::strcasecmp(ext,"jpeg") ||
52341                !cimg::strcasecmp(ext,"jpe") ||
52342                !cimg::strcasecmp(ext,"jfif") ||
52343                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
52344       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
52345       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
52346       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
52347       else if (!cimg::strcasecmp(ext,"pgm") ||
52348                !cimg::strcasecmp(ext,"ppm") ||
52349                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
52350       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
52351       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
52352       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
52353       else if (!cimg::strcasecmp(ext,"tif") ||
52354                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
52355 
52356       // 3d binary formats
52357       else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
52358       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
52359       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
52360       else if (!cimg::strcasecmp(ext,"hdr") ||
52361                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
52362       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
52363       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
52364       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
52365       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
52366 
52367       // Archive files
52368       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
52369 
52370       // Image sequences
52371       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
52372       else if (!cimg::strcasecmp(ext,"avi") ||
52373                !cimg::strcasecmp(ext,"mov") ||
52374                !cimg::strcasecmp(ext,"asf") ||
52375                !cimg::strcasecmp(ext,"divx") ||
52376                !cimg::strcasecmp(ext,"flv") ||
52377                !cimg::strcasecmp(ext,"mpg") ||
52378                !cimg::strcasecmp(ext,"m1v") ||
52379                !cimg::strcasecmp(ext,"m2v") ||
52380                !cimg::strcasecmp(ext,"m4v") ||
52381                !cimg::strcasecmp(ext,"mjp") ||
52382                !cimg::strcasecmp(ext,"mp4") ||
52383                !cimg::strcasecmp(ext,"mkv") ||
52384                !cimg::strcasecmp(ext,"mpe") ||
52385                !cimg::strcasecmp(ext,"movie") ||
52386                !cimg::strcasecmp(ext,"ogm") ||
52387                !cimg::strcasecmp(ext,"ogg") ||
52388                !cimg::strcasecmp(ext,"ogv") ||
52389                !cimg::strcasecmp(ext,"qt") ||
52390                !cimg::strcasecmp(ext,"rm") ||
52391                !cimg::strcasecmp(ext,"vob") ||
52392                !cimg::strcasecmp(ext,"wmv") ||
52393                !cimg::strcasecmp(ext,"xvid") ||
52394                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
52395       return save_other(fn);
52396     }
52397 
52398     //! Save image as an ascii file.
52399     /**
52400       \param filename Filename, as a C-string.
52401     **/
52402     const CImg<T>& save_ascii(const char *const filename) const {
52403       return _save_ascii(0,filename);
52404     }
52405 
52406     //! Save image as an ascii file \overloading.
52407     const CImg<T>& save_ascii(std::FILE *const file) const {
52408       return _save_ascii(file,0);
52409     }
52410 
52411     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
52412       if (!file && !filename)
52413         throw CImgArgumentException(_cimg_instance
52414                                     "save_ascii(): Specified filename is (null).",
52415                                     cimg_instance);
52416       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52417       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
52418       const T* ptrs = _data;
52419       cimg_forYZC(*this,y,z,c) {
52420         cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
52421         std::fputc('\n',nfile);
52422       }
52423       if (!file) cimg::fclose(nfile);
52424       return *this;
52425     }
52426 
52427     //! Save image as a .cpp source file.
52428     /**
52429       \param filename Filename, as a C-string.
52430     **/
52431     const CImg<T>& save_cpp(const char *const filename) const {
52432       return _save_cpp(0,filename);
52433     }
52434 
52435     //! Save image as a .cpp source file \overloading.
52436     const CImg<T>& save_cpp(std::FILE *const file) const {
52437       return _save_cpp(file,0);
52438     }
52439 
52440     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
52441       if (!file && !filename)
52442         throw CImgArgumentException(_cimg_instance
52443                                     "save_cpp(): Specified filename is (null).",
52444                                     cimg_instance);
52445       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52446       CImg<charT> varname(1024); *varname = 0;
52447       if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
52448       if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
52449       std::fprintf(nfile,
52450                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
52451                    "%s data_%s[] = { %s\n  ",
52452                    varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
52453                    is_empty()?"};":"");
52454       if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
52455         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
52456         if (off==siz) std::fprintf(nfile," };\n");
52457         else if (!((off + 1)%16)) std::fprintf(nfile,",\n  ");
52458         else std::fprintf(nfile,", ");
52459       }
52460       if (!file) cimg::fclose(nfile);
52461       return *this;
52462     }
52463 
52464     //! Save image as a DLM file.
52465     /**
52466        \param filename Filename, as a C-string.
52467     **/
52468     const CImg<T>& save_dlm(const char *const filename) const {
52469       return _save_dlm(0,filename);
52470     }
52471 
52472     //! Save image as a DLM file \overloading.
52473     const CImg<T>& save_dlm(std::FILE *const file) const {
52474       return _save_dlm(file,0);
52475     }
52476 
52477     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
52478       if (!file && !filename)
52479         throw CImgArgumentException(_cimg_instance
52480                                     "save_dlm(): Specified filename is (null).",
52481                                     cimg_instance);
52482       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52483       if (_depth>1)
52484         cimg::warn(_cimg_instance
52485                    "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
52486                    cimg_instance,
52487                    filename?filename:"(FILE*)");
52488       if (_spectrum>1)
52489         cimg::warn(_cimg_instance
52490                    "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
52491                    cimg_instance,
52492                    filename?filename:"(FILE*)");
52493 
52494       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52495       const T* ptrs = _data;
52496       cimg_forYZC(*this,y,z,c) {
52497         cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
52498         std::fputc('\n',nfile);
52499       }
52500       if (!file) cimg::fclose(nfile);
52501       return *this;
52502     }
52503 
52504     //! Save image as a BMP file.
52505     /**
52506       \param filename Filename, as a C-string.
52507     **/
52508     const CImg<T>& save_bmp(const char *const filename) const {
52509       return _save_bmp(0,filename);
52510     }
52511 
52512     //! Save image as a BMP file \overloading.
52513     const CImg<T>& save_bmp(std::FILE *const file) const {
52514       return _save_bmp(file,0);
52515     }
52516 
52517     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
52518       if (!file && !filename)
52519         throw CImgArgumentException(_cimg_instance
52520                                     "save_bmp(): Specified filename is (null).",
52521                                     cimg_instance);
52522       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52523       if (_depth>1)
52524         cimg::warn(_cimg_instance
52525                    "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52526                    cimg_instance,
52527                    filename?filename:"(FILE*)");
52528       if (_spectrum>3)
52529         cimg::warn(_cimg_instance
52530                    "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
52531                    cimg_instance,
52532                    filename?filename:"(FILE*)");
52533 
52534       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
52535       CImg<ucharT> header(54,1,1,1,0);
52536       unsigned char align_buf[4] = { 0 };
52537       const unsigned int
52538         align = (4 - (3*_width)%4)%4,
52539         buf_size = (3*_width + align)*height(),
52540         file_size = 54 + buf_size;
52541       header[0] = 'B'; header[1] = 'M';
52542       header[0x02] = file_size&0xFF;
52543       header[0x03] = (file_size>>8)&0xFF;
52544       header[0x04] = (file_size>>16)&0xFF;
52545       header[0x05] = (file_size>>24)&0xFF;
52546       header[0x0A] = 0x36;
52547       header[0x0E] = 0x28;
52548       header[0x12] = _width&0xFF;
52549       header[0x13] = (_width>>8)&0xFF;
52550       header[0x14] = (_width>>16)&0xFF;
52551       header[0x15] = (_width>>24)&0xFF;
52552       header[0x16] = _height&0xFF;
52553       header[0x17] = (_height>>8)&0xFF;
52554       header[0x18] = (_height>>16)&0xFF;
52555       header[0x19] = (_height>>24)&0xFF;
52556       header[0x1A] = 1;
52557       header[0x1B] = 0;
52558       header[0x1C] = 24;
52559       header[0x1D] = 0;
52560       header[0x22] = buf_size&0xFF;
52561       header[0x23] = (buf_size>>8)&0xFF;
52562       header[0x24] = (buf_size>>16)&0xFF;
52563       header[0x25] = (buf_size>>24)&0xFF;
52564       header[0x27] = 0x1;
52565       header[0x2B] = 0x1;
52566       cimg::fwrite(header._data,54,nfile);
52567 
52568       const T
52569         *ptr_r = data(0,_height - 1,0,0),
52570         *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
52571         *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
52572 
52573       switch (_spectrum) {
52574       case 1 : {
52575         cimg_forY(*this,y) {
52576           cimg_forX(*this,x) {
52577             const unsigned char val = (unsigned char)*(ptr_r++);
52578             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
52579           }
52580           cimg::fwrite(align_buf,align,nfile);
52581           ptr_r-=2*_width;
52582         }
52583       } break;
52584       case 2 : {
52585         cimg_forY(*this,y) {
52586           cimg_forX(*this,x) {
52587             std::fputc(0,nfile);
52588             std::fputc((unsigned char)(*(ptr_g++)),nfile);
52589             std::fputc((unsigned char)(*(ptr_r++)),nfile);
52590           }
52591           cimg::fwrite(align_buf,align,nfile);
52592           ptr_r-=2*_width; ptr_g-=2*_width;
52593         }
52594       } break;
52595       default : {
52596         cimg_forY(*this,y) {
52597           cimg_forX(*this,x) {
52598             std::fputc((unsigned char)(*(ptr_b++)),nfile);
52599             std::fputc((unsigned char)(*(ptr_g++)),nfile);
52600             std::fputc((unsigned char)(*(ptr_r++)),nfile);
52601           }
52602           cimg::fwrite(align_buf,align,nfile);
52603           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
52604         }
52605       }
52606       }
52607       if (!file) cimg::fclose(nfile);
52608       return *this;
52609     }
52610 
52611     //! Save image as a JPEG file.
52612     /**
52613       \param filename Filename, as a C-string.
52614       \param quality Image quality (in %)
52615     **/
52616     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
52617       return _save_jpeg(0,filename,quality);
52618     }
52619 
52620     //! Save image as a JPEG file \overloading.
52621     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
52622       return _save_jpeg(file,0,quality);
52623     }
52624 
52625     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
52626       if (!file && !filename)
52627         throw CImgArgumentException(_cimg_instance
52628                                     "save_jpeg(): Specified filename is (null).",
52629                                     cimg_instance);
52630       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52631       if (_depth>1)
52632         cimg::warn(_cimg_instance
52633                    "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52634                    cimg_instance,
52635                    filename?filename:"(FILE*)");
52636 
52637 #ifndef cimg_use_jpeg
52638       if (!file) return save_other(filename,quality);
52639       else throw CImgIOException(_cimg_instance
52640                                  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
52641                                  cimg_instance);
52642 #else
52643       unsigned int dimbuf = 0;
52644       J_COLOR_SPACE colortype = JCS_RGB;
52645 
52646       switch (_spectrum) {
52647       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
52648       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
52649       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
52650       default : dimbuf = 4; colortype = JCS_CMYK; break;
52651       }
52652 
52653       // Call libjpeg functions
52654       struct jpeg_compress_struct cinfo;
52655       struct jpeg_error_mgr jerr;
52656       cinfo.err = jpeg_std_error(&jerr);
52657       jpeg_create_compress(&cinfo);
52658       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
52659       jpeg_stdio_dest(&cinfo,nfile);
52660       cinfo.image_width = _width;
52661       cinfo.image_height = _height;
52662       cinfo.input_components = dimbuf;
52663       cinfo.in_color_space = colortype;
52664       jpeg_set_defaults(&cinfo);
52665       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
52666       jpeg_start_compress(&cinfo,TRUE);
52667 
52668       JSAMPROW row_pointer[1];
52669       CImg<ucharT> buffer(_width*dimbuf);
52670 
52671       while (cinfo.next_scanline<cinfo.image_height) {
52672         unsigned char *ptrd = buffer._data;
52673 
52674         // Fill pixel buffer
52675         switch (_spectrum) {
52676         case 1 : { // Greyscale images
52677           const T *ptr_g = data(0, cinfo.next_scanline);
52678           for (unsigned int b = 0; b<cinfo.image_width; b++)
52679             *(ptrd++) = (unsigned char)*(ptr_g++);
52680         } break;
52681         case 2 : { // RG images
52682           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52683             *ptr_g = data(0,cinfo.next_scanline,0,1);
52684           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52685             *(ptrd++) = (unsigned char)*(ptr_r++);
52686             *(ptrd++) = (unsigned char)*(ptr_g++);
52687             *(ptrd++) = 0;
52688           }
52689         } break;
52690         case 3 : { // RGB images
52691           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52692             *ptr_g = data(0,cinfo.next_scanline,0,1),
52693             *ptr_b = data(0,cinfo.next_scanline,0,2);
52694           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52695             *(ptrd++) = (unsigned char)*(ptr_r++);
52696             *(ptrd++) = (unsigned char)*(ptr_g++);
52697             *(ptrd++) = (unsigned char)*(ptr_b++);
52698           }
52699         } break;
52700         default : { // CMYK images
52701           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52702             *ptr_g = data(0,cinfo.next_scanline,0,1),
52703             *ptr_b = data(0,cinfo.next_scanline,0,2),
52704             *ptr_a = data(0,cinfo.next_scanline,0,3);
52705           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52706             *(ptrd++) = (unsigned char)*(ptr_r++);
52707             *(ptrd++) = (unsigned char)*(ptr_g++);
52708             *(ptrd++) = (unsigned char)*(ptr_b++);
52709             *(ptrd++) = (unsigned char)*(ptr_a++);
52710           }
52711         }
52712         }
52713         *row_pointer = buffer._data;
52714         jpeg_write_scanlines(&cinfo,row_pointer,1);
52715       }
52716       jpeg_finish_compress(&cinfo);
52717       if (!file) cimg::fclose(nfile);
52718       jpeg_destroy_compress(&cinfo);
52719       return *this;
52720 #endif
52721     }
52722 
52723     //! Save image, using built-in ImageMagick++ library.
52724     /**
52725       \param filename Filename, as a C-string.
52726       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
52727     **/
52728     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
52729       if (!filename)
52730         throw CImgArgumentException(_cimg_instance
52731                                     "save_magick(): Specified filename is (null).",
52732                                     cimg_instance);
52733       if (is_empty()) { cimg::fempty(0,filename); return *this; }
52734 
52735 #ifdef cimg_use_magick
52736       double stmin, stmax = (double)max_min(stmin);
52737       if (_depth>1)
52738         cimg::warn(_cimg_instance
52739                    "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52740                    cimg_instance,
52741                    filename);
52742 
52743       if (_spectrum>3)
52744         cimg::warn(_cimg_instance
52745                    "save_magick(): Instance is multispectral, only the three first channels will be "
52746                    "saved in file '%s'.",
52747                    cimg_instance,
52748                    filename);
52749 
52750       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
52751         cimg::warn(_cimg_instance
52752                    "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
52753                    cimg_instance,
52754                    filename,stmin,stmax);
52755 
52756       Magick::Image image(Magick::Geometry(_width,_height),"black");
52757       image.type(Magick::TrueColorType);
52758       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
52759       const T
52760         *ptr_r = data(0,0,0,0),
52761         *ptr_g = _spectrum>1?data(0,0,0,1):0,
52762         *ptr_b = _spectrum>2?data(0,0,0,2):0;
52763       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
52764       switch (_spectrum) {
52765       case 1 : // Scalar images
52766         for (ulongT off = (ulongT)_width*_height; off; --off) {
52767           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
52768           ++pixels;
52769         }
52770         break;
52771       case 2 : // RG images
52772         for (ulongT off = (ulongT)_width*_height; off; --off) {
52773           pixels->red = (Magick::Quantum)*(ptr_r++);
52774           pixels->green = (Magick::Quantum)*(ptr_g++);
52775           pixels->blue = 0; ++pixels;
52776         }
52777         break;
52778       default : // RGB images
52779         for (ulongT off = (ulongT)_width*_height; off; --off) {
52780           pixels->red = (Magick::Quantum)*(ptr_r++);
52781           pixels->green = (Magick::Quantum)*(ptr_g++);
52782           pixels->blue = (Magick::Quantum)*(ptr_b++);
52783           ++pixels;
52784         }
52785       }
52786       image.syncPixels();
52787       image.write(filename);
52788       return *this;
52789 #else
52790       cimg::unused(bytes_per_pixel);
52791       throw CImgIOException(_cimg_instance
52792                             "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
52793                             cimg_instance,
52794                             filename);
52795 #endif
52796     }
52797 
52798     //! Save image as a PNG file.
52799     /**
52800        \param filename Filename, as a C-string.
52801        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
52802     **/
52803     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
52804       return _save_png(0,filename,bytes_per_pixel);
52805     }
52806 
52807     //! Save image as a PNG file \overloading.
52808     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
52809       return _save_png(file,0,bytes_per_pixel);
52810     }
52811 
52812     const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
52813                              const unsigned int bytes_per_pixel=0) const {
52814       if (!file && !filename)
52815         throw CImgArgumentException(_cimg_instance
52816                                     "save_png(): Specified filename is (null).",
52817                                     cimg_instance);
52818       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52819 
52820 #ifndef cimg_use_png
52821       cimg::unused(bytes_per_pixel);
52822       if (!file) return save_other(filename);
52823       else throw CImgIOException(_cimg_instance
52824                                  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
52825                                  cimg_instance);
52826 #else
52827 
52828 #if defined __GNUC__
52829       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
52830       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
52831       volatile double stmin, stmax = (double)max_min(stmin);
52832 #else
52833       const char *nfilename = filename;
52834       std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
52835       double stmin, stmax = (double)max_min(stmin);
52836 #endif
52837 
52838       if (_depth>1)
52839         cimg::warn(_cimg_instance
52840                    "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52841                    cimg_instance,
52842                    filename);
52843 
52844       if (_spectrum>4)
52845         cimg::warn(_cimg_instance
52846                    "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
52847                    cimg_instance,
52848                    filename);
52849 
52850       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
52851         cimg::warn(_cimg_instance
52852                    "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
52853                    cimg_instance,
52854                    filename,stmin,stmax);
52855 
52856       // Setup PNG structures for write
52857       png_voidp user_error_ptr = 0;
52858       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
52859       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
52860                                                     user_warning_fn);
52861       if (!png_ptr){
52862         if (!file) cimg::fclose(nfile);
52863         throw CImgIOException(_cimg_instance
52864                               "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
52865                               cimg_instance,
52866                               nfilename?nfilename:"(FILE*)");
52867       }
52868       png_infop info_ptr = png_create_info_struct(png_ptr);
52869       if (!info_ptr) {
52870         png_destroy_write_struct(&png_ptr,(png_infopp)0);
52871         if (!file) cimg::fclose(nfile);
52872         throw CImgIOException(_cimg_instance
52873                               "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
52874                               cimg_instance,
52875                               nfilename?nfilename:"(FILE*)");
52876       }
52877       if (setjmp(png_jmpbuf(png_ptr))) {
52878         png_destroy_write_struct(&png_ptr, &info_ptr);
52879         if (!file) cimg::fclose(nfile);
52880         throw CImgIOException(_cimg_instance
52881                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
52882                               cimg_instance,
52883                               nfilename?nfilename:"(FILE*)");
52884       }
52885       png_init_io(png_ptr, nfile);
52886 
52887       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
52888 
52889       int color_type;
52890       switch (spectrum()) {
52891       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
52892       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
52893       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
52894       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
52895       }
52896       const int interlace_type = PNG_INTERLACE_NONE;
52897       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
52898       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
52899       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
52900       png_write_info(png_ptr,info_ptr);
52901       const int byte_depth = bit_depth>>3;
52902       const int numChan = spectrum()>4?4:spectrum();
52903       const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
52904 
52905       // Allocate Memory for Image Save and Fill pixel data
52906       png_bytep *const imgData = new png_byte*[_height];
52907       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
52908       const T *pC0 = data(0,0,0,0);
52909       switch (pixel_bit_depth_flag) {
52910       case 7 :  { // Gray 8-bit
52911         cimg_forY(*this,y) {
52912           unsigned char *ptrd = imgData[y];
52913           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
52914         }
52915       } break;
52916       case 14 : { // Gray w/ Alpha 8-bit
52917         const T *pC1 = data(0,0,0,1);
52918         cimg_forY(*this,y) {
52919           unsigned char *ptrd = imgData[y];
52920           cimg_forX(*this,x) {
52921             *(ptrd++) = (unsigned char)*(pC0++);
52922             *(ptrd++) = (unsigned char)*(pC1++);
52923           }
52924         }
52925       } break;
52926       case 21 :  { // RGB 8-bit
52927         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
52928         cimg_forY(*this,y) {
52929           unsigned char *ptrd = imgData[y];
52930           cimg_forX(*this,x) {
52931             *(ptrd++) = (unsigned char)*(pC0++);
52932             *(ptrd++) = (unsigned char)*(pC1++);
52933             *(ptrd++) = (unsigned char)*(pC2++);
52934           }
52935         }
52936       } break;
52937       case 28 : { // RGB x/ Alpha 8-bit
52938         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
52939         cimg_forY(*this,y){
52940           unsigned char *ptrd = imgData[y];
52941           cimg_forX(*this,x){
52942             *(ptrd++) = (unsigned char)*(pC0++);
52943             *(ptrd++) = (unsigned char)*(pC1++);
52944             *(ptrd++) = (unsigned char)*(pC2++);
52945             *(ptrd++) = (unsigned char)*(pC3++);
52946           }
52947         }
52948       } break;
52949       case 15 : { // Gray 16-bit
52950         cimg_forY(*this,y){
52951           unsigned short *ptrd = (unsigned short*)(imgData[y]);
52952           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
52953           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
52954         }
52955       } break;
52956       case 30 : { // Gray w/ Alpha 16-bit
52957         const T *pC1 = data(0,0,0,1);
52958         cimg_forY(*this,y){
52959           unsigned short *ptrd = (unsigned short*)(imgData[y]);
52960           cimg_forX(*this,x) {
52961             *(ptrd++) = (unsigned short)*(pC0++);
52962             *(ptrd++) = (unsigned short)*(pC1++);
52963           }
52964           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
52965         }
52966       } break;
52967       case 45 : { // RGB 16-bit
52968         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
52969         cimg_forY(*this,y) {
52970           unsigned short *ptrd = (unsigned short*)(imgData[y]);
52971           cimg_forX(*this,x) {
52972             *(ptrd++) = (unsigned short)*(pC0++);
52973             *(ptrd++) = (unsigned short)*(pC1++);
52974             *(ptrd++) = (unsigned short)*(pC2++);
52975           }
52976           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
52977         }
52978       } break;
52979       case 60 : { // RGB w/ Alpha 16-bit
52980         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
52981         cimg_forY(*this,y) {
52982           unsigned short *ptrd = (unsigned short*)(imgData[y]);
52983           cimg_forX(*this,x) {
52984             *(ptrd++) = (unsigned short)*(pC0++);
52985             *(ptrd++) = (unsigned short)*(pC1++);
52986             *(ptrd++) = (unsigned short)*(pC2++);
52987             *(ptrd++) = (unsigned short)*(pC3++);
52988           }
52989           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
52990         }
52991       } break;
52992       default :
52993         if (!file) cimg::fclose(nfile);
52994         throw CImgIOException(_cimg_instance
52995                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
52996                               cimg_instance,
52997                               nfilename?nfilename:"(FILE*)");
52998       }
52999       png_write_image(png_ptr,imgData);
53000       png_write_end(png_ptr,info_ptr);
53001       png_destroy_write_struct(&png_ptr, &info_ptr);
53002 
53003       // Deallocate Image Write Memory
53004       cimg_forY(*this,n) delete[] imgData[n];
53005       delete[] imgData;
53006 
53007       if (!file) cimg::fclose(nfile);
53008       return *this;
53009 #endif
53010     }
53011 
53012     //! Save image as a PNM file.
53013     /**
53014       \param filename Filename, as a C-string.
53015       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
53016     **/
53017     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
53018       return _save_pnm(0,filename,bytes_per_pixel);
53019     }
53020 
53021     //! Save image as a PNM file \overloading.
53022     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
53023       return _save_pnm(file,0,bytes_per_pixel);
53024     }
53025 
53026     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
53027                              const unsigned int bytes_per_pixel=0) const {
53028       if (!file && !filename)
53029         throw CImgArgumentException(_cimg_instance
53030                                     "save_pnm(): Specified filename is (null).",
53031                                     cimg_instance);
53032       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53033 
53034       double stmin, stmax = (double)max_min(stmin);
53035       if (_depth>1)
53036         cimg::warn(_cimg_instance
53037                    "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53038                    cimg_instance,
53039                    filename?filename:"(FILE*)");
53040       if (_spectrum>3)
53041         cimg::warn(_cimg_instance
53042                    "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
53043                    cimg_instance,
53044                    filename?filename:"(FILE*)");
53045       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
53046         cimg::warn(_cimg_instance
53047                    "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
53048                    cimg_instance,
53049                    stmin,stmax,filename?filename:"(FILE*)");
53050 
53051       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53052       const T
53053         *ptr_r = data(0,0,0,0),
53054         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
53055         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
53056       const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
53057 
53058       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
53059                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
53060 
53061       switch (_spectrum) {
53062       case 1 : { // Scalar image
53063         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
53064           CImg<ucharT> buf((unsigned int)buf_size);
53065           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53066             const ulongT N = std::min((ulongT)to_write,buf_size);
53067             unsigned char *ptrd = buf._data;
53068             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
53069             cimg::fwrite(buf._data,N,nfile);
53070             to_write-=N;
53071           }
53072         } else { // Binary PGM 16 bits
53073           CImg<ushortT> buf((unsigned int)buf_size);
53074           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53075             const ulongT N = std::min((ulongT)to_write,buf_size);
53076             unsigned short *ptrd = buf._data;
53077             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
53078             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53079             cimg::fwrite(buf._data,N,nfile);
53080             to_write-=N;
53081           }
53082         }
53083       } break;
53084       case 2 : { // RG image
53085         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
53086           CImg<ucharT> buf((unsigned int)buf_size);
53087           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53088             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53089             unsigned char *ptrd = buf._data;
53090             for (ulongT i = N; i>0; --i) {
53091               *(ptrd++) = (unsigned char)*(ptr_r++);
53092               *(ptrd++) = (unsigned char)*(ptr_g++);
53093               *(ptrd++) = 0;
53094             }
53095             cimg::fwrite(buf._data,3*N,nfile);
53096             to_write-=N;
53097           }
53098         } else {             // Binary PPM 16 bits
53099           CImg<ushortT> buf((unsigned int)buf_size);
53100           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53101             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53102             unsigned short *ptrd = buf._data;
53103             for (ulongT i = N; i>0; --i) {
53104               *(ptrd++) = (unsigned short)*(ptr_r++);
53105               *(ptrd++) = (unsigned short)*(ptr_g++);
53106               *(ptrd++) = 0;
53107             }
53108             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53109             cimg::fwrite(buf._data,3*N,nfile);
53110             to_write-=N;
53111           }
53112         }
53113       } break;
53114       default : { // RGB image
53115         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
53116           CImg<ucharT> buf((unsigned int)buf_size);
53117           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53118             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53119             unsigned char *ptrd = buf._data;
53120             for (ulongT i = N; i>0; --i) {
53121               *(ptrd++) = (unsigned char)*(ptr_r++);
53122               *(ptrd++) = (unsigned char)*(ptr_g++);
53123               *(ptrd++) = (unsigned char)*(ptr_b++);
53124             }
53125             cimg::fwrite(buf._data,3*N,nfile);
53126             to_write-=N;
53127           }
53128         } else { // Binary PPM 16 bits
53129           CImg<ushortT> buf((unsigned int)buf_size);
53130           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53131             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53132             unsigned short *ptrd = buf._data;
53133             for (ulongT i = N; i>0; --i) {
53134               *(ptrd++) = (unsigned short)*(ptr_r++);
53135               *(ptrd++) = (unsigned short)*(ptr_g++);
53136               *(ptrd++) = (unsigned short)*(ptr_b++);
53137             }
53138             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53139             cimg::fwrite(buf._data,3*N,nfile);
53140             to_write-=N;
53141           }
53142         }
53143       }
53144       }
53145       if (!file) cimg::fclose(nfile);
53146       return *this;
53147     }
53148 
53149     //! Save image as a PNK file.
53150     /**
53151       \param filename Filename, as a C-string.
53152     **/
53153     const CImg<T>& save_pnk(const char *const filename) const {
53154       return _save_pnk(0,filename);
53155     }
53156 
53157     //! Save image as a PNK file \overloading.
53158     const CImg<T>& save_pnk(std::FILE *const file) const {
53159       return _save_pnk(file,0);
53160     }
53161 
53162     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
53163       if (!file && !filename)
53164         throw CImgArgumentException(_cimg_instance
53165                                     "save_pnk(): Specified filename is (null).",
53166                                     cimg_instance);
53167       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53168       if (_spectrum>1)
53169         cimg::warn(_cimg_instance
53170                    "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
53171                    cimg_instance,
53172                    filename?filename:"(FILE*)");
53173 
53174       const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
53175       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53176       const T *ptr = data(0,0,0,0);
53177 
53178       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file.
53179         _save_pnm(file,filename,0);
53180       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d.
53181         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
53182         CImg<ucharT> buf((unsigned int)buf_size);
53183         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53184           const ulongT N = std::min((ulongT)to_write,buf_size);
53185           unsigned char *ptrd = buf._data;
53186           for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
53187           cimg::fwrite(buf._data,N,nfile);
53188           to_write-=N;
53189         }
53190       } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3d.
53191         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
53192         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
53193         CImg<intT> buf((unsigned int)buf_size);
53194         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53195           const ulongT N = std::min((ulongT)to_write,buf_size);
53196           int *ptrd = buf._data;
53197           for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
53198           cimg::fwrite(buf._data,N,nfile);
53199           to_write-=N;
53200         }
53201       } else { // Save as P9: Binary float-valued 3d.
53202         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
53203         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
53204         CImg<floatT> buf((unsigned int)buf_size);
53205         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53206           const ulongT N = std::min((ulongT)to_write,buf_size);
53207           float *ptrd = buf._data;
53208           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
53209           cimg::fwrite(buf._data,N,nfile);
53210           to_write-=N;
53211         }
53212       }
53213 
53214       if (!file) cimg::fclose(nfile);
53215       return *this;
53216     }
53217 
53218     //! Save image as a PFM file.
53219     /**
53220       \param filename Filename, as a C-string.
53221     **/
53222     const CImg<T>& save_pfm(const char *const filename) const {
53223       get_mirror('y')._save_pfm(0,filename);
53224       return *this;
53225     }
53226 
53227     //! Save image as a PFM file \overloading.
53228     const CImg<T>& save_pfm(std::FILE *const file) const {
53229       get_mirror('y')._save_pfm(file,0);
53230       return *this;
53231     }
53232 
53233     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
53234       if (!file && !filename)
53235         throw CImgArgumentException(_cimg_instance
53236                                     "save_pfm(): Specified filename is (null).",
53237                                     cimg_instance);
53238       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53239       if (_depth>1)
53240         cimg::warn(_cimg_instance
53241                    "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53242                    cimg_instance,
53243                    filename?filename:"(FILE*)");
53244       if (_spectrum>3)
53245         cimg::warn(_cimg_instance
53246                    "save_pfm(): image instance is multispectral, only the three first channels will be saved "
53247                    "in file '%s'.",
53248                    cimg_instance,
53249                    filename?filename:"(FILE*)");
53250 
53251       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53252       const T
53253         *ptr_r = data(0,0,0,0),
53254         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
53255         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
53256       const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
53257 
53258       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
53259                    (_spectrum==1?'f':'F'),_width,_height);
53260 
53261       switch (_spectrum) {
53262       case 1 : { // Scalar image
53263         CImg<floatT> buf(buf_size);
53264         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53265           const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
53266           float *ptrd = buf._data;
53267           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
53268           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53269           cimg::fwrite(buf._data,N,nfile);
53270           to_write-=N;
53271         }
53272       } break;
53273       case 2 : { // RG image
53274         CImg<floatT> buf(buf_size);
53275         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53276           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
53277           float *ptrd = buf._data;
53278           for (ulongT i = N; i>0; --i) {
53279             *(ptrd++) = (float)*(ptr_r++);
53280             *(ptrd++) = (float)*(ptr_g++);
53281             *(ptrd++) = 0;
53282           }
53283           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53284           cimg::fwrite(buf._data,3*N,nfile);
53285           to_write-=N;
53286         }
53287       } break;
53288       default : { // RGB image
53289         CImg<floatT> buf(buf_size);
53290         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53291           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
53292           float *ptrd = buf._data;
53293           for (ulongT i = N; i>0; --i) {
53294             *(ptrd++) = (float)*(ptr_r++);
53295             *(ptrd++) = (float)*(ptr_g++);
53296             *(ptrd++) = (float)*(ptr_b++);
53297           }
53298           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53299           cimg::fwrite(buf._data,3*N,nfile);
53300           to_write-=N;
53301         }
53302       }
53303       }
53304       if (!file) cimg::fclose(nfile);
53305       return *this;
53306     }
53307 
53308     //! Save image as a RGB file.
53309     /**
53310       \param filename Filename, as a C-string.
53311     **/
53312     const CImg<T>& save_rgb(const char *const filename) const {
53313       return _save_rgb(0,filename);
53314     }
53315 
53316     //! Save image as a RGB file \overloading.
53317     const CImg<T>& save_rgb(std::FILE *const file) const {
53318       return _save_rgb(file,0);
53319     }
53320 
53321     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
53322       if (!file && !filename)
53323         throw CImgArgumentException(_cimg_instance
53324                                     "save_rgb(): Specified filename is (null).",
53325                                     cimg_instance);
53326       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53327       if (_spectrum!=3)
53328         cimg::warn(_cimg_instance
53329                    "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
53330                    cimg_instance,
53331                    filename?filename:"(FILE*)");
53332 
53333       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53334       const ulongT wh = (ulongT)_width*_height;
53335       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
53336       const T
53337         *ptr1 = data(0,0,0,0),
53338         *ptr2 = _spectrum>1?data(0,0,0,1):0,
53339         *ptr3 = _spectrum>2?data(0,0,0,2):0;
53340       switch (_spectrum) {
53341       case 1 : { // Scalar image
53342         for (ulongT k = 0; k<wh; ++k) {
53343           const unsigned char val = (unsigned char)*(ptr1++);
53344           *(nbuffer++) = val;
53345           *(nbuffer++) = val;
53346           *(nbuffer++) = val;
53347         }
53348       } break;
53349       case 2 : { // RG image
53350         for (ulongT k = 0; k<wh; ++k) {
53351           *(nbuffer++) = (unsigned char)(*(ptr1++));
53352           *(nbuffer++) = (unsigned char)(*(ptr2++));
53353           *(nbuffer++) = 0;
53354         }
53355       } break;
53356       default : { // RGB image
53357         for (ulongT k = 0; k<wh; ++k) {
53358           *(nbuffer++) = (unsigned char)(*(ptr1++));
53359           *(nbuffer++) = (unsigned char)(*(ptr2++));
53360           *(nbuffer++) = (unsigned char)(*(ptr3++));
53361         }
53362       }
53363       }
53364       cimg::fwrite(buffer,3*wh,nfile);
53365       if (!file) cimg::fclose(nfile);
53366       delete[] buffer;
53367       return *this;
53368     }
53369 
53370     //! Save image as a RGBA file.
53371     /**
53372        \param filename Filename, as a C-string.
53373     **/
53374     const CImg<T>& save_rgba(const char *const filename) const {
53375       return _save_rgba(0,filename);
53376     }
53377 
53378     //! Save image as a RGBA file \overloading.
53379     const CImg<T>& save_rgba(std::FILE *const file) const {
53380       return _save_rgba(file,0);
53381     }
53382 
53383     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
53384       if (!file && !filename)
53385         throw CImgArgumentException(_cimg_instance
53386                                     "save_rgba(): Specified filename is (null).",
53387                                     cimg_instance);
53388       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53389       if (_spectrum!=4)
53390         cimg::warn(_cimg_instance
53391                    "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
53392                    cimg_instance,
53393                    filename?filename:"(FILE*)");
53394 
53395       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53396       const ulongT wh = (ulongT)_width*_height;
53397       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
53398       const T
53399         *ptr1 = data(0,0,0,0),
53400         *ptr2 = _spectrum>1?data(0,0,0,1):0,
53401         *ptr3 = _spectrum>2?data(0,0,0,2):0,
53402         *ptr4 = _spectrum>3?data(0,0,0,3):0;
53403       switch (_spectrum) {
53404       case 1 : { // Scalar images
53405         for (ulongT k = 0; k<wh; ++k) {
53406           const unsigned char val = (unsigned char)*(ptr1++);
53407           *(nbuffer++) = val;
53408           *(nbuffer++) = val;
53409           *(nbuffer++) = val;
53410           *(nbuffer++) = 255;
53411         }
53412       } break;
53413       case 2 : { // RG images
53414         for (ulongT k = 0; k<wh; ++k) {
53415           *(nbuffer++) = (unsigned char)(*(ptr1++));
53416           *(nbuffer++) = (unsigned char)(*(ptr2++));
53417           *(nbuffer++) = 0;
53418           *(nbuffer++) = 255;
53419         }
53420       } break;
53421       case 3 : { // RGB images
53422         for (ulongT k = 0; k<wh; ++k) {
53423           *(nbuffer++) = (unsigned char)(*(ptr1++));
53424           *(nbuffer++) = (unsigned char)(*(ptr2++));
53425           *(nbuffer++) = (unsigned char)(*(ptr3++));
53426           *(nbuffer++) = 255;
53427         }
53428       } break;
53429       default : { // RGBA images
53430         for (ulongT k = 0; k<wh; ++k) {
53431           *(nbuffer++) = (unsigned char)(*(ptr1++));
53432           *(nbuffer++) = (unsigned char)(*(ptr2++));
53433           *(nbuffer++) = (unsigned char)(*(ptr3++));
53434           *(nbuffer++) = (unsigned char)(*(ptr4++));
53435         }
53436       }
53437       }
53438       cimg::fwrite(buffer,4*wh,nfile);
53439       if (!file) cimg::fclose(nfile);
53440       delete[] buffer;
53441       return *this;
53442     }
53443 
53444     //! Save image as a TIFF file.
53445     /**
53446        \param filename Filename, as a C-string.
53447        \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
53448        \param voxel_size Voxel size, to be stored in the filename.
53449        \param description Description, to be stored in the filename.
53450        \param use_bigtiff Allow to save big tiff files (>4Gb).
53451        \note
53452        - libtiff support is enabled by defining the precompilation
53453         directive \c cimg_use_tif.
53454        - When libtiff is enabled, 2D and 3D (multipage) several
53455         channel per pixel are supported for
53456         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
53457        - If \c cimg_use_tif is not defined at compile time the
53458         function uses CImg<T>&save_other(const char*).
53459      **/
53460     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
53461                              const float *const voxel_size=0, const char *const description=0,
53462                              const bool use_bigtiff=true) const {
53463       if (!filename)
53464         throw CImgArgumentException(_cimg_instance
53465                                     "save_tiff(): Specified filename is (null).",
53466                                     cimg_instance);
53467       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53468 
53469 #ifdef cimg_use_tiff
53470       const bool
53471         _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images.
53472       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
53473       if (tif) {
53474         cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
53475         TIFFClose(tif);
53476       } else throw CImgIOException(_cimg_instance
53477                                    "save_tiff(): Failed to open file '%s' for writing.",
53478                                    cimg_instance,
53479                                    filename);
53480       return *this;
53481 #else
53482       cimg::unused(compression_type,voxel_size,description,use_bigtiff);
53483       return save_other(filename);
53484 #endif
53485     }
53486 
53487 #ifdef cimg_use_tiff
53488 
53489 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
53490       const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
53491 
53492     // [internal] Save a plane into a tiff file
53493     template<typename t>
53494     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
53495                               const unsigned int compression_type, const float *const voxel_size,
53496                               const char *const description) const {
53497       if (is_empty() || !tif || pixel_t) return *this;
53498       const char *const filename = TIFFFileName(tif);
53499       uint32 rowsperstrip = (uint32)-1;
53500       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
53501       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
53502       else photometric = PHOTOMETRIC_MINISBLACK;
53503       TIFFSetDirectory(tif,directory);
53504       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
53505       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
53506       if (voxel_size) {
53507         const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
53508         TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
53509         TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.0f/vx);
53510         TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.0f/vy);
53511         CImg<charT> s_description(256);
53512         cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
53513         TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
53514       }
53515       if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
53516       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
53517       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
53518       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
53519       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
53520       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
53521       double valm, valM = max_min(valm);
53522       TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
53523       TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
53524       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
53525       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
53526       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
53527       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
53528                    compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
53529       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
53530       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
53531       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
53532       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
53533 
53534       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
53535       if (buf) {
53536         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
53537           uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
53538           tstrip_t strip = TIFFComputeStrip(tif,row,0);
53539           tsize_t i = 0;
53540           for (unsigned int rr = 0; rr<nrow; ++rr)
53541             for (unsigned int cc = 0; cc<_width; ++cc)
53542               for (unsigned int vv = 0; vv<spp; ++vv)
53543                 buf[i++] = (t)(*this)(cc,row + rr,z,vv);
53544           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
53545             throw CImgIOException(_cimg_instance
53546                                   "save_tiff(): Invalid strip writing when saving file '%s'.",
53547                                   cimg_instance,
53548                                   filename?filename:"(FILE*)");
53549         }
53550         _TIFFfree(buf);
53551       }
53552       TIFFWriteDirectory(tif);
53553       return *this;
53554     }
53555 
53556     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
53557                               const unsigned int compression_type, const float *const voxel_size,
53558                               const char *const description) const {
53559       _cimg_save_tiff("bool",unsigned char,compression_type);
53560       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
53561       _cimg_save_tiff("char",char,compression_type);
53562       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
53563       _cimg_save_tiff("short",short,compression_type);
53564       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
53565       _cimg_save_tiff("int",int,compression_type);
53566       _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
53567       _cimg_save_tiff("int64",int,compression_type);
53568       _cimg_save_tiff("float",float,compression_type);
53569       _cimg_save_tiff("double",float,compression_type);
53570       const char *const filename = TIFFFileName(tif);
53571       throw CImgInstanceException(_cimg_instance
53572                                   "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
53573                                   cimg_instance,
53574                                   pixel_type(),filename?filename:"(FILE*)");
53575       return *this;
53576     }
53577 #endif
53578 
53579     //! Save image as a MINC2 file.
53580     /**
53581        \param filename Filename, as a C-string.
53582        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
53583     **/
53584     const CImg<T>& save_minc2(const char *const filename,
53585                               const char *const imitate_file=0) const {
53586       if (!filename)
53587         throw CImgArgumentException(_cimg_instance
53588                                    "save_minc2(): Specified filename is (null).",
53589                                    cimg_instance);
53590       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53591 
53592 #ifndef cimg_use_minc2
53593      cimg::unused(imitate_file);
53594      return save_other(filename);
53595 #else
53596      minc::minc_1_writer wtr;
53597      if (imitate_file)
53598        wtr.open(filename, imitate_file);
53599      else {
53600        minc::minc_info di;
53601        if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
53602        if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
53603        if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
53604        if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
53605        wtr.open(filename,di,1,NC_FLOAT,0);
53606      }
53607      if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
53608        wtr.setup_write_byte();
53609      else if (cimg::type<T>::string()==cimg::type<int>::string())
53610        wtr.setup_write_int();
53611      else if (cimg::type<T>::string()==cimg::type<double>::string())
53612        wtr.setup_write_double();
53613      else
53614        wtr.setup_write_float();
53615      minc::save_standard_volume(wtr, this->_data);
53616      return *this;
53617 #endif
53618     }
53619 
53620     //! Save image as an ANALYZE7.5 or NIFTI file.
53621     /**
53622       \param filename Filename, as a C-string.
53623       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
53624     **/
53625     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
53626       if (!filename)
53627         throw CImgArgumentException(_cimg_instance
53628                                     "save_analyze(): Specified filename is (null).",
53629                                     cimg_instance);
53630       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53631 
53632       std::FILE *file;
53633       CImg<charT> hname(1024), iname(1024);
53634       const char *const ext = cimg::split_filename(filename);
53635       short datatype = -1;
53636       if (!*ext) {
53637         cimg_snprintf(hname,hname._width,"%s.hdr",filename);
53638         cimg_snprintf(iname,iname._width,"%s.img",filename);
53639       }
53640       if (!cimg::strncasecmp(ext,"hdr",3)) {
53641         std::strcpy(hname,filename);
53642         std::strncpy(iname,filename,iname._width - 1);
53643         cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
53644       }
53645       if (!cimg::strncasecmp(ext,"img",3)) {
53646         std::strcpy(hname,filename);
53647         std::strncpy(iname,filename,iname._width - 1);
53648         cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
53649       }
53650       if (!cimg::strncasecmp(ext,"nii",3)) {
53651         std::strncpy(hname,filename,hname._width - 1); *iname = 0;
53652       }
53653 
53654       CImg<charT> header(*iname?348:352,1,1,1,0);
53655       int *const iheader = (int*)header._data;
53656       *iheader = 348;
53657       std::strcpy(header._data + 4,"CImg");
53658       std::strcpy(header._data + 14," ");
53659       ((short*)&(header[36]))[0] = 4096;
53660       ((char*)&(header[38]))[0] = 114;
53661       ((short*)&(header[40]))[0] = 4;
53662       ((short*)&(header[40]))[1] = (short)_width;
53663       ((short*)&(header[40]))[2] = (short)_height;
53664       ((short*)&(header[40]))[3] = (short)_depth;
53665       ((short*)&(header[40]))[4] = (short)_spectrum;
53666       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
53667       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
53668       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
53669       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
53670       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
53671       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
53672       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
53673       if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
53674       if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
53675       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
53676       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
53677       if (datatype<0)
53678         throw CImgIOException(_cimg_instance
53679                               "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
53680                               cimg_instance,
53681                               pixel_type(),filename);
53682 
53683       ((short*)&(header[70]))[0] = datatype;
53684       ((short*)&(header[72]))[0] = sizeof(T);
53685       ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
53686       ((float*)&(header[112]))[0] = 1;
53687       ((float*)&(header[76]))[0] = 0;
53688       if (voxel_size) {
53689         ((float*)&(header[76]))[1] = voxel_size[0];
53690         ((float*)&(header[76]))[2] = voxel_size[1];
53691         ((float*)&(header[76]))[3] = voxel_size[2];
53692       } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
53693       file = cimg::fopen(hname,"wb");
53694       cimg::fwrite(header._data,header.width(),file);
53695       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
53696       cimg::fwrite(_data,size(),file);
53697       cimg::fclose(file);
53698       return *this;
53699     }
53700 
53701     //! Save image as a .cimg file.
53702     /**
53703       \param filename Filename, as a C-string.
53704       \param is_compressed Tells if the file contains compressed image data.
53705     **/
53706     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
53707       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
53708       return *this;
53709     }
53710 
53711     //! Save image as a .cimg file \overloading.
53712     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
53713       CImgList<T>(*this,true).save_cimg(file,is_compressed);
53714       return *this;
53715     }
53716 
53717     //! Save image as a sub-image into an existing .cimg file.
53718     /**
53719       \param filename Filename, as a C-string.
53720       \param n0 Index of the image inside the file.
53721       \param x0 X-coordinate of the sub-image location.
53722       \param y0 Y-coordinate of the sub-image location.
53723       \param z0 Z-coordinate of the sub-image location.
53724       \param c0 C-coordinate of the sub-image location.
53725     **/
53726     const CImg<T>& save_cimg(const char *const filename,
53727                              const unsigned int n0,
53728                              const unsigned int x0, const unsigned int y0,
53729                              const unsigned int z0, const unsigned int c0) const {
53730       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
53731       return *this;
53732     }
53733 
53734     //! Save image as a sub-image into an existing .cimg file \overloading.
53735     const CImg<T>& save_cimg(std::FILE *const file,
53736 			     const unsigned int n0,
53737 			     const unsigned int x0, const unsigned int y0,
53738 			     const unsigned int z0, const unsigned int c0) const {
53739       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
53740       return *this;
53741     }
53742 
53743     //! Save blank image as a .cimg file.
53744     /**
53745         \param filename Filename, as a C-string.
53746         \param dx Width of the image.
53747         \param dy Height of the image.
53748         \param dz Depth of the image.
53749         \param dc Number of channels of the image.
53750         \note
53751         - All pixel values of the saved image are set to \c 0.
53752         - Use this method to save large images without having to instanciate and allocate them.
53753     **/
53754     static void save_empty_cimg(const char *const filename,
53755                                 const unsigned int dx, const unsigned int dy=1,
53756                                 const unsigned int dz=1, const unsigned int dc=1) {
53757       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
53758     }
53759 
53760     //! Save blank image as a .cimg file \overloading.
53761     /**
53762        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
53763        with a file stream argument instead of a filename string.
53764     **/
53765     static void save_empty_cimg(std::FILE *const file,
53766                                 const unsigned int dx, const unsigned int dy=1,
53767                                 const unsigned int dz=1, const unsigned int dc=1) {
53768       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
53769     }
53770 
53771     //! Save image as an INRIMAGE-4 file.
53772     /**
53773       \param filename Filename, as a C-string.
53774       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
53775     **/
53776     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
53777       return _save_inr(0,filename,voxel_size);
53778     }
53779 
53780     //! Save image as an INRIMAGE-4 file \overloading.
53781     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
53782       return _save_inr(file,0,voxel_size);
53783     }
53784 
53785     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
53786       if (!file && !filename)
53787         throw CImgArgumentException(_cimg_instance
53788                                     "save_inr(): Specified filename is (null).",
53789                                     cimg_instance);
53790       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53791 
53792       int inrpixsize = -1;
53793       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
53794       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
53795         inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
53796       }
53797       if (!cimg::strcasecmp(pixel_type(),"char")) {
53798         inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
53799       }
53800       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
53801         inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
53802       }
53803       if (!cimg::strcasecmp(pixel_type(),"short")) {
53804         inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
53805       }
53806       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
53807         inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
53808       }
53809       if (!cimg::strcasecmp(pixel_type(),"int")) {
53810         inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
53811       }
53812       if (!cimg::strcasecmp(pixel_type(),"float")) {
53813         inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
53814       }
53815       if (!cimg::strcasecmp(pixel_type(),"double")) {
53816         inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
53817       }
53818       if (inrpixsize<=0)
53819         throw CImgIOException(_cimg_instance
53820                               "save_inr(): Unsupported pixel type '%s' for file '%s'",
53821                               cimg_instance,
53822                               pixel_type(),filename?filename:"(FILE*)");
53823 
53824       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53825       CImg<charT> header(257);
53826       int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
53827                               _width,_height,_depth,_spectrum);
53828       if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
53829                                         voxel_size[0],voxel_size[1],voxel_size[2]);
53830       err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
53831       std::memset(header._data + err,'\n',252 - err);
53832       std::memcpy(header._data + 252,"##}\n",4);
53833       cimg::fwrite(header._data,256,nfile);
53834       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
53835       if (!file) cimg::fclose(nfile);
53836       return *this;
53837     }
53838 
53839     //! Save image as an OpenEXR file.
53840     /**
53841        \param filename Filename, as a C-string.
53842        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
53843     **/
53844     const CImg<T>& save_exr(const char *const filename) const {
53845       if (!filename)
53846         throw CImgArgumentException(_cimg_instance
53847                                     "save_exr(): Specified filename is (null).",
53848                                     cimg_instance);
53849       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53850       if (_depth>1)
53851         cimg::warn(_cimg_instance
53852                    "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53853                    cimg_instance,
53854                    filename);
53855 
53856 #ifndef cimg_use_openexr
53857       return save_other(filename);
53858 #else
53859       Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
53860       switch (_spectrum) {
53861       case 1 : { // Grayscale image.
53862         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53863           rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
53864           rgba.a = (half)1;
53865           *(ptrd++) = rgba;
53866         }
53867       } break;
53868       case 2 : { // RG image.
53869         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
53870                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
53871           rgba.r = (half)(*(ptr_r++));
53872           rgba.g = (half)(*(ptr_g++));
53873           rgba.b = (half)0;
53874           rgba.a = (half)1;
53875           *(ptrd++) = rgba;
53876         }
53877       } break;
53878       case 3 : { // RGB image.
53879         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
53880                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53881           rgba.r = (half)(*(ptr_r++));
53882           rgba.g = (half)(*(ptr_g++));
53883           rgba.b = (half)(*(ptr_b++));
53884           rgba.a = (half)1;
53885           *(ptrd++) = rgba;
53886         }
53887       } break;
53888       default : { // RGBA image.
53889         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),
53890                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53891           rgba.r = (half)(*(ptr_r++));
53892           rgba.g = (half)(*(ptr_g++));
53893           rgba.b = (half)(*(ptr_b++));
53894           rgba.a = (half)(*(ptr_a++));
53895           *(ptrd++) = rgba;
53896         }
53897       } break;
53898       }
53899       Imf::RgbaOutputFile outFile(filename,_width,_height,
53900                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
53901                                   Imf::WRITE_RGB:Imf::WRITE_RGBA);
53902       outFile.setFrameBuffer(ptrd0,1,_width);
53903       outFile.writePixels(_height);
53904       delete[] ptrd0;
53905       return *this;
53906 #endif
53907     }
53908 
53909     //! Save image as a Pandore-5 file.
53910     /**
53911        \param filename Filename, as a C-string.
53912        \param colorspace Colorspace data field in output file
53913        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
53914        for more information).
53915     **/
53916     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
53917       return _save_pandore(0,filename,colorspace);
53918     }
53919 
53920     //! Save image as a Pandore-5 file \overloading.
53921     /**
53922         Same as save_pandore(const char *,unsigned int) const
53923         with a file stream argument instead of a filename string.
53924     **/
53925     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
53926       return _save_pandore(file,0,colorspace);
53927     }
53928 
53929     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
53930       unsigned int nbdims = 0;
53931       if (id==2 || id==3 || id==4) {
53932         dims[0] = 1; dims[1] = _width; nbdims = 2;
53933       }
53934       if (id==5 || id==6 || id==7) {
53935         dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
53936       }
53937       if (id==8 || id==9 || id==10) {
53938         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
53939       }
53940       if (id==16 || id==17 || id==18) {
53941         dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
53942       }
53943       if (id==19 || id==20 || id==21) {
53944         dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
53945       }
53946       if (id==22 || id==23 || id==25) {
53947         dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
53948       }
53949       if (id==26 || id==27 || id==29) {
53950         dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
53951       }
53952       if (id==30 || id==31 || id==33) {
53953         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
53954       }
53955       return nbdims;
53956     }
53957 
53958     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
53959                                  const unsigned int colorspace) const {
53960 
53961 #define __cimg_save_pandore_case(dtype) \
53962        dtype *buffer = new dtype[size()]; \
53963        const T *ptrs = _data; \
53964        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
53965        buffer-=size(); \
53966        cimg::fwrite(buffer,size(),nfile); \
53967        delete[] buffer
53968 
53969 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
53970       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
53971           (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
53972 	unsigned int *iheader = (unsigned int*)(header + 12); \
53973 	nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
53974 	cimg::fwrite(header,36,nfile); \
53975         if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
53976           for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
53977         else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
53978           for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
53979         else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
53980           for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
53981         else throw CImgIOException(_cimg_instance \
53982                                    "save_pandore(): Unsupported datatype for file '%s'.",\
53983                                    cimg_instance, \
53984                                    filename?filename:"(FILE*)"); \
53985 	if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
53986           __cimg_save_pandore_case(unsigned char); \
53987 	} else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
53988           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
53989           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
53990           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
53991           else throw CImgIOException(_cimg_instance \
53992                                      "save_pandore(): Unsupported datatype for file '%s'.",\
53993                                      cimg_instance, \
53994                                      filename?filename:"(FILE*)"); \
53995 	} else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
53996           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
53997           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
53998           else throw CImgIOException(_cimg_instance \
53999                                      "save_pandore(): Unsupported datatype for file '%s'.",\
54000                                      cimg_instance, \
54001                                      filename?filename:"(FILE*)"); \
54002         } \
54003 	saved = true; \
54004       }
54005 
54006       if (!file && !filename)
54007         throw CImgArgumentException(_cimg_instance
54008                                     "save_pandore(): Specified filename is (null).",
54009                                     cimg_instance);
54010       if (is_empty()) { cimg::fempty(file,filename); return *this; }
54011 
54012       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
54013       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
54014                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,
54015                                    'N','o',' ','d','a','t','e',0,0,0,0 };
54016       unsigned int nbdims, dims[5] = { 0 };
54017       bool saved = false;
54018       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
54019       _cimg_save_pandore_case(1,1,1,"char",3);
54020       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
54021       _cimg_save_pandore_case(1,1,1,"short",3);
54022       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
54023       _cimg_save_pandore_case(1,1,1,"int",3);
54024       _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
54025       _cimg_save_pandore_case(1,1,1,"int64",3);
54026       _cimg_save_pandore_case(1,1,1,"float",4);
54027       _cimg_save_pandore_case(1,1,1,"double",4);
54028 
54029       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
54030       _cimg_save_pandore_case(0,1,1,"char",6);
54031       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
54032       _cimg_save_pandore_case(0,1,1,"short",6);
54033       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
54034       _cimg_save_pandore_case(0,1,1,"int",6);
54035       _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
54036       _cimg_save_pandore_case(0,1,1,"int64",6);
54037       _cimg_save_pandore_case(0,1,1,"float",7);
54038       _cimg_save_pandore_case(0,1,1,"double",7);
54039 
54040       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
54041       _cimg_save_pandore_case(0,0,1,"char",9);
54042       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
54043       _cimg_save_pandore_case(0,0,1,"short",9);
54044       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
54045       _cimg_save_pandore_case(0,0,1,"int",9);
54046       _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
54047       _cimg_save_pandore_case(0,0,1,"int64",9);
54048       _cimg_save_pandore_case(0,0,1,"float",10);
54049       _cimg_save_pandore_case(0,0,1,"double",10);
54050 
54051       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
54052       _cimg_save_pandore_case(0,1,3,"char",17);
54053       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
54054       _cimg_save_pandore_case(0,1,3,"short",17);
54055       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
54056       _cimg_save_pandore_case(0,1,3,"int",17);
54057       _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
54058       _cimg_save_pandore_case(0,1,3,"int64",17);
54059       _cimg_save_pandore_case(0,1,3,"float",18);
54060       _cimg_save_pandore_case(0,1,3,"double",18);
54061 
54062       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
54063       _cimg_save_pandore_case(0,0,3,"char",20);
54064       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
54065       _cimg_save_pandore_case(0,0,3,"short",20);
54066       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
54067       _cimg_save_pandore_case(0,0,3,"int",20);
54068       _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
54069       _cimg_save_pandore_case(0,0,3,"int64",20);
54070       _cimg_save_pandore_case(0,0,3,"float",21);
54071       _cimg_save_pandore_case(0,0,3,"double",21);
54072 
54073       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
54074       _cimg_save_pandore_case(1,1,0,"char",23);
54075       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
54076       _cimg_save_pandore_case(1,1,0,"short",23);
54077       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
54078       _cimg_save_pandore_case(1,1,0,"int",23);
54079       _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
54080       _cimg_save_pandore_case(1,1,0,"int64",23);
54081       _cimg_save_pandore_case(1,1,0,"float",25);
54082       _cimg_save_pandore_case(1,1,0,"double",25);
54083 
54084       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
54085       _cimg_save_pandore_case(0,1,0,"char",27);
54086       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
54087       _cimg_save_pandore_case(0,1,0,"short",27);
54088       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
54089       _cimg_save_pandore_case(0,1,0,"int",27);
54090       _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
54091       _cimg_save_pandore_case(0,1,0,"int64",27);
54092       _cimg_save_pandore_case(0,1,0,"float",29);
54093       _cimg_save_pandore_case(0,1,0,"double",29);
54094 
54095       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
54096       _cimg_save_pandore_case(0,0,0,"char",31);
54097       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
54098       _cimg_save_pandore_case(0,0,0,"short",31);
54099       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
54100       _cimg_save_pandore_case(0,0,0,"int",31);
54101       _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
54102       _cimg_save_pandore_case(0,0,0,"int64",31);
54103       _cimg_save_pandore_case(0,0,0,"float",33);
54104       _cimg_save_pandore_case(0,0,0,"double",33);
54105 
54106       if (!file) cimg::fclose(nfile);
54107       return *this;
54108     }
54109 
54110     //! Save image as a raw data file.
54111     /**
54112        \param filename Filename, as a C-string.
54113        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
54114        \note The .raw format does not store the image dimensions in the output file,
54115        so you have to keep track of them somewhere to be able to read the file correctly afterwards.
54116     **/
54117     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
54118       return _save_raw(0,filename,is_multiplexed);
54119     }
54120 
54121     //! Save image as a raw data file \overloading.
54122     /**
54123        Same as save_raw(const char *,bool) const
54124        with a file stream argument instead of a filename string.
54125     **/
54126     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
54127       return _save_raw(file,0,is_multiplexed);
54128     }
54129 
54130     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
54131       if (!file && !filename)
54132         throw CImgArgumentException(_cimg_instance
54133                                     "save_raw(): Specified filename is (null).",
54134                                     cimg_instance);
54135       if (is_empty()) { cimg::fempty(file,filename); return *this; }
54136 
54137       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
54138       if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
54139       else {
54140         CImg<T> buf(_spectrum);
54141         cimg_forXYZ(*this,x,y,z) {
54142           cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
54143           cimg::fwrite(buf._data,_spectrum,nfile);
54144         }
54145       }
54146       if (!file) cimg::fclose(nfile);
54147       return *this;
54148     }
54149 
54150     //! Save image as a .yuv video file.
54151     /**
54152        \param filename Filename, as a C-string.
54153        \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
54154        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
54155        \note Each slice of the instance image is considered to be a single frame of the output video file.
54156     **/
54157     const CImg<T>& save_yuv(const char *const filename,
54158                             const unsigned int chroma_subsampling=444,
54159                             const bool is_rgb=true) const {
54160       get_split('z').save_yuv(filename,chroma_subsampling,is_rgb);
54161       return *this;
54162     }
54163 
54164     //! Save image as a .yuv video file \overloading.
54165     /**
54166        Same as save_yuv(const char*,const unsigned int,const bool) const
54167        with a file stream argument instead of a filename string.
54168     **/
54169     const CImg<T>& save_yuv(std::FILE *const file,
54170                             const unsigned int chroma_subsampling=444,
54171                             const bool is_rgb=true) const {
54172       get_split('z').save_yuv(file,chroma_subsampling,is_rgb);
54173       return *this;
54174     }
54175 
54176     //! Save 3d object as an Object File Format (.off) file.
54177     /**
54178        \param filename Filename, as a C-string.
54179        \param primitives List of 3d object primitives.
54180        \param colors List of 3d object colors.
54181        \note
54182        - Instance image contains the vertices data of the 3d object.
54183        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
54184        Such primitives will be lost or simplified during file saving.
54185        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
54186     **/
54187     template<typename tf, typename tc>
54188     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54189                             const char *const filename) const {
54190       return _save_off(primitives,colors,0,filename);
54191     }
54192 
54193     //! Save 3d object as an Object File Format (.off) file \overloading.
54194     /**
54195        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
54196        with a file stream argument instead of a filename string.
54197     **/
54198     template<typename tf, typename tc>
54199     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54200                             std::FILE *const file) const {
54201       return _save_off(primitives,colors,file,0);
54202     }
54203 
54204     template<typename tf, typename tc>
54205     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54206                              std::FILE *const file, const char *const filename) const {
54207       if (!file && !filename)
54208         throw CImgArgumentException(_cimg_instance
54209                                     "save_off(): Specified filename is (null).",
54210                                     cimg_instance);
54211       if (is_empty())
54212         throw CImgInstanceException(_cimg_instance
54213                                     "save_off(): Empty instance, for file '%s'.",
54214                                     cimg_instance,
54215                                     filename?filename:"(FILE*)");
54216 
54217       CImgList<T> opacities;
54218       CImg<charT> error_message(1024);
54219       if (!is_object3d(primitives,colors,opacities,true,error_message))
54220         throw CImgInstanceException(_cimg_instance
54221                                     "save_off(): Invalid specified 3d object, for file '%s' (%s).",
54222                                     cimg_instance,
54223                                     filename?filename:"(FILE*)",error_message.data());
54224 
54225       const CImg<tc> default_color(1,3,1,1,200);
54226       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
54227       unsigned int supported_primitives = 0;
54228       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
54229       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
54230       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
54231                                       (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
54232       cimglist_for(primitives,l) {
54233         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
54234         const unsigned int psiz = primitives[l].size(), csiz = color.size();
54235         const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
54236         switch (psiz) {
54237         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
54238                               (unsigned int)primitives(l,0),r,g,b); break;
54239         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
54240                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
54241         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
54242                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
54243                               (unsigned int)primitives(l,1),r,g,b); break;
54244         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
54245                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
54246                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
54247         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
54248                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
54249         case 6 : {
54250           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
54251           const float
54252             rt = color.atXY(xt,yt,0)/255.0f,
54253             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54254             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54255           std::fprintf(nfile,"2 %u %u %f %f %f\n",
54256                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
54257         } break;
54258         case 9 : {
54259           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
54260           const float
54261             rt = color.atXY(xt,yt,0)/255.0f,
54262             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54263             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54264           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
54265                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
54266                        (unsigned int)primitives(l,1),rt,gt,bt);
54267         } break;
54268         case 12 : {
54269           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
54270           const float
54271             rt = color.atXY(xt,yt,0)/255.0f,
54272             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54273             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54274           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
54275                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
54276                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
54277         } break;
54278         }
54279       }
54280       if (!file) cimg::fclose(nfile);
54281       return *this;
54282     }
54283 
54284     //! Save volumetric image as a video, using the OpenCV library.
54285     /**
54286       \param filename Filename to write data to.
54287       \param fps Number of frames per second.
54288       \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
54289       \param keep_open Tells if the video writer associated to the specified filename
54290         must be kept open or not (to allow frames to be added in the same file afterwards).
54291     **/
54292     const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
54293                               const char *codec=0, const bool keep_open=false) const {
54294       if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
54295       CImgList<T> list;
54296       get_split('z').move_to(list);
54297       list.save_video(filename,fps,codec,keep_open);
54298       return *this;
54299     }
54300 
54301     //! Save volumetric image as a video, using ffmpeg external binary.
54302     /**
54303        \param filename Filename, as a C-string.
54304        \param fps Video framerate.
54305        \param codec Video codec, as a C-string.
54306        \param bitrate Video bitrate.
54307        \note
54308        - Each slice of the instance image is considered to be a single frame of the output video file.
54309        - This method uses \c ffmpeg, an external executable binary provided by
54310          <a href="http://www.ffmpeg.org">FFmpeg</a>.
54311        It must be installed for the method to succeed.
54312     **/
54313     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
54314                                         const char *const codec=0, const unsigned int bitrate=2048) const {
54315       if (!filename)
54316         throw CImgArgumentException(_cimg_instance
54317                                     "save_ffmpeg_external(): Specified filename is (null).",
54318                                     cimg_instance);
54319       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54320 
54321       CImgList<T> list;
54322       get_split('z').move_to(list);
54323       list.save_ffmpeg_external(filename,fps,codec,bitrate);
54324       return *this;
54325     }
54326 
54327     //! Save image using gzip external binary.
54328     /**
54329        \param filename Filename, as a C-string.
54330        \note This method uses \c gzip, an external executable binary provided by
54331          <a href="//http://www.gzip.org">gzip</a>.
54332        It must be installed for the method to succeed.
54333     **/
54334     const CImg<T>& save_gzip_external(const char *const filename) const {
54335       if (!filename)
54336         throw CImgArgumentException(_cimg_instance
54337                                     "save_gzip_external(): Specified filename is (null).",
54338                                     cimg_instance);
54339       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54340 
54341       CImg<charT> command(1024), filename_tmp(256), body(256);
54342       const char
54343         *ext = cimg::split_filename(filename,body),
54344         *ext2 = cimg::split_filename(body,0);
54345       std::FILE *file;
54346       do {
54347         if (!cimg::strcasecmp(ext,"gz")) {
54348           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54349                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
54350           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
54351                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54352         } else {
54353           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54354                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
54355           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
54356                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54357         }
54358         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54359       } while (file);
54360       save(filename_tmp);
54361       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
54362                     cimg::gzip_path(),
54363                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54364                     CImg<charT>::string(filename)._system_strescape().data());
54365       cimg::system(command);
54366       file = std_fopen(filename,"rb");
54367       if (!file)
54368         throw CImgIOException(_cimg_instance
54369                               "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
54370                               cimg_instance,
54371                               filename);
54372 
54373       else cimg::fclose(file);
54374       std::remove(filename_tmp);
54375       return *this;
54376     }
54377 
54378     //! Save image using GraphicsMagick's external binary.
54379     /**
54380        \param filename Filename, as a C-string.
54381        \param quality Image quality (expressed in percent), when the file format supports it.
54382        \note This method uses \c gm, an external executable binary provided by
54383          <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
54384        It must be installed for the method to succeed.
54385     **/
54386     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
54387       if (!filename)
54388         throw CImgArgumentException(_cimg_instance
54389                                     "save_graphicsmagick_external(): Specified filename is (null).",
54390                                     cimg_instance);
54391       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54392       if (_depth>1)
54393         cimg::warn(_cimg_instance
54394                    "save_other(): File '%s', saving a volumetric image with an external call to "
54395                    "GraphicsMagick only writes the first image slice.",
54396                    cimg_instance,filename);
54397 
54398 #ifdef cimg_use_png
54399 #define _cimg_sge_ext1 "png"
54400 #define _cimg_sge_ext2 "png"
54401 #else
54402 #define _cimg_sge_ext1 "pgm"
54403 #define _cimg_sge_ext2 "ppm"
54404 #endif
54405       CImg<charT> command(1024), filename_tmp(256);
54406       std::FILE *file;
54407       do {
54408         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54409                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
54410                       _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2);
54411         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54412       } while (file);
54413 #ifdef cimg_use_png
54414       save_png(filename_tmp);
54415 #else
54416       save_pnm(filename_tmp);
54417 #endif
54418       cimg_snprintf(command,command._width,"%s convert -quality %u \"%s\" \"%s\"",
54419                     cimg::graphicsmagick_path(),quality,
54420                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54421                     CImg<charT>::string(filename)._system_strescape().data());
54422       cimg::system(command);
54423       file = std_fopen(filename,"rb");
54424       if (!file)
54425         throw CImgIOException(_cimg_instance
54426                               "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
54427                               cimg_instance,
54428                               filename);
54429 
54430       if (file) cimg::fclose(file);
54431       std::remove(filename_tmp);
54432       return *this;
54433     }
54434 
54435     //! Save image using ImageMagick's external binary.
54436     /**
54437        \param filename Filename, as a C-string.
54438        \param quality Image quality (expressed in percent), when the file format supports it.
54439        \note This method uses \c convert, an external executable binary provided by
54440        <a href="http://www.imagemagick.org">ImageMagick</a>.
54441        It must be installed for the method to succeed.
54442     **/
54443     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
54444       if (!filename)
54445         throw CImgArgumentException(_cimg_instance
54446                                     "save_imagemagick_external(): Specified filename is (null).",
54447                                     cimg_instance);
54448       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54449       if (_depth>1)
54450         cimg::warn(_cimg_instance
54451                    "save_other(): File '%s', saving a volumetric image with an external call to "
54452                    "ImageMagick only writes the first image slice.",
54453                    cimg_instance,filename);
54454 #ifdef cimg_use_png
54455 #define _cimg_sie_ext1 "png"
54456 #define _cimg_sie_ext2 "png"
54457 #else
54458 #define _cimg_sie_ext1 "pgm"
54459 #define _cimg_sie_ext2 "ppm"
54460 #endif
54461       CImg<charT> command(1024), filename_tmp(256);
54462       std::FILE *file;
54463       do {
54464         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
54465                       cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2);
54466         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54467       } while (file);
54468 #ifdef cimg_use_png
54469       save_png(filename_tmp);
54470 #else
54471       save_pnm(filename_tmp);
54472 #endif
54473       cimg_snprintf(command,command._width,"%s -quality %u \"%s\" \"%s\"",
54474                     cimg::imagemagick_path(),quality,
54475                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54476                     CImg<charT>::string(filename)._system_strescape().data());
54477       cimg::system(command);
54478       file = std_fopen(filename,"rb");
54479       if (!file)
54480         throw CImgIOException(_cimg_instance
54481                               "save_imagemagick_external(): Failed to save file '%s' with "
54482                               "external command 'magick/convert'.",
54483                               cimg_instance,
54484                               filename);
54485 
54486       if (file) cimg::fclose(file);
54487       std::remove(filename_tmp);
54488       return *this;
54489     }
54490 
54491     //! Save image as a Dicom file.
54492     /**
54493        \param filename Filename, as a C-string.
54494        \note This method uses \c medcon, an external executable binary provided by
54495          <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
54496        It must be installed for the method to succeed.
54497     **/
54498     const CImg<T>& save_medcon_external(const char *const filename) const {
54499       if (!filename)
54500         throw CImgArgumentException(_cimg_instance
54501                                     "save_medcon_external(): Specified filename is (null).",
54502                                     cimg_instance);
54503       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54504 
54505       CImg<charT> command(1024), filename_tmp(256), body(256);
54506       std::FILE *file;
54507       do {
54508         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
54509         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54510       } while (file);
54511       save_analyze(filename_tmp);
54512       cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"",
54513                     cimg::medcon_path(),
54514                     CImg<charT>::string(filename)._system_strescape().data(),
54515                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54516       cimg::system(command);
54517       std::remove(filename_tmp);
54518       cimg::split_filename(filename_tmp,body);
54519       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
54520       std::remove(filename_tmp);
54521 
54522       file = std_fopen(filename,"rb");
54523       if (!file) {
54524         cimg_snprintf(command,command._width,"m000-%s",filename);
54525         file = std_fopen(command,"rb");
54526         if (!file) {
54527           cimg::fclose(cimg::fopen(filename,"r"));
54528           throw CImgIOException(_cimg_instance
54529                                 "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
54530                                 cimg_instance,
54531                                 filename);
54532         }
54533       }
54534       cimg::fclose(file);
54535       std::rename(command,filename);
54536       return *this;
54537     }
54538 
54539     // Save image for non natively supported formats.
54540     /**
54541        \param filename Filename, as a C-string.
54542        \param quality Image quality (expressed in percent), when the file format supports it.
54543        \note
54544        - The filename extension tells about the desired file format.
54545        - This method tries to save the instance image as a file, using external tools from
54546        <a href="http://www.imagemagick.org">ImageMagick</a> or
54547        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
54548          At least one of these tool must be installed for the method to succeed.
54549        - It is recommended to use the generic method save(const char*, int) const instead,
54550          as it can handle some file formats natively.
54551     **/
54552     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
54553       if (!filename)
54554         throw CImgArgumentException(_cimg_instance
54555                                     "save_other(): Specified filename is (null).",
54556                                     cimg_instance);
54557       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54558       if (_depth>1)
54559         cimg::warn(_cimg_instance
54560                    "save_other(): File '%s', saving a volumetric image with an external call to "
54561                    "ImageMagick or GraphicsMagick only writes the first image slice.",
54562                    cimg_instance,filename);
54563 
54564       const unsigned int omode = cimg::exception_mode();
54565       bool is_saved = true;
54566       cimg::exception_mode(0);
54567       try { save_magick(filename); }
54568       catch (CImgException&) {
54569         try { save_imagemagick_external(filename,quality); }
54570         catch (CImgException&) {
54571           try { save_graphicsmagick_external(filename,quality); }
54572           catch (CImgException&) {
54573             is_saved = false;
54574           }
54575         }
54576       }
54577       cimg::exception_mode(omode);
54578       if (!is_saved)
54579         throw CImgIOException(_cimg_instance
54580                               "save_other(): Failed to save file '%s'. Format is not natively supported, "
54581                               "and no external commands succeeded.",
54582                               cimg_instance,
54583                               filename);
54584       return *this;
54585     }
54586 
54587     //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
54588     /**
54589        \param is_compressed tells if zlib compression must be used for serialization
54590        (this requires 'cimg_use_zlib' been enabled).
54591     **/
54592     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
54593       return CImgList<T>(*this,true).get_serialize(is_compressed);
54594     }
54595 
54596     // [internal] Return a 40x38 color logo of a 'danger' item.
54597     static CImg<T> _logo40x38() {
54598       CImg<T> res(40,38,1,3);
54599       const unsigned char *ptrs = cimg::logo40x38;
54600       T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
54601       for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
54602         const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
54603         for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
54604       }
54605       return res;
54606     }
54607 
54608     //@}
54609   };
54610 
54611   /*
54612    #-----------------------------------------
54613    #
54614    #
54615    #
54616    # Definition of the CImgList<T> structure
54617    #
54618    #
54619    #
54620    #------------------------------------------
54621    */
54622   //! Represent a list of images CImg<T>.
54623   template<typename T>
54624   struct CImgList {
54625     unsigned int _width, _allocated_width;
54626     CImg<T> *_data;
54627 
54628     //! Simple iterator type, to loop through each image of a list.
54629     /**
54630        \note
54631        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
54632        - You may use it like this:
54633        \code
54634        CImgList<> list;   // Assuming this image list is not empty.
54635        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
54636        \endcode
54637        - Using the loop macro \c cimglist_for is another (more concise) alternative:
54638        \code
54639        cimglist_for(list,l) list[l].mirror('x');
54640        \endcode
54641     **/
54642     typedef CImg<T>* iterator;
54643 
54644     //! Simple const iterator type, to loop through each image of a \c const list instance.
54645     /**
54646        \note
54647        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
54648        - Similar to CImgList<T>::iterator, but for constant list instances.
54649     **/
54650     typedef const CImg<T>* const_iterator;
54651 
54652     //! Pixel value type.
54653     /**
54654        Refer to the pixels value type of the images in the list.
54655        \note
54656        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
54657          It is then similar to CImg<T>::value_type.
54658        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
54659          compatibility with STL naming conventions.
54660     **/
54661     typedef T value_type;
54662 
54663     // Define common types related to template type T.
54664     typedef typename cimg::superset<T,bool>::type Tbool;
54665     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
54666     typedef typename cimg::superset<T,char>::type Tchar;
54667     typedef typename cimg::superset<T,unsigned short>::type Tushort;
54668     typedef typename cimg::superset<T,short>::type Tshort;
54669     typedef typename cimg::superset<T,unsigned int>::type Tuint;
54670     typedef typename cimg::superset<T,int>::type Tint;
54671     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
54672     typedef typename cimg::superset<T,cimg_long>::type Tlong;
54673     typedef typename cimg::superset<T,float>::type Tfloat;
54674     typedef typename cimg::superset<T,double>::type Tdouble;
54675     typedef typename cimg::last<T,bool>::type boolT;
54676     typedef typename cimg::last<T,unsigned char>::type ucharT;
54677     typedef typename cimg::last<T,char>::type charT;
54678     typedef typename cimg::last<T,unsigned short>::type ushortT;
54679     typedef typename cimg::last<T,short>::type shortT;
54680     typedef typename cimg::last<T,unsigned int>::type uintT;
54681     typedef typename cimg::last<T,int>::type intT;
54682     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
54683     typedef typename cimg::last<T,cimg_long>::type longT;
54684     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
54685     typedef typename cimg::last<T,cimg_int64>::type int64T;
54686     typedef typename cimg::last<T,float>::type floatT;
54687     typedef typename cimg::last<T,double>::type doubleT;
54688 
54689     //@}
54690     //---------------------------
54691     //
54692     //! \name Plugins
54693     //@{
54694     //---------------------------
54695 #ifdef cimglist_plugin
54696 #include cimglist_plugin
54697 #endif
54698 #ifdef cimglist_plugin1
54699 #include cimglist_plugin1
54700 #endif
54701 #ifdef cimglist_plugin2
54702 #include cimglist_plugin2
54703 #endif
54704 #ifdef cimglist_plugin3
54705 #include cimglist_plugin3
54706 #endif
54707 #ifdef cimglist_plugin4
54708 #include cimglist_plugin4
54709 #endif
54710 #ifdef cimglist_plugin5
54711 #include cimglist_plugin5
54712 #endif
54713 #ifdef cimglist_plugin6
54714 #include cimglist_plugin6
54715 #endif
54716 #ifdef cimglist_plugin7
54717 #include cimglist_plugin7
54718 #endif
54719 #ifdef cimglist_plugin8
54720 #include cimglist_plugin8
54721 #endif
54722 
54723     //@}
54724     //--------------------------------------------------------
54725     //
54726     //! \name Constructors / Destructor / Instance Management
54727     //@{
54728     //--------------------------------------------------------
54729 
54730     //! Destructor.
54731     /**
54732        Destroy current list instance.
54733        \note
54734        - Any allocated buffer is deallocated.
54735        - Destroying an empty list does nothing actually.
54736      **/
54737     ~CImgList() {
54738       delete[] _data;
54739     }
54740 
54741     //! Default constructor.
54742     /**
54743        Construct a new empty list instance.
54744        \note
54745        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
54746          image buffer pointer data().
54747        - An empty list may be reassigned afterwards, with the family of the assign() methods.
54748          In all cases, the type of pixels stays \c T.
54749      **/
54750     CImgList():
54751       _width(0),_allocated_width(0),_data(0) {}
54752 
54753     //! Construct list containing empty images.
54754     /**
54755        \param n Number of empty images.
54756        \note Useful when you know by advance the number of images you want to manage, as
54757        it will allocate the right amount of memory for the list, without needs for reallocation
54758        (that may occur when starting from an empty list and inserting several images in it).
54759     **/
54760     explicit CImgList(const unsigned int n):_width(n) {
54761       if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
54762       else { _allocated_width = 0; _data = 0; }
54763     }
54764 
54765     //! Construct list containing images of specified size.
54766     /**
54767        \param n Number of images.
54768        \param width Width of images.
54769        \param height Height of images.
54770        \param depth Depth of images.
54771        \param spectrum Number of channels of images.
54772        \note Pixel values are not initialized and may probably contain garbage.
54773     **/
54774     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
54775              const unsigned int depth=1, const unsigned int spectrum=1):
54776       _width(0),_allocated_width(0),_data(0) {
54777       assign(n);
54778       cimglist_apply(*this,assign)(width,height,depth,spectrum);
54779     }
54780 
54781     //! Construct list containing images of specified size, and initialize pixel values.
54782     /**
54783        \param n Number of images.
54784        \param width Width of images.
54785        \param height Height of images.
54786        \param depth Depth of images.
54787        \param spectrum Number of channels of images.
54788        \param val Initialization value for images pixels.
54789     **/
54790     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54791              const unsigned int depth, const unsigned int spectrum, const T& val):
54792       _width(0),_allocated_width(0),_data(0) {
54793       assign(n);
54794       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
54795     }
54796 
54797     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
54798     /**
54799        \param n Number of images.
54800        \param width Width of images.
54801        \param height Height of images.
54802        \param depth Depth of images.
54803        \param spectrum Number of channels of images.
54804        \param val0 First value of the initializing integers sequence.
54805        \param val1 Second value of the initializing integers sequence.
54806        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
54807          or you will probably segfault.
54808     **/
54809     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54810              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
54811       _width(0),_allocated_width(0),_data(0) {
54812 #define _CImgList_stdarg(t) { \
54813 	assign(n,width,height,depth,spectrum); \
54814 	const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
54815 	T *ptrd = _data->_data; \
54816 	va_list ap; \
54817 	va_start(ap,val1); \
54818 	for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
54819 	  *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
54820 	  if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
54821 	} \
54822 	va_end(ap); \
54823       }
54824       _CImgList_stdarg(int);
54825     }
54826 
54827     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
54828     /**
54829        \param n Number of images.
54830        \param width Width of images.
54831        \param height Height of images.
54832        \param depth Depth of images.
54833        \param spectrum Number of channels of images.
54834        \param val0 First value of the initializing doubles sequence.
54835        \param val1 Second value of the initializing doubles sequence.
54836        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
54837          or you will probably segfault.
54838     **/
54839     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54840              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
54841       _width(0),_allocated_width(0),_data(0) {
54842       _CImgList_stdarg(double);
54843     }
54844 
54845     //! Construct list containing copies of an input image.
54846     /**
54847        \param n Number of images.
54848        \param img Input image to copy in the constructed list.
54849        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
54850     **/
54851     template<typename t>
54852     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
54853       _width(0),_allocated_width(0),_data(0) {
54854       assign(n);
54855       cimglist_apply(*this,assign)(img,is_shared);
54856     }
54857 
54858     //! Construct list from one image.
54859     /**
54860        \param img Input image to copy in the constructed list.
54861        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
54862      **/
54863     template<typename t>
54864     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
54865       _width(0),_allocated_width(0),_data(0) {
54866       assign(1);
54867       _data[0].assign(img,is_shared);
54868     }
54869 
54870     //! Construct list from two images.
54871     /**
54872        \param img1 First input image to copy in the constructed list.
54873        \param img2 Second input image to copy in the constructed list.
54874        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54875      **/
54876     template<typename t1, typename t2>
54877     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
54878       _width(0),_allocated_width(0),_data(0) {
54879       assign(2);
54880       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
54881     }
54882 
54883     //! Construct list from three images.
54884     /**
54885        \param img1 First input image to copy in the constructed list.
54886        \param img2 Second input image to copy in the constructed list.
54887        \param img3 Third input image to copy in the constructed list.
54888        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54889     **/
54890     template<typename t1, typename t2, typename t3>
54891     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
54892       _width(0),_allocated_width(0),_data(0) {
54893       assign(3);
54894       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54895     }
54896 
54897     //! Construct list from four images.
54898     /**
54899        \param img1 First input image to copy in the constructed list.
54900        \param img2 Second input image to copy in the constructed list.
54901        \param img3 Third input image to copy in the constructed list.
54902        \param img4 Fourth input image to copy in the constructed list.
54903        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54904     **/
54905     template<typename t1, typename t2, typename t3, typename t4>
54906     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54907              const bool is_shared=false):
54908       _width(0),_allocated_width(0),_data(0) {
54909       assign(4);
54910       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54911       _data[3].assign(img4,is_shared);
54912     }
54913 
54914     //! Construct list from five images.
54915     /**
54916        \param img1 First input image to copy in the constructed list.
54917        \param img2 Second input image to copy in the constructed list.
54918        \param img3 Third input image to copy in the constructed list.
54919        \param img4 Fourth input image to copy in the constructed list.
54920        \param img5 Fifth input image to copy in the constructed list.
54921        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54922     **/
54923     template<typename t1, typename t2, typename t3, typename t4, typename t5>
54924     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54925              const CImg<t5>& img5, const bool is_shared=false):
54926       _width(0),_allocated_width(0),_data(0) {
54927       assign(5);
54928       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54929       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
54930     }
54931 
54932     //! Construct list from six images.
54933     /**
54934        \param img1 First input image to copy in the constructed list.
54935        \param img2 Second input image to copy in the constructed list.
54936        \param img3 Third input image to copy in the constructed list.
54937        \param img4 Fourth input image to copy in the constructed list.
54938        \param img5 Fifth input image to copy in the constructed list.
54939        \param img6 Sixth input image to copy in the constructed list.
54940        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54941     **/
54942     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
54943     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54944              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
54945       _width(0),_allocated_width(0),_data(0) {
54946       assign(6);
54947       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54948       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
54949     }
54950 
54951     //! Construct list from seven images.
54952     /**
54953        \param img1 First input image to copy in the constructed list.
54954        \param img2 Second input image to copy in the constructed list.
54955        \param img3 Third input image to copy in the constructed list.
54956        \param img4 Fourth input image to copy in the constructed list.
54957        \param img5 Fifth input image to copy in the constructed list.
54958        \param img6 Sixth input image to copy in the constructed list.
54959        \param img7 Seventh input image to copy in the constructed list.
54960        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54961     **/
54962     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
54963     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54964              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
54965       _width(0),_allocated_width(0),_data(0) {
54966       assign(7);
54967       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54968       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
54969       _data[6].assign(img7,is_shared);
54970     }
54971 
54972     //! Construct list from eight 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 img6 Sixth input image to copy in the constructed list.
54980        \param img7 Seventh input image to copy in the constructed list.
54981        \param img8 Eighth input image to copy in the constructed list.
54982        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54983     **/
54984     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
54985     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54986              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
54987              const bool is_shared=false):
54988       _width(0),_allocated_width(0),_data(0) {
54989       assign(8);
54990       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54991       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
54992       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
54993     }
54994 
54995     //! Construct list copy.
54996     /**
54997        \param list Input list to copy.
54998        \note The shared state of each element of the constructed list is kept the same as in \c list.
54999     **/
55000     template<typename t>
55001     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
55002       assign(list._width);
55003       cimglist_for(*this,l) _data[l].assign(list[l],false);
55004     }
55005 
55006     //! Construct list copy \specialization.
55007     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
55008       assign(list._width);
55009       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
55010     }
55011 
55012     //! Construct list copy, and force the shared state of the list elements.
55013     /**
55014        \param list Input list to copy.
55015        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
55016     **/
55017     template<typename t>
55018     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
55019       assign(list._width);
55020       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
55021     }
55022 
55023     //! Construct list by reading the content of a file.
55024     /**
55025        \param filename Filename, as a C-string.
55026     **/
55027     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
55028       assign(filename);
55029     }
55030 
55031     //! Construct list from the content of a display window.
55032     /**
55033        \param disp Display window to get content from.
55034        \note Constructed list contains a single image only.
55035     **/
55036     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
55037       assign(disp);
55038     }
55039 
55040     //! Return a list with elements being shared copies of images in the list instance.
55041     /**
55042       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
55043     **/
55044     CImgList<T> get_shared() {
55045       CImgList<T> res(_width);
55046       cimglist_for(*this,l) res[l].assign(_data[l],true);
55047       return res;
55048     }
55049 
55050     //! Return a list with elements being shared copies of images in the list instance \const.
55051     const CImgList<T> get_shared() const {
55052       CImgList<T> res(_width);
55053       cimglist_for(*this,l) res[l].assign(_data[l],true);
55054       return res;
55055     }
55056 
55057     //! Destructor \inplace.
55058     /**
55059        \see CImgList().
55060     **/
55061     CImgList<T>& assign() {
55062       delete[] _data;
55063       _width = _allocated_width = 0;
55064       _data = 0;
55065       return *this;
55066     }
55067 
55068     //! Destructor \inplace.
55069     /**
55070        Equivalent to assign().
55071        \note Only here for compatibility with STL naming conventions.
55072     **/
55073     CImgList<T>& clear() {
55074       return assign();
55075     }
55076 
55077     //! Construct list containing empty images \inplace.
55078     /**
55079        \see CImgList(unsigned int).
55080     **/
55081     CImgList<T>& assign(const unsigned int n) {
55082       if (!n) return assign();
55083       if (_allocated_width<n || _allocated_width>(n<<2)) {
55084         delete[] _data;
55085         _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
55086       }
55087       _width = n;
55088       return *this;
55089     }
55090 
55091     //! Construct list containing images of specified size \inplace.
55092     /**
55093        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
55094     **/
55095     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
55096                         const unsigned int depth=1, const unsigned int spectrum=1) {
55097       assign(n);
55098       cimglist_apply(*this,assign)(width,height,depth,spectrum);
55099       return *this;
55100     }
55101 
55102     //! Construct list containing images of specified size, and initialize pixel values \inplace.
55103     /**
55104        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
55105     **/
55106     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55107                         const unsigned int depth, const unsigned int spectrum, const T& val) {
55108       assign(n);
55109       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
55110       return *this;
55111     }
55112 
55113     //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
55114     /**
55115        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
55116     **/
55117     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55118                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
55119       _CImgList_stdarg(int);
55120       return *this;
55121     }
55122 
55123     //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
55124     /**
55125        \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
55126     **/
55127     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55128                         const unsigned int depth, const unsigned int spectrum,
55129                         const double val0, const double val1, ...) {
55130       _CImgList_stdarg(double);
55131       return *this;
55132     }
55133 
55134     //! Construct list containing copies of an input image \inplace.
55135     /**
55136        \see CImgList(unsigned int, const CImg<t>&, bool).
55137     **/
55138     template<typename t>
55139     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
55140       assign(n);
55141       cimglist_apply(*this,assign)(img,is_shared);
55142       return *this;
55143     }
55144 
55145     //! Construct list from one image \inplace.
55146     /**
55147        \see CImgList(const CImg<t>&, bool).
55148     **/
55149     template<typename t>
55150     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
55151       assign(1);
55152       _data[0].assign(img,is_shared);
55153       return *this;
55154     }
55155 
55156     //! Construct list from two images \inplace.
55157     /**
55158        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
55159     **/
55160     template<typename t1, typename t2>
55161     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
55162       assign(2);
55163       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
55164       return *this;
55165     }
55166 
55167     //! Construct list from three images \inplace.
55168     /**
55169        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55170     **/
55171     template<typename t1, typename t2, typename t3>
55172     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
55173       assign(3);
55174       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55175       return *this;
55176     }
55177 
55178     //! Construct list from four images \inplace.
55179     /**
55180        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55181     **/
55182     template<typename t1, typename t2, typename t3, typename t4>
55183     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55184                         const bool is_shared=false) {
55185       assign(4);
55186       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55187       _data[3].assign(img4,is_shared);
55188       return *this;
55189     }
55190 
55191     //! Construct list from five images \inplace.
55192     /**
55193        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55194     **/
55195     template<typename t1, typename t2, typename t3, typename t4, typename t5>
55196     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55197                         const CImg<t5>& img5, const bool is_shared=false) {
55198       assign(5);
55199       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55200       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
55201       return *this;
55202     }
55203 
55204     //! Construct list from six images \inplace.
55205     /**
55206        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
55207     **/
55208     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
55209     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55210                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
55211       assign(6);
55212       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55213       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55214       return *this;
55215     }
55216 
55217     //! Construct list from seven images \inplace.
55218     /**
55219        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
55220        const CImg<t>&, bool).
55221     **/
55222     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
55223     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55224                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
55225       assign(7);
55226       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55227       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55228       _data[6].assign(img7,is_shared);
55229       return *this;
55230     }
55231 
55232     //! Construct list from eight images \inplace.
55233     /**
55234        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
55235        const CImg<t>&, const CImg<t>&, bool).
55236     **/
55237     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
55238     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55239                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
55240                         const bool is_shared=false) {
55241       assign(8);
55242       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55243       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55244       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
55245       return *this;
55246     }
55247 
55248     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
55249     /**
55250       \see CImgList(const CImgList<t>&, bool is_shared).
55251     **/
55252     template<typename t>
55253     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
55254       cimg::unused(is_shared);
55255       assign(list._width);
55256       cimglist_for(*this,l) _data[l].assign(list[l],false);
55257       return *this;
55258     }
55259 
55260     //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
55261     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
55262       if (this==&list) return *this;
55263       CImgList<T> res(list._width);
55264       cimglist_for(res,l) res[l].assign(list[l],is_shared);
55265       return res.move_to(*this);
55266     }
55267 
55268     //! Construct list by reading the content of a file \inplace.
55269     /**
55270       \see CImgList(const char *const).
55271     **/
55272     CImgList<T>& assign(const char *const filename) {
55273       return load(filename);
55274     }
55275 
55276     //! Construct list from the content of a display window \inplace.
55277     /**
55278       \see CImgList(const CImgDisplay&).
55279     **/
55280     CImgList<T>& assign(const CImgDisplay &disp) {
55281       return assign(CImg<T>(disp));
55282     }
55283 
55284     //! Transfer the content of the list instance to another list.
55285     /**
55286        \param list Destination list.
55287        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
55288     **/
55289     template<typename t>
55290     CImgList<t>& move_to(CImgList<t>& list) {
55291       list.assign(_width);
55292       bool is_one_shared_element = false;
55293       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
55294       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
55295       else cimglist_for(*this,l) _data[l].move_to(list[l]);
55296       assign();
55297       return list;
55298     }
55299 
55300     //! Transfer the content of the list instance at a specified position in another list.
55301     /**
55302        \param list Destination list.
55303        \param pos Index of the insertion in the list.
55304        \note When returning, the list instance is empty and the initial content of \c list is preserved
55305        (only images indexes may be modified).
55306      **/
55307     template<typename t>
55308     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
55309       if (is_empty()) return list;
55310       const unsigned int npos = pos>list._width?list._width:pos;
55311       list.insert(_width,npos);
55312       bool is_one_shared_element = false;
55313       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
55314       if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
55315       else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
55316       assign();
55317       return list;
55318     }
55319 
55320     //! Swap all fields between two list instances.
55321     /**
55322        \param list List to swap fields with.
55323        \note Can be used to exchange the content of two lists in a fast way.
55324     **/
55325     CImgList<T>& swap(CImgList<T>& list) {
55326       cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
55327       cimg::swap(_data,list._data);
55328       return list;
55329     }
55330 
55331     //! Return a reference to an empty list.
55332     /**
55333       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
55334       \code
55335       void f(const CImgList<char>& list=CImgList<char>::empty());
55336       \endcode
55337     **/
55338     static CImgList<T>& empty() {
55339       static CImgList<T> _empty;
55340       return _empty.assign();
55341     }
55342 
55343     //! Return a reference to an empty list \const.
55344     static const CImgList<T>& const_empty() {
55345       static const CImgList<T> _empty;
55346       return _empty;
55347     }
55348 
55349     //@}
55350     //------------------------------------------
55351     //
55352     //! \name Overloaded Operators
55353     //@{
55354     //------------------------------------------
55355 
55356     //! Return a reference to one image element of the list.
55357     /**
55358        \param pos Indice of the image element.
55359     **/
55360     CImg<T>& operator()(const unsigned int pos) {
55361 #if cimg_verbosity>=3
55362       if (pos>=_width) {
55363         cimg::warn(_cimglist_instance
55364                    "operator(): Invalid image request, at position [%u].",
55365                    cimglist_instance,
55366                    pos);
55367         return *_data;
55368       }
55369 #endif
55370       return _data[pos];
55371     }
55372 
55373     //! Return a reference to one image of the list.
55374     /**
55375        \param pos Indice of the image element.
55376     **/
55377     const CImg<T>& operator()(const unsigned int pos) const {
55378       return const_cast<CImgList<T>*>(this)->operator()(pos);
55379     }
55380 
55381     //! Return a reference to one pixel value of one image of the list.
55382     /**
55383        \param pos Indice of the image element.
55384        \param x X-coordinate of the pixel value.
55385        \param y Y-coordinate of the pixel value.
55386        \param z Z-coordinate of the pixel value.
55387        \param c C-coordinate of the pixel value.
55388        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
55389     **/
55390     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
55391                   const unsigned int z=0, const unsigned int c=0) {
55392       return (*this)[pos](x,y,z,c);
55393     }
55394 
55395     //! Return a reference to one pixel value of one image of the list \const.
55396     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
55397                         const unsigned int z=0, const unsigned int c=0) const {
55398       return (*this)[pos](x,y,z,c);
55399     }
55400 
55401     //! Return pointer to the first image of the list.
55402     /**
55403        \note Images in a list are stored as a buffer of \c CImg<T>.
55404     **/
55405     operator CImg<T>*() {
55406       return _data;
55407     }
55408 
55409     //! Return pointer to the first image of the list \const.
55410     operator const CImg<T>*() const {
55411       return _data;
55412     }
55413 
55414     //! Construct list from one image \inplace.
55415     /**
55416         \param img Input image to copy in the constructed list.
55417         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
55418     **/
55419     template<typename t>
55420     CImgList<T>& operator=(const CImg<t>& img) {
55421       return assign(img);
55422     }
55423 
55424     //! Construct list from another list.
55425     /**
55426        \param list Input list to copy.
55427        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
55428     **/
55429     template<typename t>
55430     CImgList<T>& operator=(const CImgList<t>& list) {
55431       return assign(list);
55432     }
55433 
55434     //! Construct list from another list \specialization.
55435     CImgList<T>& operator=(const CImgList<T>& list) {
55436       return assign(list);
55437     }
55438 
55439     //! Construct list by reading the content of a file \inplace.
55440     /**
55441        \see CImgList(const char *const).
55442     **/
55443     CImgList<T>& operator=(const char *const filename) {
55444       return assign(filename);
55445     }
55446 
55447     //! Construct list from the content of a display window \inplace.
55448     /**
55449         \see CImgList(const CImgDisplay&).
55450     **/
55451     CImgList<T>& operator=(const CImgDisplay& disp) {
55452       return assign(disp);
55453     }
55454 
55455     //! Return a non-shared copy of a list.
55456     /**
55457         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
55458           It forces the copy to have non-shared elements.
55459     **/
55460     CImgList<T> operator+() const {
55461       return CImgList<T>(*this,false);
55462     }
55463 
55464     //! Return a copy of the list instance, where image \c img has been inserted at the end.
55465     /**
55466        \param img Image inserted at the end of the instance copy.
55467        \note Define a convenient way to create temporary lists of images, as in the following code:
55468        \code
55469        (img1,img2,img3,img4).display("My four images");
55470        \endcode
55471     **/
55472     template<typename t>
55473     CImgList<T>& operator,(const CImg<t>& img) {
55474       return insert(img);
55475     }
55476 
55477     //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
55478     template<typename t>
55479     CImgList<T> operator,(const CImg<t>& img) const {
55480       return (+*this).insert(img);
55481     }
55482 
55483     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
55484     /**
55485        \param list List inserted at the end of the instance copy.
55486     **/
55487     template<typename t>
55488     CImgList<T>& operator,(const CImgList<t>& list) {
55489       return insert(list);
55490     }
55491 
55492     //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
55493     template<typename t>
55494     CImgList<T>& operator,(const CImgList<t>& list) const {
55495       return (+*this).insert(list);
55496     }
55497 
55498     //! Return image corresponding to the appending of all images of the instance list along specified axis.
55499     /**
55500       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
55501       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
55502     **/
55503     CImg<T> operator>(const char axis) const {
55504       return get_append(axis,0);
55505     }
55506 
55507     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
55508     /**
55509       \param axis Axis used for image splitting.
55510       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
55511     **/
55512     CImgList<T> operator<(const char axis) const {
55513       return get_split(axis);
55514     }
55515 
55516     //@}
55517     //-------------------------------------
55518     //
55519     //! \name Instance Characteristics
55520     //@{
55521     //-------------------------------------
55522 
55523     //! Return the type of image pixel values as a C string.
55524     /**
55525        Return a \c char* string containing the usual type name of the image pixel values
55526        (i.e. a stringified version of the template parameter \c T).
55527        \note
55528        - The returned string may contain spaces (as in \c "unsigned char").
55529        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
55530     **/
55531     static const char* pixel_type() {
55532       return cimg::type<T>::string();
55533     }
55534 
55535     //! Return the size of the list, i.e. the number of images contained in it.
55536     /**
55537       \note Similar to size() but returns result as a (signed) integer.
55538     **/
55539     int width() const {
55540       return (int)_width;
55541     }
55542 
55543     //! Return the size of the list, i.e. the number of images contained in it.
55544     /**
55545       \note Similar to width() but returns result as an unsigned integer.
55546     **/
55547     unsigned int size() const {
55548       return _width;
55549     }
55550 
55551     //! Return pointer to the first image of the list.
55552     /**
55553        \note Images in a list are stored as a buffer of \c CImg<T>.
55554     **/
55555     CImg<T> *data() {
55556       return _data;
55557     }
55558 
55559     //! Return pointer to the first image of the list \const.
55560     const CImg<T> *data() const {
55561       return _data;
55562     }
55563 
55564     //! Return pointer to the pos-th image of the list.
55565     /**
55566        \param pos Indice of the image element to access.
55567        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
55568     **/
55569 #if cimg_verbosity>=3
55570     CImg<T> *data(const unsigned int pos) {
55571       if (pos>=size())
55572         cimg::warn(_cimglist_instance
55573                    "data(): Invalid pointer request, at position [%u].",
55574                    cimglist_instance,
55575                    pos);
55576       return _data + pos;
55577     }
55578 
55579     const CImg<T> *data(const unsigned int l) const {
55580       return const_cast<CImgList<T>*>(this)->data(l);
55581     }
55582 #else
55583     CImg<T> *data(const unsigned int l) {
55584       return _data + l;
55585     }
55586 
55587     //! Return pointer to the pos-th image of the list \const.
55588     const CImg<T> *data(const unsigned int l) const {
55589       return _data + l;
55590     }
55591 #endif
55592 
55593     //! Return iterator to the first image of the list.
55594     /**
55595     **/
55596     iterator begin() {
55597       return _data;
55598     }
55599 
55600     //! Return iterator to the first image of the list \const.
55601     const_iterator begin() const {
55602       return _data;
55603     }
55604 
55605     //! Return iterator to one position after the last image of the list.
55606     /**
55607     **/
55608     iterator end() {
55609       return _data + _width;
55610     }
55611 
55612     //! Return iterator to one position after the last image of the list \const.
55613     const_iterator end() const {
55614       return _data + _width;
55615     }
55616 
55617     //! Return reference to the first image of the list.
55618     /**
55619     **/
55620     CImg<T>& front() {
55621       return *_data;
55622     }
55623 
55624     //! Return reference to the first image of the list \const.
55625     const CImg<T>& front() const {
55626       return *_data;
55627     }
55628 
55629     //! Return a reference to the last image of the list.
55630     /**
55631     **/
55632     const CImg<T>& back() const {
55633       return *(_data + _width - 1);
55634     }
55635 
55636     //! Return a reference to the last image of the list \const.
55637     CImg<T>& back() {
55638       return *(_data + _width - 1);
55639     }
55640 
55641     //! Return pos-th image of the list.
55642     /**
55643        \param pos Indice of the image element to access.
55644     **/
55645     CImg<T>& at(const int pos) {
55646       if (is_empty())
55647         throw CImgInstanceException(_cimglist_instance
55648                                     "at(): Empty instance.",
55649                                     cimglist_instance);
55650 
55651       return _data[cimg::cut(pos,0,width() - 1)];
55652     }
55653 
55654     //! Access to pixel value with Dirichlet boundary conditions.
55655     /**
55656        \param pos Indice of the image element to access.
55657        \param x X-coordinate of the pixel value.
55658        \param y Y-coordinate of the pixel value.
55659        \param z Z-coordinate of the pixel value.
55660        \param c C-coordinate of the pixel value.
55661        \param out_value Default value returned if \c offset is outside image bounds.
55662        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
55663     **/
55664     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55665       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
55666     }
55667 
55668     //! Access to pixel value with Dirichlet boundary conditions \const.
55669     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55670       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
55671     }
55672 
55673     //! Access to pixel value with Neumann boundary conditions.
55674     /**
55675        \param pos Indice of the image element to access.
55676        \param x X-coordinate of the pixel value.
55677        \param y Y-coordinate of the pixel value.
55678        \param z Z-coordinate of the pixel value.
55679        \param c C-coordinate of the pixel value.
55680        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
55681     **/
55682     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
55683       if (is_empty())
55684         throw CImgInstanceException(_cimglist_instance
55685                                     "atNXYZC(): Empty instance.",
55686                                     cimglist_instance);
55687 
55688       return _atNXYZC(pos,x,y,z,c);
55689     }
55690 
55691     //! Access to pixel value with Neumann boundary conditions \const.
55692     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
55693       if (is_empty())
55694         throw CImgInstanceException(_cimglist_instance
55695                                     "atNXYZC(): Empty instance.",
55696                                     cimglist_instance);
55697 
55698       return _atNXYZC(pos,x,y,z,c);
55699     }
55700 
55701     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
55702       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
55703     }
55704 
55705     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
55706       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
55707     }
55708 
55709     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
55710     /**
55711        \param pos Indice of the image element to access.
55712        \param x X-coordinate of the pixel value.
55713        \param y Y-coordinate of the pixel value.
55714        \param z Z-coordinate of the pixel value.
55715        \param c C-coordinate of the pixel value.
55716        \param out_value Default value returned if \c offset is outside image bounds.
55717        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55718     **/
55719     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55720       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
55721     }
55722 
55723     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
55724     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55725       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
55726     }
55727 
55728     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
55729     /**
55730        \param pos Indice of the image element to access.
55731        \param x X-coordinate of the pixel value.
55732        \param y Y-coordinate of the pixel value.
55733        \param z Z-coordinate of the pixel value.
55734        \param c C-coordinate of the pixel value.
55735        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55736     **/
55737    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
55738       if (is_empty())
55739         throw CImgInstanceException(_cimglist_instance
55740                                     "atNXYZ(): Empty instance.",
55741                                     cimglist_instance);
55742 
55743       return _atNXYZ(pos,x,y,z,c);
55744     }
55745 
55746     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
55747     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
55748       if (is_empty())
55749         throw CImgInstanceException(_cimglist_instance
55750                                     "atNXYZ(): Empty instance.",
55751                                     cimglist_instance);
55752 
55753       return _atNXYZ(pos,x,y,z,c);
55754     }
55755 
55756     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
55757       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
55758     }
55759 
55760     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
55761       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
55762     }
55763 
55764     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
55765     /**
55766        \param pos Indice of the image element to access.
55767        \param x X-coordinate of the pixel value.
55768        \param y Y-coordinate of the pixel value.
55769        \param z Z-coordinate of the pixel value.
55770        \param c C-coordinate of the pixel value.
55771        \param out_value Default value returned if \c offset is outside image bounds.
55772        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55773     **/
55774     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55775       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
55776     }
55777 
55778     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
55779     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55780       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
55781     }
55782 
55783     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
55784     /**
55785        \param pos Indice of the image element to access.
55786        \param x X-coordinate of the pixel value.
55787        \param y Y-coordinate of the pixel value.
55788        \param z Z-coordinate of the pixel value.
55789        \param c C-coordinate of the pixel value.
55790        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55791     **/
55792     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
55793       if (is_empty())
55794         throw CImgInstanceException(_cimglist_instance
55795                                     "atNXY(): Empty instance.",
55796                                     cimglist_instance);
55797 
55798       return _atNXY(pos,x,y,z,c);
55799     }
55800 
55801     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
55802     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
55803       if (is_empty())
55804         throw CImgInstanceException(_cimglist_instance
55805                                     "atNXY(): Empty instance.",
55806                                     cimglist_instance);
55807 
55808       return _atNXY(pos,x,y,z,c);
55809     }
55810 
55811     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
55812       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
55813     }
55814 
55815     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
55816       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
55817     }
55818 
55819     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
55820     /**
55821        \param pos Indice of the image element to access.
55822        \param x X-coordinate of the pixel value.
55823        \param y Y-coordinate of the pixel value.
55824        \param z Z-coordinate of the pixel value.
55825        \param c C-coordinate of the pixel value.
55826        \param out_value Default value returned if \c offset is outside image bounds.
55827        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55828     **/
55829     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55830       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
55831     }
55832 
55833     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
55834     T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55835       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
55836     }
55837 
55838     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
55839     /**
55840        \param pos Indice of the image element to access.
55841        \param x X-coordinate of the pixel value.
55842        \param y Y-coordinate of the pixel value.
55843        \param z Z-coordinate of the pixel value.
55844        \param c C-coordinate of the pixel value.
55845        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55846     **/
55847     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
55848       if (is_empty())
55849         throw CImgInstanceException(_cimglist_instance
55850                                     "atNX(): Empty instance.",
55851                                     cimglist_instance);
55852 
55853       return _atNX(pos,x,y,z,c);
55854     }
55855 
55856     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
55857     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
55858       if (is_empty())
55859         throw CImgInstanceException(_cimglist_instance
55860                                     "atNX(): Empty instance.",
55861                                     cimglist_instance);
55862 
55863       return _atNX(pos,x,y,z,c);
55864     }
55865 
55866     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
55867       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
55868     }
55869 
55870     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
55871       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
55872     }
55873 
55874     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
55875     /**
55876        \param pos Indice of the image element to access.
55877        \param x X-coordinate of the pixel value.
55878        \param y Y-coordinate of the pixel value.
55879        \param z Z-coordinate of the pixel value.
55880        \param c C-coordinate of the pixel value.
55881        \param out_value Default value returned if \c offset is outside image bounds.
55882        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55883     **/
55884     T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55885       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
55886     }
55887 
55888     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
55889     T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55890       return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
55891     }
55892 
55893     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
55894     /**
55895        \param pos Indice of the image element to access.
55896        \param x X-coordinate of the pixel value.
55897        \param y Y-coordinate of the pixel value.
55898        \param z Z-coordinate of the pixel value.
55899        \param c C-coordinate of the pixel value.
55900        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55901     **/
55902     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
55903       if (is_empty())
55904         throw CImgInstanceException(_cimglist_instance
55905                                     "atN(): Empty instance.",
55906                                     cimglist_instance);
55907       return _atN(pos,x,y,z,c);
55908     }
55909 
55910     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
55911     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
55912       if (is_empty())
55913         throw CImgInstanceException(_cimglist_instance
55914                                     "atN(): Empty instance.",
55915                                     cimglist_instance);
55916       return _atN(pos,x,y,z,c);
55917     }
55918 
55919     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
55920       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
55921     }
55922 
55923     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
55924       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
55925     }
55926 
55927     //@}
55928     //-------------------------------------
55929     //
55930     //! \name Instance Checking
55931     //@{
55932     //-------------------------------------
55933 
55934     //! Return \c true if list is empty.
55935     /**
55936     **/
55937     bool is_empty() const {
55938       return (!_data || !_width);
55939     }
55940 
55941     //! Test if number of image elements is equal to specified value.
55942     /**
55943         \param size_n Number of image elements to test.
55944     **/
55945     bool is_sameN(const unsigned int size_n) const {
55946       return _width==size_n;
55947     }
55948 
55949     //! Test if number of image elements is equal between two images lists.
55950     /**
55951         \param list Input list to compare with.
55952     **/
55953     template<typename t>
55954     bool is_sameN(const CImgList<t>& list) const {
55955       return is_sameN(list._width);
55956     }
55957 
55958     // Define useful functions to check list dimensions.
55959     // (cannot be documented because macro-generated).
55960 #define _cimglist_def_is_same1(axis) \
55961     bool is_same##axis(const unsigned int val) const { \
55962       bool res = true; \
55963       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
55964     } \
55965     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
55966       return is_sameN(n) && is_same##axis(val); \
55967     } \
55968 
55969 #define _cimglist_def_is_same2(axis1,axis2) \
55970     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
55971       bool res = true; \
55972       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
55973     } \
55974     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
55975       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
55976     } \
55977 
55978 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
55979     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
55980                                       const unsigned int val3) const { \
55981       bool res = true; \
55982       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
55983       return res; \
55984     } \
55985     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
55986                                        const unsigned int val2, const unsigned int val3) const { \
55987       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
55988     } \
55989 
55990 #define _cimglist_def_is_same(axis) \
55991     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
55992       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
55993     } \
55994     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
55995       const unsigned int lmin = std::min(_width,list._width); \
55996       bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
55997     } \
55998     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
55999       return (is_sameN(n) && is_same##axis(img)); \
56000     } \
56001     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
56002       return (is_sameN(list) && is_same##axis(list)); \
56003     }
56004 
56005     _cimglist_def_is_same(XY)
56006     _cimglist_def_is_same(XZ)
56007     _cimglist_def_is_same(XC)
56008     _cimglist_def_is_same(YZ)
56009     _cimglist_def_is_same(YC)
56010     _cimglist_def_is_same(XYZ)
56011     _cimglist_def_is_same(XYC)
56012     _cimglist_def_is_same(YZC)
56013     _cimglist_def_is_same(XYZC)
56014     _cimglist_def_is_same1(X)
56015     _cimglist_def_is_same1(Y)
56016     _cimglist_def_is_same1(Z)
56017     _cimglist_def_is_same1(C)
56018     _cimglist_def_is_same2(X,Y)
56019     _cimglist_def_is_same2(X,Z)
56020     _cimglist_def_is_same2(X,C)
56021     _cimglist_def_is_same2(Y,Z)
56022     _cimglist_def_is_same2(Y,C)
56023     _cimglist_def_is_same2(Z,C)
56024     _cimglist_def_is_same3(X,Y,Z)
56025     _cimglist_def_is_same3(X,Y,C)
56026     _cimglist_def_is_same3(X,Z,C)
56027     _cimglist_def_is_same3(Y,Z,C)
56028 
56029     //! Test if dimensions of each image of the list match specified arguments.
56030     /**
56031       \param dx Checked image width.
56032       \param dy Checked image height.
56033       \param dz Checked image depth.
56034       \param dc Checked image spectrum.
56035     **/
56036     bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
56037                      const unsigned int dz, const unsigned int dc) const {
56038       bool res = true;
56039       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
56040       return res;
56041     }
56042 
56043     //! Test if list dimensions match specified arguments.
56044     /**
56045        \param n Number of images in the list.
56046        \param dx Checked image width.
56047        \param dy Checked image height.
56048        \param dz Checked image depth.
56049        \param dc Checked image spectrum.
56050     **/
56051     bool is_sameNXYZC(const unsigned int n,
56052                       const unsigned int dx, const unsigned int dy,
56053                       const unsigned int dz, const unsigned int dc) const {
56054       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
56055     }
56056 
56057     //! Test if list contains one particular pixel location.
56058     /**
56059        \param n Index of the image whom checked pixel value belong to.
56060        \param x X-coordinate of the checked pixel value.
56061        \param y Y-coordinate of the checked pixel value.
56062        \param z Z-coordinate of the checked pixel value.
56063        \param c C-coordinate of the checked pixel value.
56064     **/
56065     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
56066       if (is_empty()) return false;
56067       return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
56068         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
56069     }
56070 
56071     //! Test if list contains image with specified indice.
56072     /**
56073        \param n Index of the checked image.
56074     **/
56075     bool containsN(const int n) const {
56076       if (is_empty()) return false;
56077       return n>=0 && n<(int)_width;
56078     }
56079 
56080     //! Test if one image of the list contains the specified referenced value.
56081     /**
56082        \param pixel Reference to pixel value to test.
56083        \param[out] n Index of image containing the pixel value, if test succeeds.
56084        \param[out] x X-coordinate of the pixel value, if test succeeds.
56085        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56086        \param[out] z Z-coordinate of the pixel value, if test succeeds.
56087        \param[out] c C-coordinate of the pixel value, if test succeeds.
56088        \note If true, set coordinates (n,x,y,z,c).
56089     **/
56090     template<typename t>
56091     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
56092       if (is_empty()) return false;
56093       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
56094       return false;
56095     }
56096 
56097     //! Test if one of the image list contains the specified referenced value.
56098     /**
56099        \param pixel Reference to pixel value to test.
56100        \param[out] n Index of image containing the pixel value, if test succeeds.
56101        \param[out] x X-coordinate of the pixel value, if test succeeds.
56102        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56103        \param[out] z Z-coordinate of the pixel value, if test succeeds.
56104        \note If true, set coordinates (n,x,y,z).
56105     **/
56106     template<typename t>
56107     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
56108       t c;
56109       return contains(pixel,n,x,y,z,c);
56110     }
56111 
56112     //! Test if one of the image list contains the specified referenced value.
56113     /**
56114        \param pixel Reference to pixel value to test.
56115        \param[out] n Index of image containing the pixel value, if test succeeds.
56116        \param[out] x X-coordinate of the pixel value, if test succeeds.
56117        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56118        \note If true, set coordinates (n,x,y).
56119     **/
56120     template<typename t>
56121     bool contains(const T& pixel, t& n, t& x, t&y) const {
56122       t z, c;
56123       return contains(pixel,n,x,y,z,c);
56124     }
56125 
56126     //! Test if one of the image list contains the specified referenced value.
56127     /**
56128        \param pixel Reference to pixel value to test.
56129        \param[out] n Index of image containing the pixel value, if test succeeds.
56130        \param[out] x X-coordinate of the pixel value, if test succeeds.
56131        \note If true, set coordinates (n,x).
56132     **/
56133     template<typename t>
56134     bool contains(const T& pixel, t& n, t& x) const {
56135       t y, z, c;
56136       return contains(pixel,n,x,y,z,c);
56137     }
56138 
56139     //! Test if one of the image list contains the specified referenced value.
56140     /**
56141        \param pixel Reference to pixel value to test.
56142        \param[out] n Index of image containing the pixel value, if test succeeds.
56143        \note If true, set coordinates (n).
56144     **/
56145     template<typename t>
56146     bool contains(const T& pixel, t& n) const {
56147       t x, y, z, c;
56148       return contains(pixel,n,x,y,z,c);
56149     }
56150 
56151     //! Test if one of the image list contains the specified referenced value.
56152     /**
56153        \param pixel Reference to pixel value to test.
56154     **/
56155     bool contains(const T& pixel) const {
56156       unsigned int n, x, y, z, c;
56157       return contains(pixel,n,x,y,z,c);
56158     }
56159 
56160     //! Test if the list contains the image 'img'.
56161     /**
56162        \param img Reference to image to test.
56163        \param[out] n Index of image in the list, if test succeeds.
56164        \note If true, returns the position (n) of the image in the list.
56165     **/
56166     template<typename t>
56167     bool contains(const CImg<T>& img, t& n) const {
56168       if (is_empty()) return false;
56169       const CImg<T> *const ptr = &img;
56170       cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
56171       return false;
56172     }
56173 
56174     //! Test if the list contains the image img.
56175     /**
56176        \param img Reference to image to test.
56177     **/
56178     bool contains(const CImg<T>& img) const {
56179       unsigned int n;
56180       return contains(img,n);
56181     }
56182 
56183     //@}
56184     //-------------------------------------
56185     //
56186     //! \name Mathematical Functions
56187     //@{
56188     //-------------------------------------
56189 
56190     //! Return a reference to the minimum pixel value of the instance list.
56191     /**
56192     **/
56193     T& min() {
56194       if (is_empty())
56195         throw CImgInstanceException(_cimglist_instance
56196                                     "min(): Empty instance.",
56197                                     cimglist_instance);
56198       T *ptr_min = _data->_data;
56199       T min_value = *ptr_min;
56200       cimglist_for(*this,l) {
56201         const CImg<T>& img = _data[l];
56202         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
56203       }
56204       return *ptr_min;
56205     }
56206 
56207     //! Return a reference to the minimum pixel value of the instance list \const.
56208     const T& min() const {
56209       if (is_empty())
56210         throw CImgInstanceException(_cimglist_instance
56211                                     "min(): Empty instance.",
56212                                     cimglist_instance);
56213       const T *ptr_min = _data->_data;
56214       T min_value = *ptr_min;
56215       cimglist_for(*this,l) {
56216         const CImg<T>& img = _data[l];
56217         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
56218       }
56219       return *ptr_min;
56220     }
56221 
56222     //! Return a reference to the maximum pixel value of the instance list.
56223     /**
56224     **/
56225     T& max() {
56226       if (is_empty())
56227         throw CImgInstanceException(_cimglist_instance
56228                                     "max(): Empty instance.",
56229                                     cimglist_instance);
56230       T *ptr_max = _data->_data;
56231       T max_value = *ptr_max;
56232       cimglist_for(*this,l) {
56233         const CImg<T>& img = _data[l];
56234         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
56235       }
56236       return *ptr_max;
56237     }
56238 
56239     //! Return a reference to the maximum pixel value of the instance list \const.
56240     const T& max() const {
56241       if (is_empty())
56242         throw CImgInstanceException(_cimglist_instance
56243                                     "max(): Empty instance.",
56244                                     cimglist_instance);
56245       const T *ptr_max = _data->_data;
56246       T max_value = *ptr_max;
56247       cimglist_for(*this,l) {
56248         const CImg<T>& img = _data[l];
56249         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
56250       }
56251       return *ptr_max;
56252     }
56253 
56254     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
56255     /**
56256        \param[out] max_val Value of the maximum value found.
56257     **/
56258     template<typename t>
56259     T& min_max(t& max_val) {
56260       if (is_empty())
56261         throw CImgInstanceException(_cimglist_instance
56262                                     "min_max(): Empty instance.",
56263                                     cimglist_instance);
56264       T *ptr_min = _data->_data;
56265       T min_value = *ptr_min, max_value = min_value;
56266       cimglist_for(*this,l) {
56267         const CImg<T>& img = _data[l];
56268         cimg_for(img,ptrs,T) {
56269           const T val = *ptrs;
56270           if (val<min_value) { min_value = val; ptr_min = ptrs; }
56271           if (val>max_value) max_value = val;
56272         }
56273       }
56274       max_val = (t)max_value;
56275       return *ptr_min;
56276     }
56277 
56278     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
56279     /**
56280        \param[out] max_val Value of the maximum value found.
56281     **/
56282     template<typename t>
56283     const T& min_max(t& max_val) const {
56284       if (is_empty())
56285         throw CImgInstanceException(_cimglist_instance
56286                                     "min_max(): Empty instance.",
56287                                     cimglist_instance);
56288       const T *ptr_min = _data->_data;
56289       T min_value = *ptr_min, max_value = min_value;
56290       cimglist_for(*this,l) {
56291         const CImg<T>& img = _data[l];
56292         cimg_for(img,ptrs,T) {
56293           const T val = *ptrs;
56294           if (val<min_value) { min_value = val; ptr_min = ptrs; }
56295           if (val>max_value) max_value = val;
56296         }
56297       }
56298       max_val = (t)max_value;
56299       return *ptr_min;
56300     }
56301 
56302     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
56303     /**
56304        \param[out] min_val Value of the minimum value found.
56305     **/
56306     template<typename t>
56307     T& max_min(t& min_val) {
56308       if (is_empty())
56309         throw CImgInstanceException(_cimglist_instance
56310                                     "max_min(): Empty instance.",
56311                                     cimglist_instance);
56312       T *ptr_max = _data->_data;
56313       T min_value = *ptr_max, max_value = min_value;
56314       cimglist_for(*this,l) {
56315         const CImg<T>& img = _data[l];
56316         cimg_for(img,ptrs,T) {
56317           const T val = *ptrs;
56318           if (val>max_value) { max_value = val; ptr_max = ptrs; }
56319           if (val<min_value) min_value = val;
56320         }
56321       }
56322       min_val = (t)min_value;
56323       return *ptr_max;
56324     }
56325 
56326     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
56327     template<typename t>
56328     const T& max_min(t& min_val) const {
56329       if (is_empty())
56330         throw CImgInstanceException(_cimglist_instance
56331                                     "max_min(): Empty instance.",
56332                                     cimglist_instance);
56333       const T *ptr_max = _data->_data;
56334       T min_value = *ptr_max, max_value = min_value;
56335       cimglist_for(*this,l) {
56336         const CImg<T>& img = _data[l];
56337         cimg_for(img,ptrs,T) {
56338           const T val = *ptrs;
56339           if (val>max_value) { max_value = val; ptr_max = ptrs; }
56340           if (val<min_value) min_value = val;
56341         }
56342       }
56343       min_val = (t)min_value;
56344       return *ptr_max;
56345     }
56346 
56347     //@}
56348     //---------------------------
56349     //
56350     //! \name List Manipulation
56351     //@{
56352     //---------------------------
56353 
56354     //! Insert a copy of the image \c img into the current image list, at position \c pos.
56355     /**
56356         \param img Image to insert a copy to the list.
56357         \param pos Index of the insertion.
56358         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
56359     **/
56360     template<typename t>
56361     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
56362       const unsigned int npos = pos==~0U?_width:pos;
56363       if (npos>_width)
56364         throw CImgArgumentException(_cimglist_instance
56365                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
56366                                     "at position %u.",
56367                                     cimglist_instance,
56368                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
56369       if (is_shared)
56370         throw CImgArgumentException(_cimglist_instance
56371                                     "insert(): Invalid insertion request of specified shared image "
56372                                     "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
56373                                     cimglist_instance,
56374                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
56375 
56376       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
56377                                                                         (_allocated_width=16)]:0;
56378       if (!_data) { // Insert new element into empty list.
56379         _data = new_data;
56380         *_data = img;
56381       } else {
56382         if (new_data) { // Insert with re-allocation.
56383           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
56384           if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg<T>)*(_width - 1 - npos));
56385           std::memset(_data,0,sizeof(CImg<T>)*(_width - 1));
56386           delete[] _data;
56387           _data = new_data;
56388         } else if (npos!=_width - 1) // Insert without re-allocation.
56389           std::memmove(_data + npos + 1,_data + npos,sizeof(CImg<T>)*(_width - 1 - npos));
56390         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
56391         _data[npos]._data = 0;
56392         _data[npos] = img;
56393       }
56394       return *this;
56395     }
56396 
56397     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
56398     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
56399       const unsigned int npos = pos==~0U?_width:pos;
56400       if (npos>_width)
56401         throw CImgArgumentException(_cimglist_instance
56402                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
56403                                     "at position %u.",
56404                                     cimglist_instance,
56405                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
56406       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
56407                                                                         (_allocated_width=16)]:0;
56408       if (!_data) { // Insert new element into empty list.
56409         _data = new_data;
56410         if (is_shared && img) {
56411           _data->_width = img._width;
56412           _data->_height = img._height;
56413           _data->_depth = img._depth;
56414           _data->_spectrum = img._spectrum;
56415           _data->_is_shared = true;
56416           _data->_data = img._data;
56417         } else *_data = img;
56418       }
56419       else {
56420         if (new_data) { // Insert with re-allocation.
56421           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
56422           if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg<T>)*(_width - 1 - npos));
56423           if (is_shared && img) {
56424             new_data[npos]._width = img._width;
56425             new_data[npos]._height = img._height;
56426             new_data[npos]._depth = img._depth;
56427             new_data[npos]._spectrum = img._spectrum;
56428             new_data[npos]._is_shared = true;
56429             new_data[npos]._data = img._data;
56430           } else {
56431             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
56432             new_data[npos]._data = 0;
56433             new_data[npos] = img;
56434           }
56435           std::memset(_data,0,sizeof(CImg<T>)*(_width - 1));
56436           delete[] _data;
56437           _data = new_data;
56438         } else { // Insert without re-allocation.
56439           if (npos!=_width - 1) std::memmove(_data + npos + 1,_data + npos,sizeof(CImg<T>)*(_width - 1 - npos));
56440           if (is_shared && img) {
56441             _data[npos]._width = img._width;
56442             _data[npos]._height = img._height;
56443             _data[npos]._depth = img._depth;
56444             _data[npos]._spectrum = img._spectrum;
56445             _data[npos]._is_shared = true;
56446             _data[npos]._data = img._data;
56447           } else {
56448             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
56449             _data[npos]._data = 0;
56450             _data[npos] = img;
56451           }
56452         }
56453       }
56454       return *this;
56455     }
56456 
56457     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
56458     template<typename t>
56459     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
56460       return (+*this).insert(img,pos,is_shared);
56461     }
56462 
56463     //! Insert n empty images img into the current image list, at position \p pos.
56464     /**
56465        \param n Number of empty images to insert.
56466        \param pos Index of the insertion.
56467     **/
56468     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
56469       CImg<T> empty;
56470       if (!n) return *this;
56471       const unsigned int npos = pos==~0U?_width:pos;
56472       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
56473       return *this;
56474     }
56475 
56476     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
56477     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
56478       return (+*this).insert(n,pos);
56479     }
56480 
56481     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
56482     /**
56483        \param n Number of image copies to insert.
56484        \param img Image to insert by copy.
56485        \param pos Index of the insertion.
56486        \param is_shared Tells if inserted images are shared copies of \c img or not.
56487     **/
56488     template<typename t>
56489     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
56490                         const bool is_shared=false) {
56491       if (!n) return *this;
56492       const unsigned int npos = pos==~0U?_width:pos;
56493       insert(img,npos,is_shared);
56494       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
56495       return *this;
56496     }
56497 
56498     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
56499     template<typename t>
56500     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
56501                            const bool is_shared=false) const {
56502       return (+*this).insert(n,img,pos,is_shared);
56503     }
56504 
56505     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
56506     /**
56507       \param list Image list to insert.
56508       \param pos Index of the insertion.
56509       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
56510     **/
56511     template<typename t>
56512     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
56513       const unsigned int npos = pos==~0U?_width:pos;
56514       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
56515       else insert(CImgList<T>(list),npos,is_shared);
56516       return *this;
56517     }
56518 
56519     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
56520     template<typename t>
56521     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
56522       return (+*this).insert(list,pos,is_shared);
56523     }
56524 
56525     //! Insert n copies of the list \c list at position \c pos of the current list.
56526     /**
56527       \param n Number of list copies to insert.
56528       \param list Image list to insert.
56529       \param pos Index of the insertion.
56530       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
56531     **/
56532     template<typename t>
56533     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
56534                         const bool is_shared=false) {
56535       if (!n) return *this;
56536       const unsigned int npos = pos==~0U?_width:pos;
56537       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
56538       return *this;
56539     }
56540 
56541     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
56542     template<typename t>
56543     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
56544                            const bool is_shared=false) const {
56545       return (+*this).insert(n,list,pos,is_shared);
56546     }
56547 
56548     //! Remove all images between from indexes.
56549     /**
56550       \param pos1 Starting index of the removal.
56551       \param pos2 Ending index of the removal.
56552     **/
56553     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
56554       const unsigned int
56555         npos1 = pos1<pos2?pos1:pos2,
56556         tpos2 = pos1<pos2?pos2:pos1,
56557         npos2 = tpos2<_width?tpos2:_width - 1;
56558       if (npos1>=_width)
56559         throw CImgArgumentException(_cimglist_instance
56560                                     "remove(): Invalid remove request at positions %u->%u.",
56561                                     cimglist_instance,
56562                                     npos1,tpos2);
56563       else {
56564         if (tpos2>=_width)
56565           throw CImgArgumentException(_cimglist_instance
56566                                       "remove(): Invalid remove request at positions %u->%u.",
56567                                       cimglist_instance,
56568                                       npos1,tpos2);
56569 
56570         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
56571         const unsigned int nb = 1 + npos2 - npos1;
56572         if (!(_width-=nb)) return assign();
56573         if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
56574           if (npos1!=_width) std::memmove(_data + npos1,_data + npos2 + 1,sizeof(CImg<T>)*(_width - npos1));
56575           std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
56576         } else { // Removing items with reallocation.
56577           _allocated_width>>=2;
56578           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
56579           CImg<T> *const new_data = new CImg<T>[_allocated_width];
56580           if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
56581           if (npos1!=_width) std::memcpy(new_data + npos1,_data + npos2 + 1,sizeof(CImg<T>)*(_width - npos1));
56582           if (_width!=_allocated_width) std::memset(new_data + _width,0,sizeof(CImg<T>)*(_allocated_width - _width));
56583           std::memset(_data,0,sizeof(CImg<T>)*(_width + nb));
56584           delete[] _data;
56585           _data = new_data;
56586         }
56587       }
56588       return *this;
56589     }
56590 
56591     //! Remove all images between from indexes \newinstance.
56592     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
56593       return (+*this).remove(pos1,pos2);
56594     }
56595 
56596     //! Remove image at index \c pos from the image list.
56597     /**
56598       \param pos Index of the image to remove.
56599     **/
56600     CImgList<T>& remove(const unsigned int pos) {
56601       return remove(pos,pos);
56602     }
56603 
56604     //! Remove image at index \c pos from the image list \newinstance.
56605     CImgList<T> get_remove(const unsigned int pos) const {
56606       return (+*this).remove(pos);
56607     }
56608 
56609     //! Remove last image.
56610     /**
56611     **/
56612     CImgList<T>& remove() {
56613       return remove(_width - 1);
56614     }
56615 
56616     //! Remove last image \newinstance.
56617     CImgList<T> get_remove() const {
56618       return (+*this).remove();
56619     }
56620 
56621     //! Reverse list order.
56622     CImgList<T>& reverse() {
56623       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
56624       return *this;
56625     }
56626 
56627     //! Reverse list order \newinstance.
56628     CImgList<T> get_reverse() const {
56629       return (+*this).reverse();
56630     }
56631 
56632     //! Return a sublist.
56633     /**
56634       \param pos0 Starting index of the sublist.
56635       \param pos1 Ending index of the sublist.
56636     **/
56637     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
56638       return get_images(pos0,pos1).move_to(*this);
56639     }
56640 
56641     //! Return a sublist \newinstance.
56642     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
56643       if (pos0>pos1 || pos1>=_width)
56644         throw CImgArgumentException(_cimglist_instance
56645                                     "images(): Specified sub-list indices (%u->%u) are out of bounds.",
56646                                     cimglist_instance,
56647                                     pos0,pos1);
56648       CImgList<T> res(pos1 - pos0 + 1);
56649       cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
56650       return res;
56651     }
56652 
56653     //! Return a shared sublist.
56654     /**
56655       \param pos0 Starting index of the sublist.
56656       \param pos1 Ending index of the sublist.
56657     **/
56658     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
56659       if (pos0>pos1 || pos1>=_width)
56660         throw CImgArgumentException(_cimglist_instance
56661                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
56662                                     cimglist_instance,
56663                                     pos0,pos1);
56664       CImgList<T> res(pos1 - pos0 + 1);
56665       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
56666       return res;
56667     }
56668 
56669     //! Return a shared sublist \newinstance.
56670     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
56671       if (pos0>pos1 || pos1>=_width)
56672         throw CImgArgumentException(_cimglist_instance
56673                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
56674                                     cimglist_instance,
56675                                     pos0,pos1);
56676       CImgList<T> res(pos1 - pos0 + 1);
56677       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
56678       return res;
56679     }
56680 
56681     //! Return a single image which is the appending of all images of the current CImgList instance.
56682     /**
56683        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
56684        \param align Appending alignment.
56685     **/
56686     CImg<T> get_append(const char axis, const float align=0) const {
56687       if (is_empty()) return CImg<T>();
56688       if (_width==1) return +((*this)[0]);
56689       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
56690       CImg<T> res;
56691       switch (cimg::lowercase(axis)) {
56692       case 'x' : { // Along the X-axis.
56693         cimglist_for(*this,l) {
56694           const CImg<T>& img = (*this)[l];
56695           if (img) {
56696             dx+=img._width;
56697             dy = std::max(dy,img._height);
56698             dz = std::max(dz,img._depth);
56699             dc = std::max(dc,img._spectrum);
56700           }
56701         }
56702         res.assign(dx,dy,dz,dc,(T)0);
56703         if (res) cimglist_for(*this,l) {
56704             const CImg<T>& img = (*this)[l];
56705             if (img) res.draw_image(pos,
56706                                     (int)(align*(dy - img._height)),
56707                                     (int)(align*(dz - img._depth)),
56708                                     (int)(align*(dc - img._spectrum)),
56709                                     img);
56710             pos+=img._width;
56711           }
56712       } break;
56713       case 'y' : { // Along the Y-axis.
56714         cimglist_for(*this,l) {
56715           const CImg<T>& img = (*this)[l];
56716           if (img) {
56717             dx = std::max(dx,img._width);
56718             dy+=img._height;
56719             dz = std::max(dz,img._depth);
56720             dc = std::max(dc,img._spectrum);
56721           }
56722         }
56723         res.assign(dx,dy,dz,dc,(T)0);
56724         if (res) cimglist_for(*this,l) {
56725             const CImg<T>& img = (*this)[l];
56726             if (img) res.draw_image((int)(align*(dx - img._width)),
56727                                     pos,
56728                                     (int)(align*(dz - img._depth)),
56729                                     (int)(align*(dc - img._spectrum)),
56730                                     img);
56731             pos+=img._height;
56732           }
56733       } break;
56734       case 'z' : { // Along the Z-axis.
56735         cimglist_for(*this,l) {
56736           const CImg<T>& img = (*this)[l];
56737           if (img) {
56738             dx = std::max(dx,img._width);
56739             dy = std::max(dy,img._height);
56740             dz+=img._depth;
56741             dc = std::max(dc,img._spectrum);
56742           }
56743         }
56744         res.assign(dx,dy,dz,dc,(T)0);
56745         if (res) cimglist_for(*this,l) {
56746             const CImg<T>& img = (*this)[l];
56747             if (img) res.draw_image((int)(align*(dx - img._width)),
56748                                     (int)(align*(dy - img._height)),
56749                                     pos,
56750                                     (int)(align*(dc - img._spectrum)),
56751                                     img);
56752             pos+=img._depth;
56753           }
56754       } break;
56755       default : { // Along the C-axis.
56756         cimglist_for(*this,l) {
56757           const CImg<T>& img = (*this)[l];
56758           if (img) {
56759             dx = std::max(dx,img._width);
56760             dy = std::max(dy,img._height);
56761             dz = std::max(dz,img._depth);
56762             dc+=img._spectrum;
56763           }
56764         }
56765         res.assign(dx,dy,dz,dc,(T)0);
56766         if (res) cimglist_for(*this,l) {
56767             const CImg<T>& img = (*this)[l];
56768             if (img) res.draw_image((int)(align*(dx - img._width)),
56769                                     (int)(align*(dy - img._height)),
56770                                     (int)(align*(dz - img._depth)),
56771                                     pos,
56772                                     img);
56773             pos+=img._spectrum;
56774           }
56775       }
56776       }
56777       return res;
56778     }
56779 
56780     //! Return a list where each image has been split along the specified axis.
56781     /**
56782         \param axis Axis to split images along.
56783         \param nb Number of spliting parts for each image.
56784     **/
56785     CImgList<T>& split(const char axis, const int nb=-1) {
56786       return get_split(axis,nb).move_to(*this);
56787     }
56788 
56789     //! Return a list where each image has been split along the specified axis \newinstance.
56790     CImgList<T> get_split(const char axis, const int nb=-1) const {
56791       CImgList<T> res;
56792       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
56793       return res;
56794     }
56795 
56796     //! Insert image at the end of the list.
56797     /**
56798       \param img Image to insert.
56799     **/
56800     template<typename t>
56801     CImgList<T>& push_back(const CImg<t>& img) {
56802       return insert(img);
56803     }
56804 
56805     //! Insert image at the front of the list.
56806     /**
56807       \param img Image to insert.
56808     **/
56809     template<typename t>
56810     CImgList<T>& push_front(const CImg<t>& img) {
56811       return insert(img,0);
56812     }
56813 
56814     //! Insert list at the end of the current list.
56815     /**
56816       \param list List to insert.
56817     **/
56818     template<typename t>
56819     CImgList<T>& push_back(const CImgList<t>& list) {
56820       return insert(list);
56821     }
56822 
56823     //! Insert list at the front of the current list.
56824     /**
56825       \param list List to insert.
56826     **/
56827     template<typename t>
56828     CImgList<T>& push_front(const CImgList<t>& list) {
56829       return insert(list,0);
56830     }
56831 
56832     //! Remove last image.
56833     /**
56834     **/
56835     CImgList<T>& pop_back() {
56836       return remove(_width - 1);
56837     }
56838 
56839     //! Remove first image.
56840     /**
56841     **/
56842     CImgList<T>& pop_front() {
56843       return remove(0);
56844     }
56845 
56846     //! Remove image pointed by iterator.
56847     /**
56848       \param iter Iterator pointing to the image to remove.
56849     **/
56850     CImgList<T>& erase(const iterator iter) {
56851       return remove(iter - _data);
56852     }
56853 
56854     //@}
56855     //----------------------------------
56856     //
56857     //! \name Data Input
56858     //@{
56859     //----------------------------------
56860 
56861     //! Display a simple interactive interface to select images or sublists.
56862     /**
56863        \param disp Window instance to display selection and user interface.
56864        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
56865        \param axis Axis along whom images are appended for visualization.
56866        \param align Alignment setting when images have not all the same size.
56867        \param exit_on_anykey Exit function when any key is pressed.
56868        \return A one-column vector containing the selected image indexes.
56869     **/
56870     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
56871                           const char axis='x', const float align=0,
56872                           const bool exit_on_anykey=false) const {
56873       return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
56874     }
56875 
56876     //! Display a simple interactive interface to select images or sublists.
56877     /**
56878        \param title Title of a new window used to display selection and user interface.
56879        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
56880        \param axis Axis along whom images are appended for visualization.
56881        \param align Alignment setting when images have not all the same size.
56882        \param exit_on_anykey Exit function when any key is pressed.
56883        \return A one-column vector containing the selected image indexes.
56884     **/
56885     CImg<intT> get_select(const char *const title, const bool feature_type=true,
56886                           const char axis='x', const float align=0,
56887                           const bool exit_on_anykey=false) const {
56888       CImgDisplay disp;
56889       return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
56890     }
56891 
56892     CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
56893                        const char axis, const float align, const bool exit_on_anykey,
56894                        const unsigned int orig, const bool resize_disp,
56895                        const bool exit_on_rightbutton, const bool exit_on_wheel) const {
56896       if (is_empty())
56897         throw CImgInstanceException(_cimglist_instance
56898                                     "select(): Empty instance.",
56899                                     cimglist_instance);
56900 
56901       // Create image correspondence table and get list dimensions for visualization.
56902       CImgList<uintT> _indices;
56903       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
56904       cimglist_for(*this,l) {
56905         const CImg<T>& img = _data[l];
56906         const unsigned int
56907           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
56908           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
56909         if (w>max_width) max_width = w;
56910         if (h>max_height) max_height = h;
56911         sum_width+=w; sum_height+=h;
56912         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
56913         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
56914       }
56915       const CImg<uintT> indices0 = _indices>'x';
56916 
56917       // Create display window.
56918       if (!disp) {
56919         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
56920         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
56921         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
56922       } else if (title) disp.set_title("%s",title);
56923       if (resize_disp) {
56924         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
56925         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
56926       }
56927 
56928       const unsigned int old_normalization = disp.normalization();
56929       bool old_is_resized = disp.is_resized();
56930       disp._normalization = 0;
56931       disp.show().set_key(0);
56932       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
56933 
56934       // Enter event loop.
56935       CImg<ucharT> visu0, visu;
56936       CImg<uintT> indices;
56937       CImg<intT> positions(_width,4,1,1,-1);
56938       int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
56939       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
56940       unsigned int key = 0;
56941 
56942       while (!is_selected && !disp.is_closed() && !key) {
56943 
56944         // Create background image.
56945         if (!visu0) {
56946           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
56947           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
56948           unsigned int ind = 0;
56949           const CImg<T> onexone(1,1,1,1,(T)0);
56950           if (axis=='x')
56951             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
56952             cimglist_for(*this,ind) {
56953               unsigned int x0 = 0;
56954               while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
56955               unsigned int x1 = x0;
56956               while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
56957               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
56958               CImg<ucharT> res;
56959               src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
56960                 move_to(res);
56961               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
56962               res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
56963               positions(ind,0) = positions(ind,2) = (int)x0;
56964               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
56965               positions(ind,2)+=res._width;
56966               positions(ind,3)+=res._height - 1;
56967               visu0.draw_image(positions(ind,0),positions(ind,1),res);
56968             }
56969           else
56970             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
56971             cimglist_for(*this,ind) {
56972               unsigned int y0 = 0;
56973               while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
56974               unsigned int y1 = y0;
56975               while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
56976               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
56977               CImg<ucharT> res;
56978               src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
56979                 move_to(res);
56980               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
56981               res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
56982               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
56983               positions(ind,1) = positions(ind,3) = (int)y0;
56984               positions(ind,2)+=res._width - 1;
56985               positions(ind,3)+=res._height;
56986               visu0.draw_image(positions(ind,0),positions(ind,1),res);
56987             }
56988           if (axis=='x') --positions(ind,2); else --positions(ind,3);
56989           update_display = true;
56990         }
56991 
56992         if (!visu || oindice0!=indice0 || oindice1!=indice1) {
56993           if (indice0>=0 && indice1>=0) {
56994             visu.assign(visu0,false);
56995             const int indm = std::min(indice0,indice1), indM = std::max(indice0,indice1);
56996             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
56997                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
56998                                     background_color,0.2f);
56999                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
57000                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
57001                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
57002                                       foreground_color,0.9f,0xAAAAAAAA);
57003               }
57004             const int yt = (int)text_down?visu.height() - 13:0;
57005             if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u",
57006                                            foreground_color,background_color,0.7f,13,
57007                                            orig + indm,orig + indM,indM - indm + 1);
57008             else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13,
57009                                 orig + indice0,
57010                                 _data[indice0]._width,
57011                                 _data[indice0]._height,
57012                                 _data[indice0]._depth,
57013                                 _data[indice0]._spectrum);
57014             update_display = true;
57015           } else visu.assign();
57016         }
57017         if (!visu) { visu.assign(visu0,true); update_display = true; }
57018         if (update_display) { visu.display(disp); update_display = false; }
57019         disp.wait();
57020 
57021         // Manage user events.
57022         const int xm = disp.mouse_x(), ym = disp.mouse_y();
57023         int indice = -1;
57024 
57025         if (xm>=0) {
57026           indice = (int)indices(axis=='x'?xm:ym);
57027           if (disp.button()&1) {
57028             if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
57029             oindice1 = indice1; indice1 = indice;
57030             if (!feature_type) is_selected = true;
57031           } else {
57032             if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
57033             else is_selected = true;
57034           }
57035         } else {
57036           if (is_clicked) {
57037             if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
57038             else indice1 = -1;
57039           } else indice0 = indice1 = -1;
57040         }
57041 
57042         if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
57043         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
57044         if (disp.wheel() && exit_on_wheel) is_selected = true;
57045 
57046         CImg<charT> filename(32);
57047         switch (key = disp.key()) {
57048 #if cimg_OS!=2
57049         case cimg::keyCTRLRIGHT :
57050 #endif
57051         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
57052         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57053             disp.set_fullscreen(false).
57054               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
57055                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
57056               _is_resized = true;
57057             disp.set_key(key,false); key = 0; visu0.assign();
57058           } break;
57059         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57060             disp.set_fullscreen(false).
57061               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
57062             disp.set_key(key,false); key = 0; visu0.assign();
57063           } break;
57064         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57065             disp.set_fullscreen(false).
57066               resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
57067               _is_resized = true;
57068             disp.set_key(key,false); key = 0; visu0.assign();
57069           } break;
57070         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57071             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
57072             disp.set_key(key,false); key = 0; visu0.assign();
57073           } break;
57074         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57075             static unsigned int snap_number = 0;
57076             std::FILE *file;
57077             do {
57078               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
57079               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
57080             } while (file);
57081             if (visu0) {
57082               (+visu0).draw_text(0,0," Saving snapshot... ",
57083                                  foreground_color,background_color,0.7f,13).display(disp);
57084               visu0.save(filename);
57085               (+visu0).draw_text(0,0," Snapshot '%s' saved. ",
57086                                  foreground_color,background_color,0.7f,13,filename._data).display(disp);
57087             }
57088             disp.set_key(key,false).wait(); key = 0;
57089           } break;
57090         case cimg::keyO :
57091           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57092             static unsigned int snap_number = 0;
57093             std::FILE *file;
57094             do {
57095 #ifdef cimg_use_zlib
57096               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
57097 #else
57098               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
57099 #endif
57100               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
57101             } while (file);
57102             (+visu0).draw_text(0,0," Saving instance... ",
57103                                foreground_color,background_color,0.7f,13).display(disp);
57104             save(filename);
57105             (+visu0).draw_text(0,0," Instance '%s' saved. ",
57106                                foreground_color,background_color,0.7f,13,filename._data).display(disp);
57107             disp.set_key(key,false).wait(); key = 0;
57108           } break;
57109         }
57110         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
57111         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
57112         else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
57113         if (!exit_on_anykey && key && key!=cimg::keyESC &&
57114             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
57115           key = 0;
57116         }
57117       }
57118       CImg<intT> res(1,2,1,1,-1);
57119       if (is_selected) {
57120         if (feature_type) res.fill(std::min(indice0,indice1),std::max(indice0,indice1));
57121         else res.fill(indice0);
57122       }
57123       if (!(disp.button()&2)) disp.set_button();
57124       disp._normalization = old_normalization;
57125       disp._is_resized = old_is_resized;
57126       disp.set_key(key);
57127       return res;
57128     }
57129 
57130     //! Load a list from a file.
57131     /**
57132      \param filename Filename to read data from.
57133     **/
57134     CImgList<T>& load(const char *const filename) {
57135       if (!filename)
57136         throw CImgArgumentException(_cimglist_instance
57137                                     "load(): Specified filename is (null).",
57138                                     cimglist_instance);
57139 
57140       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
57141         CImg<charT> filename_local(256);
57142         load(cimg::load_network(filename,filename_local));
57143         std::remove(filename_local);
57144         return *this;
57145       }
57146 
57147       const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
57148       const char *const ext = cimg::split_filename(filename);
57149       const unsigned int omode = cimg::exception_mode();
57150       cimg::exception_mode(0);
57151       bool is_loaded = true;
57152       try {
57153 #ifdef cimglist_load_plugin
57154         cimglist_load_plugin(filename);
57155 #endif
57156 #ifdef cimglist_load_plugin1
57157         cimglist_load_plugin1(filename);
57158 #endif
57159 #ifdef cimglist_load_plugin2
57160         cimglist_load_plugin2(filename);
57161 #endif
57162 #ifdef cimglist_load_plugin3
57163         cimglist_load_plugin3(filename);
57164 #endif
57165 #ifdef cimglist_load_plugin4
57166         cimglist_load_plugin4(filename);
57167 #endif
57168 #ifdef cimglist_load_plugin5
57169         cimglist_load_plugin5(filename);
57170 #endif
57171 #ifdef cimglist_load_plugin6
57172         cimglist_load_plugin6(filename);
57173 #endif
57174 #ifdef cimglist_load_plugin7
57175         cimglist_load_plugin7(filename);
57176 #endif
57177 #ifdef cimglist_load_plugin8
57178         cimglist_load_plugin8(filename);
57179 #endif
57180         if (!cimg::strcasecmp(ext,"tif") ||
57181             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
57182         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
57183         else if (!cimg::strcasecmp(ext,"cimg") ||
57184                  !cimg::strcasecmp(ext,"cimgz") ||
57185                  !*ext) load_cimg(filename);
57186         else if (!cimg::strcasecmp(ext,"rec") ||
57187                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
57188         else if (!cimg::strcasecmp(ext,"avi") ||
57189                  !cimg::strcasecmp(ext,"mov") ||
57190                  !cimg::strcasecmp(ext,"asf") ||
57191                  !cimg::strcasecmp(ext,"divx") ||
57192                  !cimg::strcasecmp(ext,"flv") ||
57193                  !cimg::strcasecmp(ext,"mpg") ||
57194                  !cimg::strcasecmp(ext,"m1v") ||
57195                  !cimg::strcasecmp(ext,"m2v") ||
57196                  !cimg::strcasecmp(ext,"m4v") ||
57197                  !cimg::strcasecmp(ext,"mjp") ||
57198                  !cimg::strcasecmp(ext,"mp4") ||
57199                  !cimg::strcasecmp(ext,"mkv") ||
57200                  !cimg::strcasecmp(ext,"mpe") ||
57201                  !cimg::strcasecmp(ext,"movie") ||
57202                  !cimg::strcasecmp(ext,"ogm") ||
57203                  !cimg::strcasecmp(ext,"ogg") ||
57204                  !cimg::strcasecmp(ext,"ogv") ||
57205                  !cimg::strcasecmp(ext,"qt") ||
57206                  !cimg::strcasecmp(ext,"rm") ||
57207                  !cimg::strcasecmp(ext,"vob") ||
57208                  !cimg::strcasecmp(ext,"wmv") ||
57209                  !cimg::strcasecmp(ext,"xvid") ||
57210                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
57211         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
57212         else is_loaded = false;
57213       } catch (CImgIOException&) { is_loaded = false; }
57214 
57215       // If nothing loaded, try to guess file format from magic number in file.
57216       if (!is_loaded && !is_stdin) {
57217         std::FILE *const file = std_fopen(filename,"rb");
57218         if (!file) {
57219           cimg::exception_mode(omode);
57220           throw CImgIOException(_cimglist_instance
57221                                 "load(): Failed to open file '%s'.",
57222                                 cimglist_instance,
57223                                 filename);
57224         }
57225 
57226         const char *const f_type = cimg::ftype(file,filename);
57227         std::fclose(file);
57228         is_loaded = true;
57229         try {
57230           if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
57231           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
57232           else is_loaded = false;
57233         } catch (CImgIOException&) { is_loaded = false; }
57234       }
57235 
57236       // If nothing loaded, try to load file as a single image.
57237       if (!is_loaded) {
57238         assign(1);
57239         try {
57240           _data->load(filename);
57241         } catch (CImgIOException&) {
57242           cimg::exception_mode(omode);
57243           throw CImgIOException(_cimglist_instance
57244                                 "load(): Failed to recognize format of file '%s'.",
57245                                 cimglist_instance,
57246                                 filename);
57247         }
57248       }
57249       cimg::exception_mode(omode);
57250       return *this;
57251     }
57252 
57253     //! Load a list from a file \newinstance.
57254     static CImgList<T> get_load(const char *const filename) {
57255       return CImgList<T>().load(filename);
57256     }
57257 
57258     //! Load a list from a .cimg file.
57259     /**
57260       \param filename Filename to read data from.
57261     **/
57262     CImgList<T>& load_cimg(const char *const filename) {
57263       return _load_cimg(0,filename);
57264     }
57265 
57266     //! Load a list from a .cimg file \newinstance.
57267     static CImgList<T> get_load_cimg(const char *const filename) {
57268       return CImgList<T>().load_cimg(filename);
57269     }
57270 
57271     //! Load a list from a .cimg file.
57272     /**
57273       \param file File to read data from.
57274     **/
57275     CImgList<T>& load_cimg(std::FILE *const file) {
57276       return _load_cimg(file,0);
57277     }
57278 
57279     //! Load a list from a .cimg file \newinstance.
57280     static CImgList<T> get_load_cimg(std::FILE *const file) {
57281       return CImgList<T>().load_cimg(file);
57282     }
57283 
57284     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
57285 #ifdef cimg_use_zlib
57286 #define _cimgz_load_cimg_case(Tss) { \
57287    Bytef *const cbuf = new Bytef[csiz]; \
57288    cimg::fread(cbuf,csiz,nfile); \
57289    raw.assign(W,H,D,C); \
57290    uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \
57291    uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
57292    delete[] cbuf; \
57293    if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
57294    raw.move_to(img); \
57295 }
57296 #else
57297 #define _cimgz_load_cimg_case(Tss) \
57298    throw CImgIOException(_cimglist_instance \
57299                          "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
57300                          cimglist_instance, \
57301                          filename?filename:"(FILE*)");
57302 #endif
57303 
57304 #define _cimg_load_cimg_case(Ts,Tss) \
57305       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
57306         for (unsigned int l = 0; l<N; ++l) { \
57307           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
57308           W = H = D = C = 0; csiz = 0; \
57309           if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \
57310             throw CImgIOException(_cimglist_instance \
57311                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
57312                                   cimglist_instance, \
57313                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
57314           if (W*H*D*C>0) { \
57315             CImg<Tss> raw; \
57316             CImg<T> &img = _data[l]; \
57317             if (err==5) _cimgz_load_cimg_case(Tss) \
57318             else { \
57319               img.assign(W,H,D,C); \
57320               T *ptrd = img._data; \
57321               for (ulongT to_read = img.size(); to_read; ) { \
57322                 raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
57323                 cimg::fread(raw._data,raw._width,nfile); \
57324                 if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
57325                 const Tss *ptrs = raw._data; \
57326                 for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
57327                 to_read-=raw._width; \
57328               } \
57329             } \
57330           } \
57331         } \
57332         loaded = true; \
57333       }
57334 
57335       if (!filename && !file)
57336         throw CImgArgumentException(_cimglist_instance
57337                                     "load_cimg(): Specified filename is (null).",
57338                                     cimglist_instance);
57339 
57340       const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
57341       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57342       bool loaded = false, endian = cimg::endianness();
57343       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
57344       *tmp = *str_pixeltype = *str_endian = 0;
57345       unsigned int j, N = 0, W, H, D, C;
57346       unsigned long csiz;
57347       int i, err;
57348       do {
57349         j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
57350       } while (*tmp=='#' && i>=0);
57351       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
57352                         &N,str_pixeltype._data,str_endian._data);
57353       if (err<2) {
57354         if (!file) cimg::fclose(nfile);
57355         throw CImgIOException(_cimglist_instance
57356                               "load_cimg(): CImg header not found in file '%s'.",
57357                               cimglist_instance,
57358                               filename?filename:"(FILE*)");
57359       }
57360       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
57361       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
57362       assign(N);
57363       _cimg_load_cimg_case("bool",bool);
57364       _cimg_load_cimg_case("unsigned_char",unsigned char);
57365       _cimg_load_cimg_case("uchar",unsigned char);
57366       _cimg_load_cimg_case("char",char);
57367       _cimg_load_cimg_case("unsigned_short",unsigned short);
57368       _cimg_load_cimg_case("ushort",unsigned short);
57369       _cimg_load_cimg_case("short",short);
57370       _cimg_load_cimg_case("unsigned_int",unsigned int);
57371       _cimg_load_cimg_case("uint",unsigned int);
57372       _cimg_load_cimg_case("int",int);
57373       _cimg_load_cimg_case("unsigned_long",ulongT);
57374       _cimg_load_cimg_case("ulong",ulongT);
57375       _cimg_load_cimg_case("long",longT);
57376       _cimg_load_cimg_case("unsigned_int64",uint64T);
57377       _cimg_load_cimg_case("uint64",uint64T);
57378       _cimg_load_cimg_case("int64",int64T);
57379       _cimg_load_cimg_case("float",float);
57380       _cimg_load_cimg_case("double",double);
57381 
57382       if (!loaded) {
57383         if (!file) cimg::fclose(nfile);
57384         throw CImgIOException(_cimglist_instance
57385                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
57386                               cimglist_instance,
57387                               str_pixeltype._data,filename?filename:"(FILE*)");
57388       }
57389       if (!file) cimg::fclose(nfile);
57390       return *this;
57391     }
57392 
57393     //! Load a sublist list from a (non compressed) .cimg file.
57394     /**
57395       \param filename Filename to read data from.
57396       \param n0 Starting index of images to read (~0U for max).
57397       \param n1 Ending index of images to read (~0U for max).
57398       \param x0 Starting X-coordinates of image regions to read.
57399       \param y0 Starting Y-coordinates of image regions to read.
57400       \param z0 Starting Z-coordinates of image regions to read.
57401       \param c0 Starting C-coordinates of image regions to read.
57402       \param x1 Ending X-coordinates of image regions to read (~0U for max).
57403       \param y1 Ending Y-coordinates of image regions to read (~0U for max).
57404       \param z1 Ending Z-coordinates of image regions to read (~0U for max).
57405       \param c1 Ending C-coordinates of image regions to read (~0U for max).
57406     **/
57407     CImgList<T>& load_cimg(const char *const filename,
57408                            const unsigned int n0, const unsigned int n1,
57409                            const unsigned int x0, const unsigned int y0,
57410                            const unsigned int z0, const unsigned int c0,
57411                            const unsigned int x1, const unsigned int y1,
57412                            const unsigned int z1, const unsigned int c1) {
57413       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57414     }
57415 
57416     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
57417     static CImgList<T> get_load_cimg(const char *const filename,
57418                                      const unsigned int n0, const unsigned int n1,
57419                                      const unsigned int x0, const unsigned int y0,
57420                                      const unsigned int z0, const unsigned int c0,
57421                                      const unsigned int x1, const unsigned int y1,
57422                                      const unsigned int z1, const unsigned int c1) {
57423       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57424     }
57425 
57426     //! Load a sub-image list from a (non compressed) .cimg file \overloading.
57427     CImgList<T>& load_cimg(std::FILE *const file,
57428                            const unsigned int n0, const unsigned int n1,
57429                            const unsigned int x0, const unsigned int y0,
57430                            const unsigned int z0, const unsigned int c0,
57431                            const unsigned int x1, const unsigned int y1,
57432                            const unsigned int z1, const unsigned int c1) {
57433       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57434     }
57435 
57436     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
57437     static CImgList<T> get_load_cimg(std::FILE *const file,
57438                                      const unsigned int n0, const unsigned int n1,
57439                                      const unsigned int x0, const unsigned int y0,
57440                                      const unsigned int z0, const unsigned int c0,
57441                                      const unsigned int x1, const unsigned int y1,
57442                                      const unsigned int z1, const unsigned int c1) {
57443       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57444     }
57445 
57446     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
57447 			    const unsigned int n0, const unsigned int n1,
57448 			    const unsigned int x0, const unsigned int y0,
57449                             const unsigned int z0, const unsigned int c0,
57450 			    const unsigned int x1, const unsigned int y1,
57451                             const unsigned int z1, const unsigned int c1) {
57452 #define _cimg_load_cimg_case2(Ts,Tss) \
57453       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
57454         for (unsigned int l = 0; l<=nn1; ++l) { \
57455           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
57456           W = H = D = C = 0; \
57457           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
57458             throw CImgIOException(_cimglist_instance \
57459                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
57460                                   cimglist_instance, \
57461                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
57462           if (W*H*D*C>0) { \
57463             if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
57464             else { \
57465               const unsigned int \
57466                 _nx1 = nx1==~0U?W - 1:nx1, \
57467                 _ny1 = ny1==~0U?H - 1:ny1, \
57468                 _nz1 = nz1==~0U?D - 1:nz1, \
57469                 _nc1 = nc1==~0U?C - 1:nc1; \
57470               if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
57471                 throw CImgArgumentException(_cimglist_instance \
57472                                             "load_cimg(): Invalid specified coordinates " \
57473                                             "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
57474                                             "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
57475                                             cimglist_instance, \
57476                                             n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
57477               CImg<Tss> raw(1 + _nx1 - nx0); \
57478               CImg<T> &img = _data[l - nn0]; \
57479               img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
57480               T *ptrd = img._data; \
57481               ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
57482               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
57483               for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
57484                 const ulongT skipzb = nz0*W*H*sizeof(Tss); \
57485                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
57486                 for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
57487                   const ulongT skipyb = ny0*W*sizeof(Tss); \
57488                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
57489                   for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
57490                     const ulongT skipxb = nx0*sizeof(Tss); \
57491                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
57492                     cimg::fread(raw._data,raw._width,nfile); \
57493                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
57494                     const Tss *ptrs = raw._data; \
57495                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
57496                     const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
57497                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
57498                   } \
57499                   const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
57500                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
57501                 } \
57502                 const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
57503                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
57504               } \
57505               const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
57506               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
57507             } \
57508           } \
57509         } \
57510         loaded = true; \
57511       }
57512 
57513       if (!filename && !file)
57514         throw CImgArgumentException(_cimglist_instance
57515                                     "load_cimg(): Specified filename is (null).",
57516                                     cimglist_instance);
57517       unsigned int
57518         nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
57519         nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
57520         ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
57521         nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
57522         nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
57523 
57524       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57525       bool loaded = false, endian = cimg::endianness();
57526       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
57527       *tmp = *str_pixeltype = *str_endian = 0;
57528       unsigned int j, N, W, H, D, C;
57529       int i, err;
57530       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
57531       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
57532                         &N,str_pixeltype._data,str_endian._data);
57533       if (err<2) {
57534         if (!file) cimg::fclose(nfile);
57535         throw CImgIOException(_cimglist_instance
57536                               "load_cimg(): CImg header not found in file '%s'.",
57537                               cimglist_instance,
57538                               filename?filename:"(FILE*)");
57539       }
57540       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
57541       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
57542       nn1 = n1==~0U?N - 1:n1;
57543       if (nn1>=N)
57544         throw CImgArgumentException(_cimglist_instance
57545                                     "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
57546                                     "because file '%s' contains only %u images.",
57547                                     cimglist_instance,
57548                                     n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
57549       assign(1 + nn1 - n0);
57550       _cimg_load_cimg_case2("bool",bool);
57551       _cimg_load_cimg_case2("unsigned_char",unsigned char);
57552       _cimg_load_cimg_case2("uchar",unsigned char);
57553       _cimg_load_cimg_case2("char",char);
57554       _cimg_load_cimg_case2("unsigned_short",unsigned short);
57555       _cimg_load_cimg_case2("ushort",unsigned short);
57556       _cimg_load_cimg_case2("short",short);
57557       _cimg_load_cimg_case2("unsigned_int",unsigned int);
57558       _cimg_load_cimg_case2("uint",unsigned int);
57559       _cimg_load_cimg_case2("int",int);
57560       _cimg_load_cimg_case2("unsigned_long",ulongT);
57561       _cimg_load_cimg_case2("ulong",ulongT);
57562       _cimg_load_cimg_case2("long",longT);
57563       _cimg_load_cimg_case2("unsigned_int64",uint64T);
57564       _cimg_load_cimg_case2("uint64",uint64T);
57565       _cimg_load_cimg_case2("int64",int64T);
57566       _cimg_load_cimg_case2("float",float);
57567       _cimg_load_cimg_case2("double",double);
57568       if (!loaded) {
57569         if (!file) cimg::fclose(nfile);
57570         throw CImgIOException(_cimglist_instance
57571                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
57572                               cimglist_instance,
57573                               str_pixeltype._data,filename?filename:"(FILE*)");
57574       }
57575       if (!file) cimg::fclose(nfile);
57576       return *this;
57577     }
57578 
57579     //! Load a list from a PAR/REC (Philips) file.
57580     /**
57581       \param filename Filename to read data from.
57582     **/
57583     CImgList<T>& load_parrec(const char *const filename) {
57584       if (!filename)
57585         throw CImgArgumentException(_cimglist_instance
57586                                     "load_parrec(): Specified filename is (null).",
57587                                     cimglist_instance);
57588 
57589       CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
57590       *body = *filenamepar = *filenamerec = 0;
57591       const char *const ext = cimg::split_filename(filename,body);
57592       if (!std::strcmp(ext,"par")) {
57593         std::strncpy(filenamepar,filename,filenamepar._width - 1);
57594         cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
57595       }
57596       if (!std::strcmp(ext,"PAR")) {
57597         std::strncpy(filenamepar,filename,filenamepar._width - 1);
57598         cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
57599       }
57600       if (!std::strcmp(ext,"rec")) {
57601         std::strncpy(filenamerec,filename,filenamerec._width - 1);
57602         cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
57603       }
57604       if (!std::strcmp(ext,"REC")) {
57605         std::strncpy(filenamerec,filename,filenamerec._width - 1);
57606         cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
57607       }
57608       std::FILE *file = cimg::fopen(filenamepar,"r");
57609 
57610       // Parse header file
57611       CImgList<floatT> st_slices;
57612       CImgList<uintT> st_global;
57613       CImg<charT> line(256); *line = 0;
57614       int err;
57615       do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
57616       do {
57617         unsigned int sn,size_x,size_y,pixsize;
57618         float rs,ri,ss;
57619         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);
57620         if (err==7) {
57621           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
57622           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
57623           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
57624           else {
57625             CImg<uintT> &vec = st_global[i];
57626             if (size_x>vec[0]) vec[0] = size_x;
57627             if (size_y>vec[1]) vec[1] = size_y;
57628             vec[2] = sn;
57629           }
57630           st_slices[st_slices._width - 1][7] = (float)i;
57631         }
57632       } while (err==7);
57633 
57634       // Read data
57635       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
57636       cimglist_for(st_global,l) {
57637         const CImg<uintT>& vec = st_global[l];
57638         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
57639       }
57640 
57641       cimglist_for(st_slices,l) {
57642         const CImg<floatT>& vec = st_slices[l];
57643         const unsigned int
57644           sn = (unsigned int)vec[0] - 1,
57645           pixsize = (unsigned int)vec[1],
57646           size_x = (unsigned int)vec[2],
57647           size_y = (unsigned int)vec[3],
57648           imn = (unsigned int)vec[7];
57649         const float ri = vec[4], rs = vec[5], ss = vec[6];
57650         switch (pixsize) {
57651         case 8 : {
57652           CImg<ucharT> buf(size_x,size_y);
57653           cimg::fread(buf._data,size_x*size_y,file2);
57654           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57655           CImg<T>& img = (*this)[imn];
57656           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57657         } break;
57658         case 16 : {
57659           CImg<ushortT> buf(size_x,size_y);
57660           cimg::fread(buf._data,size_x*size_y,file2);
57661           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57662           CImg<T>& img = (*this)[imn];
57663           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57664         } break;
57665         case 32 : {
57666           CImg<uintT> buf(size_x,size_y);
57667           cimg::fread(buf._data,size_x*size_y,file2);
57668           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57669           CImg<T>& img = (*this)[imn];
57670           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57671         } break;
57672         default :
57673           cimg::fclose(file);
57674           cimg::fclose(file2);
57675           throw CImgIOException(_cimglist_instance
57676                                 "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
57677                                 cimglist_instance,
57678                                 pixsize,filename);
57679         }
57680       }
57681       cimg::fclose(file);
57682       cimg::fclose(file2);
57683       if (!_width)
57684         throw CImgIOException(_cimglist_instance
57685                               "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
57686                               cimglist_instance,
57687                               filename);
57688       return *this;
57689     }
57690 
57691     //! Load a list from a PAR/REC (Philips) file \newinstance.
57692     static CImgList<T> get_load_parrec(const char *const filename) {
57693       return CImgList<T>().load_parrec(filename);
57694     }
57695 
57696     //! Load a list from a YUV image sequence file.
57697     /**
57698         \param filename Filename to read data from.
57699         \param size_x Width of the images.
57700         \param size_y Height of the images.
57701         \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
57702         \param first_frame Index of first image frame to read.
57703         \param last_frame Index of last image frame to read.
57704         \param step_frame Step applied between each frame.
57705         \param yuv2rgb Apply YUV to RGB transformation during reading.
57706     **/
57707     CImgList<T>& load_yuv(const char *const filename,
57708                           const unsigned int size_x, const unsigned int size_y,
57709                           const unsigned int chroma_subsampling=444,
57710                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57711                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
57712       return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
57713                        first_frame,last_frame,step_frame,yuv2rgb);
57714     }
57715 
57716     //! Load a list from a YUV image sequence file \newinstance.
57717     static CImgList<T> get_load_yuv(const char *const filename,
57718                                     const unsigned int size_x, const unsigned int size_y=1,
57719                                     const unsigned int chroma_subsampling=444,
57720                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57721                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
57722       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
57723                                     first_frame,last_frame,step_frame,yuv2rgb);
57724     }
57725 
57726     //! Load a list from an image sequence YUV file \overloading.
57727     CImgList<T>& load_yuv(std::FILE *const file,
57728                           const unsigned int size_x, const unsigned int size_y,
57729                           const unsigned int chroma_subsampling=444,
57730                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57731                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
57732       return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
57733                        first_frame,last_frame,step_frame,yuv2rgb);
57734     }
57735 
57736     //! Load a list from an image sequence YUV file \newinstance.
57737     static CImgList<T> get_load_yuv(std::FILE *const file,
57738                                     const unsigned int size_x, const unsigned int size_y=1,
57739                                     const unsigned int chroma_subsampling=444,
57740                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57741                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
57742       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
57743                                     first_frame,last_frame,step_frame,yuv2rgb);
57744     }
57745 
57746     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
57747 			   const unsigned int size_x, const unsigned int size_y,
57748                            const unsigned int chroma_subsampling,
57749 			   const unsigned int first_frame, const unsigned int last_frame,
57750 			   const unsigned int step_frame, const bool yuv2rgb) {
57751       if (!filename && !file)
57752         throw CImgArgumentException(_cimglist_instance
57753                                     "load_yuv(): Specified filename is (null).",
57754                                     cimglist_instance);
57755       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
57756         throw CImgArgumentException(_cimglist_instance
57757                                     "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.",
57758                                     cimglist_instance,
57759                                     chroma_subsampling,filename?filename:"(FILE*)");
57760       const unsigned int
57761         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
57762         cfy = chroma_subsampling==420?2:1,
57763 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
57764 	nlast_frame = first_frame<last_frame?last_frame:first_frame,
57765 	nstep_frame = step_frame?step_frame:1;
57766 
57767       if (!size_x || !size_y || size_x%cfx || size_y%cfy)
57768         throw CImgArgumentException(_cimglist_instance
57769                                     "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
57770                                     cimglist_instance,
57771                                     size_x,size_y,filename?filename:"(FILE*)");
57772 
57773       CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
57774       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57775       bool stop_flag = false;
57776       int err;
57777       if (nfirst_frame) {
57778         err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
57779         if (err) {
57780           if (!file) cimg::fclose(nfile);
57781           throw CImgIOException(_cimglist_instance
57782                                 "load_yuv(): File '%s' doesn't contain frame number %u.",
57783                                 cimglist_instance,
57784                                 filename?filename:"(FILE*)",nfirst_frame);
57785         }
57786       }
57787       unsigned int frame;
57788       for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
57789         YUV.get_shared_channel(0).fill(0);
57790         // *TRY* to read the luminance part, do not replace by cimg::fread!
57791         err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
57792         if (err!=(int)(YUV._width*YUV._height)) {
57793           stop_flag = true;
57794           if (err>0)
57795             cimg::warn(_cimglist_instance
57796                        "load_yuv(): File '%s' contains incomplete data or given image dimensions "
57797                        "(%u,%u) are incorrect.",
57798                        cimglist_instance,
57799                        filename?filename:"(FILE*)",size_x,size_y);
57800         } else {
57801           UV.fill(0);
57802           // *TRY* to read the luminance part, do not replace by cimg::fread!
57803           err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
57804           if (err!=(int)(UV.size())) {
57805             stop_flag = true;
57806             if (err>0)
57807               cimg::warn(_cimglist_instance
57808                          "load_yuv(): File '%s' contains incomplete data or given image dimensions "
57809                          "(%u,%u) are incorrect.",
57810                          cimglist_instance,
57811                          filename?filename:"(FILE*)",size_x,size_y);
57812           } else {
57813             const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
57814             ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
57815             const unsigned int wd = YUV._width;
57816             switch (chroma_subsampling) {
57817             case 420 :
57818               cimg_forY(UV,y) {
57819                 cimg_forX(UV,x) {
57820                   const ucharT U = *(ptrs1++), V = *(ptrs2++);
57821                   ptrd1[wd] = U; *(ptrd1)++ = U;
57822                   ptrd1[wd] = U; *(ptrd1)++ = U;
57823                   ptrd2[wd] = V; *(ptrd2)++ = V;
57824                   ptrd2[wd] = V; *(ptrd2)++ = V;
57825                 }
57826                 ptrd1+=wd; ptrd2+=wd;
57827               }
57828               break;
57829             case 422 :
57830               cimg_forXY(UV,x,y) {
57831                 const ucharT U = *(ptrs1++), V = *(ptrs2++);
57832                 *(ptrd1++) = U; *(ptrd1++) = U;
57833                 *(ptrd2++) = V; *(ptrd2++) = V;
57834               }
57835               break;
57836             default :
57837               YUV.draw_image(0,0,0,1,UV);
57838             }
57839             if (yuv2rgb) YUV.YCbCrtoRGB();
57840             insert(YUV);
57841             if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
57842           }
57843         }
57844       }
57845       if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
57846         cimg::warn(_cimglist_instance
57847                    "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
57848                    cimglist_instance,
57849                    nlast_frame,frame - 1,filename?filename:"(FILE*)");
57850 
57851       if (!file) cimg::fclose(nfile);
57852       return *this;
57853     }
57854 
57855     //! Load an image from a video file, using OpenCV library.
57856     /**
57857       \param filename Filename, as a C-string.
57858       \param first_frame Index of the first frame to read.
57859       \param last_frame Index of the last frame to read.
57860       \param step_frame Step value for frame reading.
57861       \note If step_frame==0, the current video stream is forced to be released (without any frames read).
57862     **/
57863     CImgList<T>& load_video(const char *const filename,
57864                             const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57865                             const unsigned int step_frame=1) {
57866 #ifndef cimg_use_opencv
57867       if (first_frame || last_frame!=~0U || step_frame>1)
57868         throw CImgArgumentException(_cimglist_instance
57869                                     "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
57870                                     "and 'step_frame' can be only set when using OpenCV "
57871                                     "(-Dcimg_use_opencv must be enabled).",
57872                                     cimglist_instance,filename);
57873       return load_ffmpeg_external(filename);
57874 #else
57875       static CvCapture *captures[32] = { 0 };
57876       static CImgList<charT> filenames(32);
57877       static CImg<uintT> positions(32,1,1,1,0);
57878       static int last_used_index = -1;
57879 
57880       // Detect if a video capture already exists for the specified filename.
57881       cimg::mutex(9);
57882       int index = -1;
57883       if (filename) {
57884         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
57885           index = last_used_index;
57886         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
57887             index = l; break;
57888           }
57889       } else index = last_used_index;
57890       cimg::mutex(9,0);
57891 
57892       // Release stream if needed.
57893       if (!step_frame || (index>=0 && positions[index]>first_frame)) {
57894         if (index>=0) {
57895           cimg::mutex(9);
57896           cvReleaseCapture(&captures[index]);
57897           captures[index] = 0; filenames[index].assign(); positions[index] = 0;
57898           if (last_used_index==index) last_used_index = -1;
57899           index = -1;
57900           cimg::mutex(9,0);
57901         } else
57902           if (filename)
57903             cimg::warn(_cimglist_instance
57904                        "load_video() : File '%s', no opened video stream associated with filename found.",
57905                        cimglist_instance,filename);
57906           else
57907             cimg::warn(_cimglist_instance
57908                        "load_video() : No opened video stream found.",
57909                        cimglist_instance,filename);
57910         if (!step_frame) return *this;
57911       }
57912 
57913       // Find empty slot for capturing video stream.
57914       if (index<0) {
57915         if (!filename)
57916           throw CImgArgumentException(_cimglist_instance
57917                                       "load_video(): No already open video reader found. You must specify a "
57918                                       "non-(null) filename argument for the first call.",
57919                                       cimglist_instance);
57920         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
57921         if (index<0)
57922           throw CImgIOException(_cimglist_instance
57923                                 "load_video(): File '%s', no video reader slots available. "
57924                                 "You have to release some of your previously opened videos.",
57925                                 cimglist_instance,filename);
57926         cimg::mutex(9);
57927         captures[index] = cvCaptureFromFile(filename);
57928         CImg<charT>::string(filename).move_to(filenames[index]);
57929         positions[index] = 0;
57930         cimg::mutex(9,0);
57931         if (!captures[index]) {
57932           filenames[index].assign();
57933           std::fclose(cimg::fopen(filename,"rb"));  // Check file availability.
57934           throw CImgIOException(_cimglist_instance
57935                                 "load_video(): File '%s', unable to detect format of video file.",
57936                                 cimglist_instance,filename);
57937         }
57938       }
57939 
57940       cimg::mutex(9);
57941       const unsigned int nb_frames = (unsigned int)std::max(0.,cvGetCaptureProperty(captures[index],
57942                                                                                      CV_CAP_PROP_FRAME_COUNT));
57943       cimg::mutex(9,0);
57944       assign();
57945 
57946       // Skip frames if necessary.
57947       bool go_on = true;
57948       unsigned int &pos = positions[index];
57949       while (pos<first_frame) {
57950         cimg::mutex(9);
57951         if (!cvGrabFrame(captures[index])) { cimg::mutex(9,0); go_on = false; break; }
57952         cimg::mutex(9,0);
57953         ++pos;
57954       }
57955 
57956       // Read and convert frames.
57957       const IplImage *src = 0;
57958       if (go_on) {
57959         const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
57960         while (pos<=_last_frame) {
57961           cimg::mutex(9);
57962           src = cvQueryFrame(captures[index]);
57963           if (src) {
57964             CImg<T> frame(src->width,src->height,1,3);
57965             const int step = (int)(src->widthStep - 3*src->width);
57966             const unsigned char* ptrs = (unsigned char*)src->imageData;
57967             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);
57968             if (step>0) cimg_forY(frame,y) {
57969                 cimg_forX(frame,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
57970                 ptrs+=step;
57971               } else for (ulongT siz = (ulongT)src->width*src->height; siz; --siz) {
57972                 *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
57973               }
57974             frame.move_to(*this);
57975             ++pos;
57976 
57977             bool skip_failed = false;
57978             for (unsigned int i = 1; i<step_frame && pos<=_last_frame; ++i, ++pos)
57979               if (!cvGrabFrame(captures[index])) { skip_failed = true; break; }
57980             if (skip_failed) src = 0;
57981           }
57982           cimg::mutex(9,0);
57983           if (!src) break;
57984         }
57985       }
57986 
57987       if (!src || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary.
57988         cimg::mutex(9);
57989         cvReleaseCapture(&captures[index]);
57990         captures[index] = 0;
57991         filenames[index].assign();
57992         positions[index] = 0;
57993         index = -1;
57994         cimg::mutex(9,0);
57995       }
57996 
57997       cimg::mutex(9);
57998       last_used_index = index;
57999       cimg::mutex(9,0);
58000 
58001       if (is_empty())
58002         throw CImgIOException(_cimglist_instance
58003                               "load_video(): File '%s', unable to locate frame %u.",
58004                               cimglist_instance,filename,first_frame);
58005       return *this;
58006 #endif
58007     }
58008 
58009     //! Load an image from a video file, using OpenCV library \newinstance.
58010     static CImgList<T> get_load_video(const char *const filename,
58011                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58012                            const unsigned int step_frame=1) {
58013       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
58014     }
58015 
58016     //! Load an image from a video file using the external tool 'ffmpeg'.
58017     /**
58018       \param filename Filename to read data from.
58019     **/
58020     CImgList<T>& load_ffmpeg_external(const char *const filename) {
58021       if (!filename)
58022         throw CImgArgumentException(_cimglist_instance
58023                                     "load_ffmpeg_external(): Specified filename is (null).",
58024                                     cimglist_instance);
58025       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58026       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58027       std::FILE *file = 0;
58028       do {
58029         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58030                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58031         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
58032         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58033       } while (file);
58034       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
58035       cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\"",
58036                     cimg::ffmpeg_path(),
58037                     CImg<charT>::string(filename)._system_strescape().data(),
58038                     CImg<charT>::string(filename_tmp2)._system_strescape().data());
58039       cimg::system(command,0);
58040       const unsigned int omode = cimg::exception_mode();
58041       cimg::exception_mode(0);
58042       assign();
58043       unsigned int i = 1;
58044       for (bool stop_flag = false; !stop_flag; ++i) {
58045         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
58046         CImg<T> img;
58047         try { img.load_pnm(filename_tmp2); }
58048         catch (CImgException&) { stop_flag = true; }
58049         if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58050       }
58051       cimg::exception_mode(omode);
58052       if (is_empty())
58053         throw CImgIOException(_cimglist_instance
58054                               "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
58055                               cimglist_instance,
58056                               filename);
58057       return *this;
58058     }
58059 
58060     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
58061     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
58062       return CImgList<T>().load_ffmpeg_external(filename);
58063     }
58064 
58065     //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
58066     /**
58067       \param filename Filename to read data from.
58068     **/
58069     CImgList<T>& load_gif_external(const char *const filename) {
58070       if (!filename)
58071         throw CImgArgumentException(_cimglist_instance
58072                                     "load_gif_external(): Specified filename is (null).",
58073                                     cimglist_instance);
58074       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58075       if (!_load_gif_external(filename,false))
58076         if (!_load_gif_external(filename,true))
58077           try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
58078       if (is_empty())
58079         throw CImgIOException(_cimglist_instance
58080                               "load_gif_external(): Failed to open file '%s'.",
58081                               cimglist_instance,filename);
58082       return *this;
58083     }
58084 
58085     CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
58086       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58087       std::FILE *file = 0;
58088       do {
58089         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58090                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58091         if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
58092         else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
58093         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58094       } while (file);
58095       if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
58096                                             cimg::graphicsmagick_path(),
58097                                             CImg<charT>::string(filename)._system_strescape().data(),
58098                                             CImg<charT>::string(filename_tmp)._system_strescape().data());
58099       else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\"",
58100                          cimg::imagemagick_path(),
58101                          CImg<charT>::string(filename)._system_strescape().data(),
58102                          CImg<charT>::string(filename_tmp)._system_strescape().data());
58103       cimg::system(command,0);
58104       const unsigned int omode = cimg::exception_mode();
58105       cimg::exception_mode(0);
58106       assign();
58107 
58108       // Try to read a single frame gif.
58109       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
58110       CImg<T> img;
58111       try { img.load_png(filename_tmp2); }
58112       catch (CImgException&) { }
58113       if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58114       else { // Try to read animated gif.
58115         unsigned int i = 0;
58116         for (bool stop_flag = false; !stop_flag; ++i) {
58117           if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
58118           else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
58119           CImg<T> img;
58120           try { img.load_png(filename_tmp2); }
58121           catch (CImgException&) { stop_flag = true; }
58122           if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58123         }
58124       }
58125       cimg::exception_mode(omode);
58126       return *this;
58127     }
58128 
58129     //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
58130     static CImgList<T> get_load_gif_external(const char *const filename) {
58131       return CImgList<T>().load_gif_external(filename);
58132     }
58133 
58134     //! Load a gzipped list, using external tool 'gunzip'.
58135     /**
58136       \param filename Filename to read data from.
58137     **/
58138     CImgList<T>& load_gzip_external(const char *const filename) {
58139       if (!filename)
58140         throw CImgIOException(_cimglist_instance
58141                               "load_gzip_external(): Specified filename is (null).",
58142                               cimglist_instance);
58143       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58144       CImg<charT> command(1024), filename_tmp(256), body(256);
58145       const char
58146         *ext = cimg::split_filename(filename,body),
58147         *ext2 = cimg::split_filename(body,0);
58148       std::FILE *file = 0;
58149       do {
58150         if (!cimg::strcasecmp(ext,"gz")) {
58151           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58152                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
58153           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58154                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58155         } else {
58156           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58157                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
58158           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58159                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58160         }
58161         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58162       } while (file);
58163       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
58164                     cimg::gunzip_path(),
58165                     CImg<charT>::string(filename)._system_strescape().data(),
58166                     CImg<charT>::string(filename_tmp)._system_strescape().data());
58167       cimg::system(command);
58168       if (!(file = std_fopen(filename_tmp,"rb"))) {
58169         cimg::fclose(cimg::fopen(filename,"r"));
58170         throw CImgIOException(_cimglist_instance
58171                               "load_gzip_external(): Failed to open file '%s'.",
58172                               cimglist_instance,
58173                               filename);
58174 
58175       } else cimg::fclose(file);
58176       load(filename_tmp);
58177       std::remove(filename_tmp);
58178       return *this;
58179     }
58180 
58181     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
58182     static CImgList<T> get_load_gzip_external(const char *const filename) {
58183       return CImgList<T>().load_gzip_external(filename);
58184     }
58185 
58186     //! Load a 3d object from a .OFF file.
58187     /**
58188       \param filename Filename to read data from.
58189       \param[out] primitives At return, contains the list of 3d object primitives.
58190       \param[out] colors At return, contains the list of 3d object colors.
58191       \return List of 3d object vertices.
58192     **/
58193     template<typename tf, typename tc>
58194     CImgList<T>& load_off(const char *const filename,
58195 			  CImgList<tf>& primitives, CImgList<tc>& colors) {
58196       return get_load_off(filename,primitives,colors).move_to(*this);
58197     }
58198 
58199     //! Load a 3d object from a .OFF file \newinstance.
58200     template<typename tf, typename tc>
58201       static CImgList<T> get_load_off(const char *const filename,
58202                                       CImgList<tf>& primitives, CImgList<tc>& colors) {
58203       return CImg<T>().load_off(filename,primitives,colors)<'x';
58204     }
58205 
58206     //! Load images from a TIFF file.
58207     /**
58208         \param filename Filename to read data from.
58209         \param first_frame Index of first image frame to read.
58210         \param last_frame Index of last image frame to read.
58211         \param step_frame Step applied between each frame.
58212         \param[out] voxel_size Voxel size, as stored in the filename.
58213         \param[out] description Description, as stored in the filename.
58214     **/
58215     CImgList<T>& load_tiff(const char *const filename,
58216 			   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58217 			   const unsigned int step_frame=1,
58218                            float *const voxel_size=0,
58219                            CImg<charT> *const description=0) {
58220       const unsigned int
58221 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
58222 	nstep_frame = step_frame?step_frame:1;
58223       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
58224 #ifndef cimg_use_tiff
58225       cimg::unused(voxel_size,description);
58226       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
58227         throw CImgArgumentException(_cimglist_instance
58228                                     "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
58229                                     cimglist_instance,
58230                                     filename);
58231 
58232       return assign(CImg<T>::get_load_tiff(filename));
58233 #else
58234 #if cimg_verbosity<3
58235         TIFFSetWarningHandler(0);
58236         TIFFSetErrorHandler(0);
58237 #endif
58238       TIFF *tif = TIFFOpen(filename,"r");
58239       if (tif) {
58240         unsigned int nb_images = 0;
58241         do ++nb_images; while (TIFFReadDirectory(tif));
58242         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
58243           cimg::warn(_cimglist_instance
58244                      "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
58245                      "file '%s' contains %u image(s).",
58246                      cimglist_instance,
58247                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
58248 
58249         if (nfirst_frame>=nb_images) return assign();
58250         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
58251         assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
58252         TIFFSetDirectory(tif,0);
58253         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description);
58254         TIFFClose(tif);
58255       } else throw CImgIOException(_cimglist_instance
58256                                    "load_tiff(): Failed to open file '%s'.",
58257                                    cimglist_instance,
58258                                    filename);
58259       return *this;
58260 #endif
58261     }
58262 
58263     //! Load a multi-page TIFF file \newinstance.
58264     static CImgList<T> get_load_tiff(const char *const filename,
58265 				     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58266 				     const unsigned int step_frame=1,
58267                                      float *const voxel_size=0,
58268                                      CImg<charT> *const description=0) {
58269       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
58270     }
58271 
58272     //@}
58273     //----------------------------------
58274     //
58275     //! \name Data Output
58276     //@{
58277     //----------------------------------
58278 
58279     //! Print information about the list on the standard output.
58280     /**
58281       \param title Label set to the information displayed.
58282       \param display_stats Tells if image statistics must be computed and displayed.
58283     **/
58284     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
58285       unsigned int msiz = 0;
58286       cimglist_for(*this,l) msiz+=_data[l].size();
58287       msiz*=sizeof(T);
58288       const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
58289       CImg<charT> _title(64);
58290       if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
58291       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
58292                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
58293                    cimg::t_bold,cimg::t_normal,(void*)this,
58294                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
58295                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
58296                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
58297                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
58298       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
58299       else std::fprintf(cimg::output(),".\n");
58300 
58301       char tmp[16] = { 0 };
58302       cimglist_for(*this,ll) {
58303         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
58304         std::fprintf(cimg::output(),"  ");
58305         _data[ll].print(tmp,display_stats);
58306         if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output(),"  ...\n"); }
58307       }
58308       std::fflush(cimg::output());
58309       return *this;
58310     }
58311 
58312     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
58313     /**
58314        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
58315        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
58316        \param align Appending alignmenet.
58317        \note This function displays the list images of the current CImgList instance into an existing
58318          CImgDisplay window.
58319        Images of the list are appended in a single temporarly image for visualization purposes.
58320        The function returns immediately.
58321     **/
58322     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
58323       disp.display(*this,axis,align);
58324       return *this;
58325     }
58326 
58327     //! Display the current CImgList instance in a new display window.
58328     /**
58329         \param disp Display window.
58330         \param display_info Tells if image information are displayed on the standard output.
58331         \param axis Alignment axis for images viewing.
58332         \param align Apending alignment.
58333         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
58334         \param exit_on_anykey Exit function when any key is pressed.
58335         \note This function opens a new window with a specific title and displays the list images of the
58336           current CImgList instance into it.
58337         Images of the list are appended in a single temporarly image for visualization purposes.
58338         The function returns when a key is pressed or the display window is closed by the user.
58339     **/
58340     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
58341                                const char axis='x', const float align=0,
58342                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
58343       bool is_exit = false;
58344       return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
58345     }
58346 
58347     //! Display the current CImgList instance in a new display window.
58348     /**
58349       \param title Title of the opening display window.
58350       \param display_info Tells if list information must be written on standard output.
58351       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
58352       \param align Appending alignment.
58353       \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
58354       \param exit_on_anykey Exit function when any key is pressed.
58355     **/
58356     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
58357                                const char axis='x', const float align=0,
58358                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
58359       CImgDisplay disp;
58360       bool is_exit = false;
58361       return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
58362     }
58363 
58364     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
58365                                 const bool display_info, const char axis, const float align, unsigned int *const XYZ,
58366                                 const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
58367                                 bool &is_exit) const {
58368       if (is_empty())
58369         throw CImgInstanceException(_cimglist_instance
58370                                     "display(): Empty instance.",
58371                                     cimglist_instance);
58372       if (!disp) {
58373         if (axis=='x') {
58374           unsigned int sum_width = 0, max_height = 0;
58375           cimglist_for(*this,l) {
58376             const CImg<T> &img = _data[l];
58377             const unsigned int
58378               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
58379               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
58380             sum_width+=w;
58381             if (h>max_height) max_height = h;
58382           }
58383           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
58384         } else {
58385           unsigned int max_width = 0, sum_height = 0;
58386           cimglist_for(*this,l) {
58387             const CImg<T> &img = _data[l];
58388             const unsigned int
58389               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
58390               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
58391             if (w>max_width) max_width = w;
58392             sum_height+=h;
58393           }
58394           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
58395         }
58396         if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
58397       } else if (title) disp.set_title("%s",title);
58398       else if (titles) disp.set_title("%s",titles->__display()._data);
58399       const CImg<char> dtitle = CImg<char>::string(disp.title());
58400       if (display_info) print(disp.title());
58401       disp.show().flush();
58402 
58403       if (_width==1) {
58404         const unsigned int dw = disp._width, dh = disp._height;
58405         if (!is_first_call)
58406           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
58407         disp.set_title("%s (%ux%ux%ux%u)",
58408                        dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
58409         _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
58410         if (disp.key()) is_exit = true;
58411         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
58412       } else {
58413         bool disp_resize = !is_first_call;
58414         while (!disp.is_closed() && !is_exit) {
58415           const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
58416           disp_resize = true;
58417           if (s[0]<0 && !disp.wheel()) { // No selections done.
58418             if (disp.button()&2) { disp.flush(); break; }
58419             is_exit = true;
58420           } else if (disp.wheel()) { // Zoom in/out.
58421             const int wheel = disp.wheel();
58422             disp.set_wheel();
58423             if (!is_first_call && wheel<0) break;
58424             if (wheel>0 && _width>=4) {
58425               const unsigned int
58426                 delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
58427                 ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
58428                 ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
58429               if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
58430                 const CImgList<T> sublist = get_shared_images(ind0,ind1);
58431                 CImgList<charT> t_sublist;
58432                 if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
58433                 sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
58434                                  orig + ind0,false,is_exit);
58435               }
58436             }
58437           } else if (s[0]!=0 || s[1]!=width() - 1) {
58438             const CImgList<T> sublist = get_shared_images(s[0],s[1]);
58439             CImgList<charT> t_sublist;
58440             if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
58441             sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
58442                              orig + s[0],false,is_exit);
58443           }
58444           disp.set_title("%s",dtitle.data());
58445         }
58446       }
58447       return *this;
58448     }
58449 
58450     // [internal] Return string to describe display title.
58451     CImg<charT> __display() const {
58452       CImg<charT> res, str;
58453       cimglist_for(*this,l) {
58454         CImg<charT>::string(_data[l]).move_to(str);
58455         if (l!=width() - 1) {
58456           str.resize(str._width + 1,1,1,1,0);
58457           str[str._width - 2] = ',';
58458           str[str._width - 1] = ' ';
58459         }
58460         res.append(str,'x');
58461       }
58462       if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
58463       cimg::strellipsize(res,128,false);
58464       if (_width>1) {
58465         const unsigned int l = (unsigned int)std::strlen(res);
58466         if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
58467         cimg_snprintf(res._data + l,16," (#%u)",_width);
58468       }
58469       return res;
58470     }
58471 
58472     //! Save list into a file.
58473     /**
58474       \param filename Filename to write data to.
58475       \param number When positive, represents an index added to the filename. Otherwise, no number is added.
58476       \param digits Number of digits used for adding the number to the filename.
58477     **/
58478     const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
58479       if (!filename)
58480         throw CImgArgumentException(_cimglist_instance
58481                                     "save(): Specified filename is (null).",
58482                                     cimglist_instance);
58483       // Do not test for empty instances, since .cimg format is able to manage empty instances.
58484       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
58485       const char *const ext = cimg::split_filename(filename);
58486       CImg<charT> nfilename(1024);
58487       const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
58488         filename;
58489 
58490 #ifdef cimglist_save_plugin
58491       cimglist_save_plugin(fn);
58492 #endif
58493 #ifdef cimglist_save_plugin1
58494       cimglist_save_plugin1(fn);
58495 #endif
58496 #ifdef cimglist_save_plugin2
58497       cimglist_save_plugin2(fn);
58498 #endif
58499 #ifdef cimglist_save_plugin3
58500       cimglist_save_plugin3(fn);
58501 #endif
58502 #ifdef cimglist_save_plugin4
58503       cimglist_save_plugin4(fn);
58504 #endif
58505 #ifdef cimglist_save_plugin5
58506       cimglist_save_plugin5(fn);
58507 #endif
58508 #ifdef cimglist_save_plugin6
58509       cimglist_save_plugin6(fn);
58510 #endif
58511 #ifdef cimglist_save_plugin7
58512       cimglist_save_plugin7(fn);
58513 #endif
58514 #ifdef cimglist_save_plugin8
58515       cimglist_save_plugin8(fn);
58516 #endif
58517       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
58518       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
58519       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
58520       else if (!cimg::strcasecmp(ext,"avi") ||
58521                !cimg::strcasecmp(ext,"mov") ||
58522                !cimg::strcasecmp(ext,"asf") ||
58523                !cimg::strcasecmp(ext,"divx") ||
58524                !cimg::strcasecmp(ext,"flv") ||
58525                !cimg::strcasecmp(ext,"mpg") ||
58526                !cimg::strcasecmp(ext,"m1v") ||
58527                !cimg::strcasecmp(ext,"m2v") ||
58528                !cimg::strcasecmp(ext,"m4v") ||
58529                !cimg::strcasecmp(ext,"mjp") ||
58530                !cimg::strcasecmp(ext,"mp4") ||
58531                !cimg::strcasecmp(ext,"mkv") ||
58532                !cimg::strcasecmp(ext,"mpe") ||
58533                !cimg::strcasecmp(ext,"movie") ||
58534                !cimg::strcasecmp(ext,"ogm") ||
58535                !cimg::strcasecmp(ext,"ogg") ||
58536                !cimg::strcasecmp(ext,"ogv") ||
58537                !cimg::strcasecmp(ext,"qt") ||
58538                !cimg::strcasecmp(ext,"rm") ||
58539                !cimg::strcasecmp(ext,"vob") ||
58540                !cimg::strcasecmp(ext,"wmv") ||
58541                !cimg::strcasecmp(ext,"xvid") ||
58542                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
58543 #ifdef cimg_use_tiff
58544       else if (!cimg::strcasecmp(ext,"tif") ||
58545           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
58546 #endif
58547       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
58548       else {
58549         if (_width==1) _data[0].save(fn,-1);
58550         else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
58551       }
58552       return *this;
58553     }
58554 
58555     //! Tell if an image list can be saved as one single file.
58556     /**
58557        \param filename Filename, as a C-string.
58558        \return \c true if the file format supports multiple images, \c false otherwise.
58559     **/
58560     static bool is_saveable(const char *const filename) {
58561       const char *const ext = cimg::split_filename(filename);
58562       if (!cimg::strcasecmp(ext,"cimgz") ||
58563 #ifdef cimg_use_tiff
58564           !cimg::strcasecmp(ext,"tif") ||
58565           !cimg::strcasecmp(ext,"tiff") ||
58566 #endif
58567           !cimg::strcasecmp(ext,"yuv") ||
58568           !cimg::strcasecmp(ext,"avi") ||
58569           !cimg::strcasecmp(ext,"mov") ||
58570           !cimg::strcasecmp(ext,"asf") ||
58571           !cimg::strcasecmp(ext,"divx") ||
58572           !cimg::strcasecmp(ext,"flv") ||
58573           !cimg::strcasecmp(ext,"mpg") ||
58574           !cimg::strcasecmp(ext,"m1v") ||
58575           !cimg::strcasecmp(ext,"m2v") ||
58576           !cimg::strcasecmp(ext,"m4v") ||
58577           !cimg::strcasecmp(ext,"mjp") ||
58578           !cimg::strcasecmp(ext,"mp4") ||
58579           !cimg::strcasecmp(ext,"mkv") ||
58580           !cimg::strcasecmp(ext,"mpe") ||
58581           !cimg::strcasecmp(ext,"movie") ||
58582           !cimg::strcasecmp(ext,"ogm") ||
58583           !cimg::strcasecmp(ext,"ogg") ||
58584           !cimg::strcasecmp(ext,"ogv") ||
58585           !cimg::strcasecmp(ext,"qt") ||
58586           !cimg::strcasecmp(ext,"rm") ||
58587           !cimg::strcasecmp(ext,"vob") ||
58588           !cimg::strcasecmp(ext,"wmv") ||
58589           !cimg::strcasecmp(ext,"xvid") ||
58590           !cimg::strcasecmp(ext,"mpeg")) return true;
58591       return false;
58592     }
58593 
58594     //! Save image sequence as a GIF animated file.
58595     /**
58596        \param filename Filename to write data to.
58597        \param fps Number of desired frames per second.
58598        \param nb_loops Number of loops (\c 0 for infinite looping).
58599     **/
58600     const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
58601                                          const unsigned int nb_loops=0) {
58602       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58603       CImgList<charT> filenames;
58604       std::FILE *file = 0;
58605 
58606 #ifdef cimg_use_png
58607 #define _cimg_save_gif_ext "png"
58608 #else
58609 #define _cimg_save_gif_ext "ppm"
58610 #endif
58611 
58612       do {
58613         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58614                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58615         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_ext,filename_tmp._data);
58616         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58617       } while (file);
58618       cimglist_for(*this,l) {
58619         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_ext,filename_tmp._data,l + 1);
58620         CImg<charT>::string(filename_tmp2).move_to(filenames);
58621         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
58622         else _data[l].save(filename_tmp2);
58623       }
58624       cimg_snprintf(command,command._width,"%s -delay %u -loop %u",
58625                     cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops);
58626       CImg<ucharT>::string(command).move_to(filenames,0);
58627       cimg_snprintf(command,command._width,"\"%s\"",
58628                     CImg<charT>::string(filename)._system_strescape().data());
58629       CImg<ucharT>::string(command).move_to(filenames);
58630       CImg<charT> _command = filenames>'x';
58631       cimg_for(_command,p,char) if (!*p) *p = ' ';
58632       _command.back() = 0;
58633 
58634       cimg::system(_command);
58635       file = std_fopen(filename,"rb");
58636       if (!file)
58637         throw CImgIOException(_cimglist_instance
58638                               "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
58639                               cimglist_instance,
58640                               filename);
58641       else cimg::fclose(file);
58642       cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
58643       return *this;
58644     }
58645 
58646     //! Save list as a YUV image sequence file.
58647     /**
58648       \param filename Filename to write data to.
58649       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
58650       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
58651     **/
58652     const CImgList<T>& save_yuv(const char *const filename=0,
58653                                 const unsigned int chroma_subsampling=444,
58654                                 const bool is_rgb=true) const {
58655       return _save_yuv(0,filename,chroma_subsampling,is_rgb);
58656     }
58657 
58658     //! Save image sequence into a YUV file.
58659     /**
58660       \param file File to write data to.
58661       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
58662       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
58663     **/
58664     const CImgList<T>& save_yuv(std::FILE *const file,
58665                                 const unsigned int chroma_subsampling=444,
58666                                 const bool is_rgb=true) const {
58667       return _save_yuv(file,0,chroma_subsampling,is_rgb);
58668     }
58669 
58670     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
58671                                  const unsigned int chroma_subsampling,
58672                                  const bool is_rgb) const {
58673       if (!file && !filename)
58674         throw CImgArgumentException(_cimglist_instance
58675                                     "save_yuv(): Specified filename is (null).",
58676                                     cimglist_instance);
58677       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
58678         throw CImgArgumentException(_cimglist_instance
58679                                     "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
58680                                     cimglist_instance,
58681                                     chroma_subsampling,filename?filename:"(FILE*)");
58682       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58683       const unsigned int
58684         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
58685         cfy = chroma_subsampling==420?2:1,
58686         w0 = (*this)[0]._width, h0 = (*this)[0]._height,
58687         width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
58688       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58689       cimglist_for(*this,l) {
58690         const CImg<T> &frame = (*this)[l];
58691         CImg<ucharT> YUV;
58692         if (sizeof(T)==1 && !is_rgb &&
58693             frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
58694           YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
58695         else {
58696           YUV = frame;
58697           if (YUV._width!=width0 || YUV._height!=height0 || YUV._depth!=1) YUV.resize(width0,height0,1,-100,0);
58698           if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
58699           if (is_rgb) YUV.RGBtoYCbCr();
58700         }
58701         if (chroma_subsampling==444)
58702           cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
58703         else {
58704           cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
58705           CImg<ucharT> UV = YUV.get_channels(1,2);
58706           UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
58707           cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
58708         }
58709       }
58710       if (!file) cimg::fclose(nfile);
58711       return *this;
58712     }
58713 
58714     //! Save list into a .cimg file.
58715     /**
58716        \param filename Filename to write data to.
58717        \param is_compressed Tells if data compression must be enabled.
58718     **/
58719     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
58720       return _save_cimg(0,filename,is_compressed);
58721     }
58722 
58723     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
58724       if (!file && !filename)
58725         throw CImgArgumentException(_cimglist_instance
58726                                     "save_cimg(): Specified filename is (null).",
58727                                     cimglist_instance);
58728 #ifndef cimg_use_zlib
58729       if (is_compressed)
58730         cimg::warn(_cimglist_instance
58731                    "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
58732                    "saving them uncompressed.",
58733                    cimglist_instance,
58734                    filename?filename:"(FILE*)");
58735 #endif
58736       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58737       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
58738       if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
58739       else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
58740       cimglist_for(*this,l) {
58741         const CImg<T>& img = _data[l];
58742         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
58743         if (img._data) {
58744           CImg<T> tmp;
58745           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
58746           const CImg<T>& ref = cimg::endianness()?tmp:img;
58747           bool failed_to_compress = true;
58748           if (is_compressed) {
58749 #ifdef cimg_use_zlib
58750             const ulongT siz = sizeof(T)*ref.size();
58751             uLongf csiz = siz + siz/100 + 16;
58752             Bytef *const cbuf = new Bytef[csiz];
58753             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
58754               cimg::warn(_cimglist_instance
58755                          "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
58756                          cimglist_instance,
58757                          filename?filename:"(FILE*)");
58758             else {
58759               std::fprintf(nfile," #%lu\n",csiz);
58760               cimg::fwrite(cbuf,csiz,nfile);
58761               delete[] cbuf;
58762               failed_to_compress = false;
58763             }
58764 #endif
58765           }
58766           if (failed_to_compress) { // Write in a non-compressed way.
58767             std::fputc('\n',nfile);
58768             cimg::fwrite(ref._data,ref.size(),nfile);
58769           }
58770         } else std::fputc('\n',nfile);
58771       }
58772       if (!file) cimg::fclose(nfile);
58773       return *this;
58774     }
58775 
58776     //! Save list into a .cimg file.
58777     /**
58778        \param file File to write data to.
58779        \param is_compressed Tells if data compression must be enabled.
58780     **/
58781     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
58782       return _save_cimg(file,0,is_compressed);
58783     }
58784 
58785     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
58786                                  const unsigned int n0,
58787                                  const unsigned int x0, const unsigned int y0,
58788                                  const unsigned int z0, const unsigned int c0) const {
58789 #define _cimg_save_cimg_case(Ts,Tss) \
58790       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
58791         for (unsigned int l = 0; l<lmax; ++l) { \
58792           j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
58793           W = H = D = C = 0; \
58794           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
58795             throw CImgIOException(_cimglist_instance \
58796                                   "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
58797                                   cimglist_instance, \
58798                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
58799           if (W*H*D*C>0) { \
58800             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
58801             else { \
58802               const CImg<T>& img = (*this)[l - n0]; \
58803               const T *ptrs = img._data; \
58804               const unsigned int \
58805                 x1 = x0 + img._width - 1, \
58806                 y1 = y0 + img._height - 1, \
58807                 z1 = z0 + img._depth - 1, \
58808                 c1 = c0 + img._spectrum - 1, \
58809                 nx1 = x1>=W?W - 1:x1, \
58810                 ny1 = y1>=H?H - 1:y1, \
58811                 nz1 = z1>=D?D - 1:z1, \
58812                 nc1 = c1>=C?C - 1:c1; \
58813               CImg<Tss> raw(1 + nx1 - x0); \
58814               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
58815               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
58816               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
58817                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
58818                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
58819                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
58820                   const unsigned int skipyb = y0*W*sizeof(Tss); \
58821                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
58822                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
58823                     const unsigned int skipxb = x0*sizeof(Tss); \
58824                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
58825                     raw.assign(ptrs, raw._width); \
58826                     ptrs+=img._width; \
58827                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
58828                     cimg::fwrite(raw._data,raw._width,nfile); \
58829                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
58830                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
58831                   } \
58832                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
58833                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
58834                 } \
58835                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
58836                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
58837               } \
58838               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
58839               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
58840             } \
58841           } \
58842         } \
58843         saved = true; \
58844       }
58845 
58846       if (!file && !filename)
58847         throw CImgArgumentException(_cimglist_instance
58848                                     "save_cimg(): Specified filename is (null).",
58849                                     cimglist_instance);
58850       if (is_empty())
58851         throw CImgInstanceException(_cimglist_instance
58852                                     "save_cimg(): Empty instance, for file '%s'.",
58853                                     cimglist_instance,
58854                                     filename?filename:"(FILE*)");
58855 
58856       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
58857       bool saved = false, endian = cimg::endianness();
58858       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
58859       *tmp = *str_pixeltype = *str_endian = 0;
58860       unsigned int j, N, W, H, D, C;
58861       int i, err;
58862       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
58863       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
58864       if (err<2) {
58865         if (!file) cimg::fclose(nfile);
58866         throw CImgIOException(_cimglist_instance
58867                               "save_cimg(): CImg header not found in file '%s'.",
58868                               cimglist_instance,
58869                               filename?filename:"(FILE*)");
58870       }
58871       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
58872       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
58873       const unsigned int lmax = std::min(N,n0 + _width);
58874       _cimg_save_cimg_case("bool",bool);
58875       _cimg_save_cimg_case("unsigned_char",unsigned char);
58876       _cimg_save_cimg_case("uchar",unsigned char);
58877       _cimg_save_cimg_case("char",char);
58878       _cimg_save_cimg_case("unsigned_short",unsigned short);
58879       _cimg_save_cimg_case("ushort",unsigned short);
58880       _cimg_save_cimg_case("short",short);
58881       _cimg_save_cimg_case("unsigned_int",unsigned int);
58882       _cimg_save_cimg_case("uint",unsigned int);
58883       _cimg_save_cimg_case("int",int);
58884       _cimg_save_cimg_case("unsigned_int64",uint64T);
58885       _cimg_save_cimg_case("uint64",uint64T);
58886       _cimg_save_cimg_case("int64",int64T);
58887       _cimg_save_cimg_case("float",float);
58888       _cimg_save_cimg_case("double",double);
58889       if (!saved) {
58890         if (!file) cimg::fclose(nfile);
58891         throw CImgIOException(_cimglist_instance
58892                               "save_cimg(): Unsupported data type '%s' for file '%s'.",
58893                               cimglist_instance,
58894                               filename?filename:"(FILE*)",str_pixeltype._data);
58895       }
58896       if (!file) cimg::fclose(nfile);
58897       return *this;
58898     }
58899 
58900     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
58901     /**
58902       \param filename Filename to write data to.
58903       \param n0 Starting index of images to write.
58904       \param x0 Starting X-coordinates of image regions to write.
58905       \param y0 Starting Y-coordinates of image regions to write.
58906       \param z0 Starting Z-coordinates of image regions to write.
58907       \param c0 Starting C-coordinates of image regions to write.
58908     **/
58909     const CImgList<T>& save_cimg(const char *const filename,
58910                                  const unsigned int n0,
58911                                  const unsigned int x0, const unsigned int y0,
58912                                  const unsigned int z0, const unsigned int c0) const {
58913       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
58914     }
58915 
58916     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
58917     /**
58918       \param file File to write data to.
58919       \param n0 Starting index of images to write.
58920       \param x0 Starting X-coordinates of image regions to write.
58921       \param y0 Starting Y-coordinates of image regions to write.
58922       \param z0 Starting Z-coordinates of image regions to write.
58923       \param c0 Starting C-coordinates of image regions to write.
58924     **/
58925     const CImgList<T>& save_cimg(std::FILE *const file,
58926                                  const unsigned int n0,
58927                                  const unsigned int x0, const unsigned int y0,
58928                                  const unsigned int z0, const unsigned int c0) const {
58929       return _save_cimg(file,0,n0,x0,y0,z0,c0);
58930     }
58931 
58932     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
58933                                 const unsigned int nb,
58934                                 const unsigned int dx, const unsigned int dy,
58935                                 const unsigned int dz, const unsigned int dc) {
58936       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58937       const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
58938       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
58939       for (unsigned int i=nb; i; --i) {
58940         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
58941 	for (ulongT off = siz; off; --off) std::fputc(0,nfile);
58942       }
58943       if (!file) cimg::fclose(nfile);
58944     }
58945 
58946     //! Save empty (non-compressed) .cimg file with specified dimensions.
58947     /**
58948         \param filename Filename to write data to.
58949         \param nb Number of images to write.
58950         \param dx Width of images in the written file.
58951         \param dy Height of images in the written file.
58952         \param dz Depth of images in the written file.
58953         \param dc Spectrum of images in the written file.
58954     **/
58955     static void save_empty_cimg(const char *const filename,
58956                                 const unsigned int nb,
58957                                 const unsigned int dx, const unsigned int dy=1,
58958                                 const unsigned int dz=1, const unsigned int dc=1) {
58959       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
58960     }
58961 
58962     //! Save empty .cimg file with specified dimensions.
58963     /**
58964         \param file File to write data to.
58965         \param nb Number of images to write.
58966         \param dx Width of images in the written file.
58967         \param dy Height of images in the written file.
58968         \param dz Depth of images in the written file.
58969         \param dc Spectrum of images in the written file.
58970     **/
58971     static void save_empty_cimg(std::FILE *const file,
58972                                 const unsigned int nb,
58973                                 const unsigned int dx, const unsigned int dy=1,
58974                                 const unsigned int dz=1, const unsigned int dc=1) {
58975       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
58976     }
58977 
58978     //! Save list as a TIFF file.
58979     /**
58980       \param filename Filename to write data to.
58981       \param compression_type Compression mode used to write data.
58982       \param voxel_size Voxel size, to be stored in the filename.
58983       \param description Description, to be stored in the filename.
58984       \param use_bigtiff Allow to save big tiff files (>4Gb).
58985     **/
58986     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
58987                                  const float *const voxel_size=0, const char *const description=0,
58988                                  const bool use_bigtiff=true) const {
58989       if (!filename)
58990         throw CImgArgumentException(_cimglist_instance
58991                                     "save_tiff(): Specified filename is (null).",
58992                                     cimglist_instance);
58993       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58994 
58995 #ifndef cimg_use_tiff
58996       if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
58997       else cimglist_for(*this,l) {
58998           CImg<charT> nfilename(1024);
58999           cimg::number_filename(filename,l,6,nfilename);
59000           _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
59001         }
59002 #else
59003       ulongT siz = 0;
59004       cimglist_for(*this,l) siz+=_data[l].size();
59005       const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images.
59006       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
59007       if (tif) {
59008         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
59009           const CImg<T>& img = (*this)[l];
59010           cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
59011         }
59012         TIFFClose(tif);
59013       } else
59014         throw CImgIOException(_cimglist_instance
59015                               "save_tiff(): Failed to open stream for file '%s'.",
59016                               cimglist_instance,
59017                               filename);
59018 #endif
59019       return *this;
59020     }
59021 
59022     //! Save list as a gzipped file, using external tool 'gzip'.
59023     /**
59024       \param filename Filename to write data to.
59025     **/
59026     const CImgList<T>& save_gzip_external(const char *const filename) const {
59027       if (!filename)
59028         throw CImgIOException(_cimglist_instance
59029                               "save_gzip_external(): Specified filename is (null).",
59030                               cimglist_instance);
59031       CImg<charT> command(1024), filename_tmp(256), body(256);
59032       const char
59033         *ext = cimg::split_filename(filename,body),
59034         *ext2 = cimg::split_filename(body,0);
59035       std::FILE *file;
59036       do {
59037         if (!cimg::strcasecmp(ext,"gz")) {
59038           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59039                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
59040           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59041                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59042         } else {
59043           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59044                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
59045           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59046                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59047         }
59048         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59049       } while (file);
59050 
59051       if (is_saveable(body)) {
59052         save(filename_tmp);
59053         cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
59054                       cimg::gzip_path(),
59055                       CImg<charT>::string(filename_tmp)._system_strescape().data(),
59056                       CImg<charT>::string(filename)._system_strescape().data());
59057         cimg::system(command);
59058         file = std_fopen(filename,"rb");
59059         if (!file)
59060           throw CImgIOException(_cimglist_instance
59061                                 "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
59062                                 cimglist_instance,
59063                                 filename);
59064         else cimg::fclose(file);
59065         std::remove(filename_tmp);
59066       } else {
59067         CImg<charT> nfilename(1024);
59068         cimglist_for(*this,l) {
59069           cimg::number_filename(body,l,6,nfilename);
59070           if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
59071           _data[l].save_gzip_external(nfilename);
59072         }
59073       }
59074       return *this;
59075     }
59076 
59077     //! Save image sequence, using the OpenCV library.
59078     /**
59079        \param filename Filename to write data to.
59080        \param fps Number of frames per second.
59081        \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
59082        \param keep_open Tells if the video writer associated to the specified filename
59083        must be kept open or not (to allow frames to be added in the same file afterwards).
59084     **/
59085     const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
59086                                   const char *codec=0, const bool keep_open=false) const {
59087 #ifndef cimg_use_opencv
59088       cimg::unused(codec,keep_open);
59089       return save_ffmpeg_external(filename,fps);
59090 #else
59091       static CvVideoWriter *writers[32] = { 0 };
59092       static CImgList<charT> filenames(32);
59093       static CImg<intT> sizes(32,2,1,1,0);
59094       static int last_used_index = -1;
59095 
59096       // Detect if a video writer already exists for the specified filename.
59097       cimg::mutex(9);
59098       int index = -1;
59099       if (filename) {
59100         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
59101           index = last_used_index;
59102         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
59103             index = l; break;
59104           }
59105       } else index = last_used_index;
59106       cimg::mutex(9,0);
59107 
59108       // Find empty slot for capturing video stream.
59109       if (index<0) {
59110         if (!filename)
59111           throw CImgArgumentException(_cimglist_instance
59112                                       "save_video(): No already open video writer found. You must specify a "
59113                                       "non-(null) filename argument for the first call.",
59114                                       cimglist_instance);
59115         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
59116         if (index<0)
59117           throw CImgIOException(_cimglist_instance
59118                                 "save_video(): File '%s', no video writer slots available. "
59119                                 "You have to release some of your previously opened videos.",
59120                                 cimglist_instance,filename);
59121         if (is_empty())
59122           throw CImgInstanceException(_cimglist_instance
59123                                       "save_video(): Instance list is empty.",
59124                                       cimglist_instance);
59125         const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
59126         if (!W || !H)
59127           throw CImgInstanceException(_cimglist_instance
59128                                       "save_video(): Frame [0] is an empty image.",
59129                                       cimglist_instance);
59130 
59131 #define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x))
59132 
59133         const char
59134           *const _codec = codec && *codec?codec:cimg_OS==2?"mpeg":"mp4v",
59135           codec0 = _cimg_docase(_codec[0]),
59136           codec1 = _codec[0]?_cimg_docase(_codec[1]):0,
59137           codec2 = _codec[1]?_cimg_docase(_codec[2]):0,
59138           codec3 = _codec[2]?_cimg_docase(_codec[3]):0;
59139         cimg::mutex(9);
59140         writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3),
59141                                              fps,cvSize(W,H));
59142         CImg<charT>::string(filename).move_to(filenames[index]);
59143         sizes(index,0) = W; sizes(index,1) = H;
59144         cimg::mutex(9,0);
59145         if (!writers[index])
59146           throw CImgIOException(_cimglist_instance
59147                                 "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
59148                                 cimglist_instance,filename,
59149                                 codec0,codec1,codec2,codec3);
59150       }
59151 
59152       if (!is_empty()) {
59153         const unsigned int W = sizes(index,0), H = sizes(index,1);
59154         cimg::mutex(9);
59155         IplImage *ipl = cvCreateImage(cvSize(W,H),8,3);
59156         cimglist_for(*this,l) {
59157           CImg<T> &src = _data[l];
59158           if (src.is_empty())
59159             cimg::warn(_cimglist_instance
59160                        "save_video(): Skip empty frame %d for file '%s'.",
59161                        cimglist_instance,l,filename);
59162           if (src._depth>1 || src._spectrum>3)
59163             cimg::warn(_cimglist_instance
59164                        "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
59165                        "Some image data may be ignored when writing frame into video file '%s'.",
59166                        cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
59167           if (src._width==W && src._height==H && src._spectrum==3) {
59168             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);
59169             char *ptrd = ipl->imageData;
59170             cimg_forXY(src,x,y) {
59171               *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
59172             }
59173           } else {
59174             CImg<unsigned char> _src(src,false);
59175             _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
59176             _src.resize(W,H,1,3,_src._spectrum==1);
59177             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);
59178             char *ptrd = ipl->imageData;
59179             cimg_forXY(_src,x,y) {
59180               *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
59181             }
59182           }
59183           cvWriteFrame(writers[index],ipl);
59184         }
59185         cvReleaseImage(&ipl);
59186         cimg::mutex(9,0);
59187       }
59188 
59189       cimg::mutex(9);
59190       if (!keep_open) {
59191         cvReleaseVideoWriter(&writers[index]);
59192         writers[index] = 0;
59193         filenames[index].assign();
59194         sizes(index,0) = sizes(index,1) = 0;
59195         last_used_index = -1;
59196       } else last_used_index = index;
59197       cimg::mutex(9,0);
59198 
59199       return *this;
59200 #endif
59201     }
59202 
59203     //! Save image sequence, using the external tool 'ffmpeg'.
59204     /**
59205       \param filename Filename to write data to.
59206       \param fps Number of frames per second.
59207       \param codec Type of compression.
59208       \param bitrate Output bitrate
59209     **/
59210     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
59211                                             const char *const codec=0, const unsigned int bitrate=2048) const {
59212       if (!filename)
59213         throw CImgArgumentException(_cimglist_instance
59214                                     "save_ffmpeg_external(): Specified filename is (null).",
59215                                     cimglist_instance);
59216       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59217 
59218       const char
59219         *const ext = cimg::split_filename(filename),
59220         *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
59221 
59222       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
59223       CImgList<charT> filenames;
59224       std::FILE *file = 0;
59225       cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
59226         throw CImgInstanceException(_cimglist_instance
59227                                     "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
59228                                     cimglist_instance,
59229                                     filename);
59230       do {
59231         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
59232                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59233         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
59234         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
59235       } while (file);
59236       cimglist_for(*this,l) {
59237         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
59238         CImg<charT>::string(filename_tmp2).move_to(filenames);
59239         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2);
59240         else _data[l].save_pnm(filename_tmp2);
59241       }
59242       cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"",
59243                     cimg::ffmpeg_path(),
59244                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
59245                     _codec,bitrate,fps,
59246                     CImg<charT>::string(filename)._system_strescape().data());
59247       cimg::system(command);
59248       file = std_fopen(filename,"rb");
59249       if (!file)
59250         throw CImgIOException(_cimglist_instance
59251                               "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
59252                               cimglist_instance,
59253                               filename);
59254       else cimg::fclose(file);
59255       cimglist_for(*this,l) std::remove(filenames[l]);
59256       return *this;
59257     }
59258 
59259     //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
59260     /**
59261        \param is_compressed tells if zlib compression must be used for serialization
59262        (this requires 'cimg_use_zlib' been enabled).
59263     **/
59264     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
59265 #ifndef cimg_use_zlib
59266       if (is_compressed)
59267         cimg::warn(_cimglist_instance
59268                    "get_serialize(): Unable to compress data unless zlib is enabled, "
59269                    "storing them uncompressed.",
59270                    cimglist_instance);
59271 #endif
59272       CImgList<ucharT> stream;
59273       CImg<charT> tmpstr(128);
59274       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
59275       if (std::strstr(ptype,"unsigned")==ptype)
59276         cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
59277       else
59278         cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
59279       CImg<ucharT>::string(tmpstr,false).move_to(stream);
59280       cimglist_for(*this,l) {
59281         const CImg<T>& img = _data[l];
59282         cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
59283         CImg<ucharT>::string(tmpstr,false).move_to(stream);
59284         if (img._data) {
59285           CImg<T> tmp;
59286           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
59287           const CImg<T>& ref = cimg::endianness()?tmp:img;
59288           bool failed_to_compress = true;
59289           if (is_compressed) {
59290 #ifdef cimg_use_zlib
59291             const ulongT siz = sizeof(T)*ref.size();
59292             uLongf csiz = (ulongT)compressBound(siz);
59293             Bytef *const cbuf = new Bytef[csiz];
59294             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
59295               cimg::warn(_cimglist_instance
59296                          "get_serialize(): Failed to save compressed data, saving them uncompressed.",
59297                          cimglist_instance);
59298             else {
59299               cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
59300               CImg<ucharT>::string(tmpstr,false).move_to(stream);
59301               CImg<ucharT>(cbuf,csiz).move_to(stream);
59302               delete[] cbuf;
59303               failed_to_compress = false;
59304             }
59305 #endif
59306           }
59307           if (failed_to_compress) { // Write in a non-compressed way.
59308             CImg<charT>::string("\n",false).move_to(stream);
59309             stream.insert(1);
59310             stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
59311           }
59312         } else CImg<charT>::string("\n",false).move_to(stream);
59313       }
59314       cimglist_apply(stream,unroll)('y');
59315       return stream>'y';
59316     }
59317 
59318     //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
59319     template<typename t>
59320     static CImgList<T> get_unserialize(const CImg<t>& buffer) {
59321 #ifdef cimg_use_zlib
59322 #define _cimgz_unserialize_case(Tss) { \
59323         Bytef *cbuf = 0; \
59324         if (sizeof(t)!=1 || cimg::type<t>::string()==cimg::type<bool>::string()) { \
59325           cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
59326           for (ulongT i = 0; i<csiz; ++i) *(_cbuf++) = (Bytef)*(stream++); \
59327           is_bytef = false; \
59328         } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
59329         raw.assign(W,H,D,C); \
59330         uLongf destlen = raw.size()*sizeof(Tss); \
59331         uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
59332         if (!is_bytef) delete[] cbuf; \
59333       }
59334 #else
59335 #define _cimgz_unserialize_case(Tss) \
59336       throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
59337                                   "unless zlib is enabled.", \
59338                                   pixel_type());
59339 #endif
59340 
59341 #define _cimg_unserialize_case(Ts,Tss) \
59342       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
59343         for (unsigned int l = 0; l<N; ++l) { \
59344           j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
59345           ++stream; tmp[j] = 0; \
59346           W = H = D = C = 0; csiz = 0; \
59347           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
59348             throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
59349                                         "image #%u in serialized buffer.", \
59350                                         pixel_type(),W,H,D,C,l); \
59351           if (W*H*D*C>0) { \
59352             CImg<Tss> raw; \
59353             CImg<T> &img = res._data[l]; \
59354             if (err==5) _cimgz_unserialize_case(Tss) \
59355             else if (sizeof(Tss)==sizeof(t) && cimg::type<Tss>::is_float()==cimg::type<t>::is_float()) { \
59356               raw.assign((Tss*)stream,W,H,D,C,true); \
59357               stream+=raw.size(); \
59358             } else { \
59359               raw.assign(W,H,D,C); \
59360               CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
59361               cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
59362             } \
59363             if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
59364             raw.move_to(img); \
59365           } \
59366         } \
59367         loaded = true; \
59368       }
59369 
59370       if (buffer.is_empty())
59371         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
59372                                     pixel_type());
59373       CImgList<T> res;
59374       const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
59375       bool loaded = false, endian = cimg::endianness(), is_bytef = false;
59376       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
59377       *tmp = *str_pixeltype = *str_endian = 0;
59378       unsigned int j, N = 0, W, H, D, C;
59379       uint64T csiz;
59380       int i, err;
59381       cimg::unused(is_bytef);
59382       do {
59383         j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
59384         ++stream; tmp[j] = 0;
59385       } while (*tmp=='#' && stream<estream);
59386       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
59387                         &N,str_pixeltype._data,str_endian._data);
59388       if (err<2)
59389         throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
59390                                     pixel_type());
59391       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
59392       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
59393       res.assign(N);
59394       _cimg_unserialize_case("bool",bool);
59395       _cimg_unserialize_case("unsigned_char",unsigned char);
59396       _cimg_unserialize_case("uchar",unsigned char);
59397       _cimg_unserialize_case("char",char);
59398       _cimg_unserialize_case("unsigned_short",unsigned short);
59399       _cimg_unserialize_case("ushort",unsigned short);
59400       _cimg_unserialize_case("short",short);
59401       _cimg_unserialize_case("unsigned_int",unsigned int);
59402       _cimg_unserialize_case("uint",unsigned int);
59403       _cimg_unserialize_case("int",int);
59404       _cimg_unserialize_case("unsigned_int64",uint64T);
59405       _cimg_unserialize_case("uint64",uint64T);
59406       _cimg_unserialize_case("int64",int64T);
59407       _cimg_unserialize_case("float",float);
59408       _cimg_unserialize_case("double",double);
59409       if (!loaded)
59410         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
59411                                     "in serialized buffer.",
59412                                     pixel_type(),str_pixeltype._data);
59413       return res;
59414     }
59415 
59416     //@}
59417     //----------------------------------
59418     //
59419     //! \name Others
59420     //@{
59421     //----------------------------------
59422 
59423     //! Crop font along the X-axis.
59424     /**
59425     **/
59426     CImgList<T>& crop_font() {
59427       return get_crop_font().move_to(*this);
59428     }
59429 
59430     //! Crop font along the X-axis \newinstance.
59431     /**
59432     **/
59433     CImgList<T> get_crop_font() const {
59434       CImgList<T> res;
59435       cimglist_for(*this,l) {
59436         const CImg<T>& letter = (*this)[l];
59437         int xmin = letter.width(), xmax = 0;
59438         cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
59439         if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
59440         else letter.get_crop(xmin,0,xmax,letter._height - 1).move_to(res);
59441       }
59442       res[' '].resize(res['f']._width,-100,-100,-100,0);
59443       if (' ' + 256<res.size()) res[' ' + 256].resize(res['f']._width,-100,-100,-100,0);
59444       return res;
59445     }
59446 
59447     //! Return a CImg pre-defined font with desired size.
59448     /**
59449        \param font_height Height of the desired font (exact match for 13,23,53,103).
59450        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
59451     **/
59452     static const CImgList<ucharT>& font(const unsigned int font_height, const bool is_variable_width=true) {
59453       if (!font_height) return CImgList<ucharT>::const_empty();
59454       cimg::mutex(11);
59455 
59456       // Decompress nearest base font data if needed.
59457       static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 };
59458       static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 },
59459                                 data_Ms[] = { 86,79,57,47 };
59460       const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U;
59461       static CImg<ucharT> base_fonts[4];
59462       CImg<ucharT> &base_font = base_fonts[data_ind];
59463       if (!base_font) {
59464         const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind];
59465         base_font.assign(256*w,h);
59466         const char *data_font = data_fonts[data_ind];
59467         unsigned char *ptrd = base_font;
59468         const unsigned char *const ptrde = base_font.end();
59469 
59470         // Special case needed for 90x103 to avoid MS compiler limit with big strings.
59471         CImg<char> data90x103;
59472         if (!data_font) {
59473           ((CImg<char>(cimg::_data_font90x103[0],
59474                        (unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true),
59475             CImg<char>(cimg::_data_font90x103[1],
59476                        (unsigned int)std::strlen(cimg::_data_font90x103[1]) + 1,1,1,1,true))>'x').
59477             move_to(data90x103);
59478           data_font = data90x103.data();
59479         }
59480 
59481         // Uncompress font data (decode RLE).
59482         for (const char *ptrs = data_font; *ptrs; ++ptrs) {
59483           const int c = (int)(*ptrs - M - 32), v = c>=0?255:0, n = c>=0?c:-c;
59484           if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
59485           else { std::memset(ptrd,v,ptrde - ptrd); break; }
59486         }
59487       }
59488 
59489       // Find optimal font cache location to return.
59490       static CImgList<ucharT> fonts[16];
59491       static bool is_variable_widths[16] = { 0 };
59492       unsigned int ind = ~0U;
59493       for (int i = 0; i<16; ++i)
59494         if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) {
59495           ind = (unsigned int)i; break; // Found empty slot or cached font.
59496         }
59497       if (ind==~0U) { // No empty slots nor existing font in cache.
59498         fonts->assign();
59499         std::memmove(fonts,fonts + 1,15*sizeof(CImgList<ucharT>));
59500         std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
59501         std::memset(fonts + (ind=15),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font.
59502       }
59503       CImgList<ucharT> &font = fonts[ind];
59504 
59505       // Render requested font.
59506       if (!font) {
59507         const unsigned int padding_x = font_height<33U?1U:font_height<53U?2U:font_height<103U?3U:4U;
59508         is_variable_widths[ind] = is_variable_width;
59509         font = base_font.get_split('x',256);
59510         if (font_height!=font[0]._height)
59511           cimglist_for(font,l)
59512             font[l].resize(std::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100,
59513                            font[0]._height>font_height?2:5);
59514         if (is_variable_width) font.crop_font();
59515         cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5);
59516         font.insert(256,0);
59517         cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
59518       }
59519       cimg::mutex(11,0);
59520       return font;
59521     }
59522 
59523     //! Compute a 1d Fast Fourier Transform, along specified axis.
59524     /**
59525        \param axis Axis along which the Fourier transform is computed.
59526        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
59527     **/
59528     CImgList<T>& FFT(const char axis, const bool invert=false) {
59529       if (is_empty()) return *this;
59530       if (_width==1) insert(1);
59531       if (_width>2)
59532         cimg::warn(_cimglist_instance
59533                    "FFT(): Instance has more than 2 images",
59534                    cimglist_instance);
59535 
59536       CImg<T>::FFT(_data[0],_data[1],axis,invert);
59537       return *this;
59538     }
59539 
59540     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
59541     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
59542       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
59543     }
59544 
59545     //! Compute a n-d Fast Fourier Transform.
59546     /**
59547       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
59548     **/
59549     CImgList<T>& FFT(const bool invert=false) {
59550       if (is_empty()) return *this;
59551       if (_width==1) insert(1);
59552       if (_width>2)
59553         cimg::warn(_cimglist_instance
59554                    "FFT(): Instance has more than 2 images",
59555                    cimglist_instance);
59556 
59557       CImg<T>::FFT(_data[0],_data[1],invert);
59558       return *this;
59559     }
59560 
59561     //! Compute a n-d Fast Fourier Transform \newinstance.
59562     CImgList<Tfloat> get_FFT(const bool invert=false) const {
59563       return CImgList<Tfloat>(*this,false).FFT(invert);
59564     }
59565 
59566     //! Reverse primitives orientations of a 3d object.
59567     /**
59568     **/
59569     CImgList<T>& reverse_object3d() {
59570       cimglist_for(*this,l) {
59571         CImg<T>& p = _data[l];
59572         switch (p.size()) {
59573         case 2 : case 3: cimg::swap(p[0],p[1]); break;
59574         case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
59575         case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
59576         case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
59577         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;
59578         }
59579       }
59580       return *this;
59581     }
59582 
59583     //! Reverse primitives orientations of a 3d object \newinstance.
59584     CImgList<T> get_reverse_object3d() const {
59585       return (+*this).reverse_object3d();
59586     }
59587 
59588     //@}
59589   }; // struct CImgList<T> { ...
59590 
59591   /*
59592     #---------------------------------------------
59593     #
59594     # Completion of previously declared functions
59595     #
59596     #----------------------------------------------
59597   */
59598 
59599 namespace cimg {
59600 
59601   // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
59602   // (throw a CImgIOException when macro 'cimg_use_r' is defined).
59603   inline FILE* _stdin(const bool throw_exception) {
59604 #ifndef cimg_use_r
59605     cimg::unused(throw_exception);
59606     return stdin;
59607 #else
59608     if (throw_exception) {
59609       cimg::exception_mode(0);
59610       throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
59611                             "('cimg_use_r' is defined).");
59612     }
59613     return 0;
59614 #endif
59615   }
59616 
59617   inline FILE* _stdout(const bool throw_exception) {
59618 #ifndef cimg_use_r
59619     cimg::unused(throw_exception);
59620     return stdout;
59621 #else
59622     if (throw_exception) {
59623       cimg::exception_mode(0);
59624       throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
59625                             "('cimg_use_r' is defined).");
59626     }
59627     return 0;
59628 #endif
59629   }
59630 
59631   inline FILE* _stderr(const bool throw_exception) {
59632 #ifndef cimg_use_r
59633     cimg::unused(throw_exception);
59634     return stderr;
59635 #else
59636     if (throw_exception) {
59637       cimg::exception_mode(0);
59638       throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
59639                             "('cimg_use_r' is defined).");
59640     }
59641     return 0;
59642 #endif
59643   }
59644 
59645   // Open a file (with wide character support on Windows).
59646   inline std::FILE *win_fopen(const char *const path, const char *const mode) {
59647 #if cimg_OS==2
59648     // Convert 'path' to a wide-character string.
59649     int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
59650     if (!err) return std_fopen(path,mode);
59651     CImg<wchar_t> wpath(err);
59652     err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
59653     if (!err) return std_fopen(path,mode);
59654 
59655     // Convert 'mode' to a wide-character string.
59656     err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
59657     if (!err) return std_fopen(path,mode);
59658     CImg<wchar_t> wmode(err);
59659     err = MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err);
59660     if (!err) return std_fopen(path,mode);
59661     return _wfopen(wpath,wmode);
59662 #else
59663     return std_fopen(path,mode);
59664 #endif
59665   }
59666 
59667   //! Get/set path to store temporary files.
59668   /**
59669      \param user_path Specified path, or \c 0 to get the path currently used.
59670      \param reinit_path Force path to be recalculated (may take some time).
59671      \return Path where temporary files can be saved.
59672   **/
59673   inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
59674 #define _cimg_test_temporary_path(p)                                    \
59675     if (!path_found) {                                                  \
59676       cimg_snprintf(s_path,s_path.width(),"%s",p);                      \
59677       cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
59678       if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
59679     }
59680     static CImg<char> s_path;
59681     cimg::mutex(7);
59682     if (reinit_path) s_path.assign();
59683     if (user_path) {
59684       if (!s_path) s_path.assign(1024);
59685       std::strncpy(s_path,user_path,1023);
59686     } else if (!s_path) {
59687       s_path.assign(1024);
59688       bool path_found = false;
59689       CImg<char> tmp(1024), filename_tmp(256);
59690       std::FILE *file = 0;
59691       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
59692       char *tmpPath = std::getenv("TMP");
59693       if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
59694       if (tmpPath) _cimg_test_temporary_path(tmpPath);
59695 #if cimg_OS==2
59696       _cimg_test_temporary_path("C:\\WINNT\\Temp");
59697       _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
59698       _cimg_test_temporary_path("C:\\Temp");
59699       _cimg_test_temporary_path("C:");
59700       _cimg_test_temporary_path("D:\\WINNT\\Temp");
59701       _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
59702       _cimg_test_temporary_path("D:\\Temp");
59703       _cimg_test_temporary_path("D:");
59704 #else
59705       _cimg_test_temporary_path("/tmp");
59706       _cimg_test_temporary_path("/var/tmp");
59707 #endif
59708       if (!path_found) {
59709         *s_path = 0;
59710         std::strncpy(tmp,filename_tmp,tmp._width - 1);
59711         if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
59712       }
59713       if (!path_found) {
59714         cimg::mutex(7,0);
59715         throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
59716       }
59717     }
59718     cimg::mutex(7,0);
59719     return s_path;
59720   }
59721 
59722   //! Get/set path to the <i>Program Files/</i> directory (Windows only).
59723   /**
59724      \param user_path Specified path, or \c 0 to get the path currently used.
59725      \param reinit_path Force path to be recalculated (may take some time).
59726      \return Path containing the program files.
59727   **/
59728 #if cimg_OS==2
59729   inline const char* programfiles_path(const char *const user_path, const bool reinit_path) {
59730     static CImg<char> s_path;
59731     cimg::mutex(7);
59732     if (reinit_path) s_path.assign();
59733     if (user_path) {
59734       if (!s_path) s_path.assign(1024);
59735       std::strncpy(s_path,user_path,1023);
59736     } else if (!s_path) {
59737       s_path.assign(MAX_PATH);
59738       *s_path = 0;
59739       // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
59740 #if !defined(__INTEL_COMPILER)
59741       if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
59742         const char *const pfPath = std::getenv("PROGRAMFILES");
59743         if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
59744         else std::strcpy(s_path,"C:\\PROGRA~1");
59745       }
59746 #else
59747       std::strcpy(s_path,"C:\\PROGRA~1");
59748 #endif
59749     }
59750     cimg::mutex(7,0);
59751     return s_path;
59752   }
59753 #endif
59754 
59755   //! Get/set path to the ImageMagick's \c convert binary.
59756   /**
59757      \param user_path Specified path, or \c 0 to get the path currently used.
59758      \param reinit_path Force path to be recalculated (may take some time).
59759      \return Path containing the \c convert binary.
59760   **/
59761   inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
59762     static CImg<char> s_path;
59763     cimg::mutex(7);
59764     if (reinit_path) s_path.assign();
59765     if (user_path) {
59766       if (!s_path) s_path.assign(1024);
59767       std::strncpy(s_path,user_path,1023);
59768     } else if (!s_path) {
59769       s_path.assign(1024);
59770       bool path_found = false;
59771       std::FILE *file = 0;
59772 #if cimg_OS==2
59773       const char *const pf_path = programfiles_path();
59774       for (int l = 0; l<2 && !path_found; ++l) {
59775         const char *const s_exe = l?"convert":"magick";
59776         cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
59777         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59778         for (int k = 32; k>=10 && !path_found; --k) {
59779           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
59780           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59781         }
59782         for (int k = 9; k>=0 && !path_found; --k) {
59783           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
59784           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59785         }
59786         for (int k = 32; k>=0 && !path_found; --k) {
59787           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
59788           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59789         }
59790         for (int k = 32; k>=10 && !path_found; --k) {
59791           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59792           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59793         }
59794         for (int k = 9; k>=0 && !path_found; --k) {
59795           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59796           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59797         }
59798         for (int k = 32; k>=0 && !path_found; --k) {
59799           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59800           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59801         }
59802         for (int k = 32; k>=10 && !path_found; --k) {
59803           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
59804           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59805         }
59806         for (int k = 9; k>=0 && !path_found; --k) {
59807           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
59808           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59809         }
59810         for (int k = 32; k>=0 && !path_found; --k) {
59811           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
59812           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59813         }
59814         for (int k = 32; k>=10 && !path_found; --k) {
59815           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59816           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59817         }
59818         for (int k = 9; k>=0 && !path_found; --k) {
59819           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59820           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59821         }
59822         for (int k = 32; k>=0 && !path_found; --k) {
59823           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59824           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59825         }
59826         for (int k = 32; k>=10 && !path_found; --k) {
59827           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
59828           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59829         }
59830         for (int k = 9; k>=0 && !path_found; --k) {
59831           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
59832           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59833         }
59834         for (int k = 32; k>=0 && !path_found; --k) {
59835           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
59836           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59837         }
59838         for (int k = 32; k>=10 && !path_found; --k) {
59839           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59840           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59841         }
59842         for (int k = 9; k>=0 && !path_found; --k) {
59843           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59844           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59845         }
59846         for (int k = 32; k>=0 && !path_found; --k) {
59847           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59848           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59849         }
59850         if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
59851       }
59852 #else
59853       std::strcpy(s_path,"./magick");
59854       if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59855       if (!path_found) {
59856         std::strcpy(s_path,"./convert");
59857         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59858       }
59859       if (!path_found) std::strcpy(s_path,"convert");
59860 #endif
59861       winformat_string(s_path);
59862     }
59863     cimg::mutex(7,0);
59864     return s_path;
59865   }
59866 
59867   //! Get/set path to the GraphicsMagick's \c gm binary.
59868   /**
59869      \param user_path Specified path, or \c 0 to get the path currently used.
59870      \param reinit_path Force path to be recalculated (may take some time).
59871      \return Path containing the \c gm binary.
59872   **/
59873   inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
59874     static CImg<char> s_path;
59875     cimg::mutex(7);
59876     if (reinit_path) s_path.assign();
59877     if (user_path) {
59878       if (!s_path) s_path.assign(1024);
59879       std::strncpy(s_path,user_path,1023);
59880     } else if (!s_path) {
59881       s_path.assign(1024);
59882       bool path_found = false;
59883       std::FILE *file = 0;
59884 #if cimg_OS==2
59885       const char *const pf_path = programfiles_path();
59886       if (!path_found) {
59887         std::strcpy(s_path,".\\gm.exe");
59888         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59889       }
59890       for (int k = 32; k>=10 && !path_found; --k) {
59891         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
59892         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59893       }
59894       for (int k = 9; k>=0 && !path_found; --k) {
59895         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
59896         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59897       }
59898       for (int k = 32; k>=0 && !path_found; --k) {
59899         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
59900         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59901       }
59902       for (int k = 32; k>=10 && !path_found; --k) {
59903         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59904         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59905       }
59906       for (int k = 9; k>=0 && !path_found; --k) {
59907         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59908         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59909       }
59910       for (int k = 32; k>=0 && !path_found; --k) {
59911         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59912         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59913       }
59914       for (int k = 32; k>=10 && !path_found; --k) {
59915         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
59916         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59917       }
59918       for (int k = 9; k>=0 && !path_found; --k) {
59919         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
59920         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59921       }
59922       for (int k = 32; k>=0 && !path_found; --k) {
59923         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
59924         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59925       }
59926       for (int k = 32; k>=10 && !path_found; --k) {
59927         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
59928         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59929       }
59930       for (int k = 9; k>=0 && !path_found; --k) {
59931         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
59932         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59933       }
59934       for (int k = 32; k>=0 && !path_found; --k) {
59935         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
59936         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59937       }
59938       for (int k = 32; k>=10 && !path_found; --k) {
59939         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
59940         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59941       }
59942       for (int k = 9; k>=0 && !path_found; --k) {
59943         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
59944         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59945       }
59946       for (int k = 32; k>=0 && !path_found; --k) {
59947         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
59948         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59949       }
59950       for (int k = 32; k>=10 && !path_found; --k) {
59951         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
59952         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59953       }
59954       for (int k = 9; k>=0 && !path_found; --k) {
59955         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
59956         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59957       }
59958       for (int k = 32; k>=0 && !path_found; --k) {
59959         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
59960         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59961       }
59962       if (!path_found) std::strcpy(s_path,"gm.exe");
59963 #else
59964       if (!path_found) {
59965         std::strcpy(s_path,"./gm");
59966         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59967       }
59968       if (!path_found) std::strcpy(s_path,"gm");
59969 #endif
59970       winformat_string(s_path);
59971     }
59972     cimg::mutex(7,0);
59973     return s_path;
59974   }
59975 
59976   //! Get/set path to the XMedcon's \c medcon binary.
59977   /**
59978      \param user_path Specified path, or \c 0 to get the path currently used.
59979      \param reinit_path Force path to be recalculated (may take some time).
59980      \return Path containing the \c medcon binary.
59981   **/
59982   inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
59983     static CImg<char> s_path;
59984     cimg::mutex(7);
59985     if (reinit_path) s_path.assign();
59986     if (user_path) {
59987       if (!s_path) s_path.assign(1024);
59988       std::strncpy(s_path,user_path,1023);
59989     } else if (!s_path) {
59990       s_path.assign(1024);
59991       bool path_found = false;
59992       std::FILE *file = 0;
59993 #if cimg_OS==2
59994       const char *const pf_path = programfiles_path();
59995       if (!path_found) {
59996         std::strcpy(s_path,".\\medcon.exe");
59997         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59998       }
59999       if (!path_found) {
60000         cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
60001         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60002       }
60003       if (!path_found) {
60004         cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
60005         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60006       }
60007       if (!path_found) {
60008         std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
60009         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60010       }
60011       if (!path_found) std::strcpy(s_path,"medcon.exe");
60012 #else
60013       if (!path_found) {
60014         std::strcpy(s_path,"./medcon");
60015         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60016       }
60017       if (!path_found) std::strcpy(s_path,"medcon");
60018 #endif
60019       winformat_string(s_path);
60020     }
60021     cimg::mutex(7,0);
60022     return s_path;
60023   }
60024 
60025   //! Get/set path to the FFMPEG's \c ffmpeg binary.
60026   /**
60027      \param user_path Specified path, or \c 0 to get the path currently used.
60028      \param reinit_path Force path to be recalculated (may take some time).
60029      \return Path containing the \c ffmpeg binary.
60030   **/
60031   inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
60032     static CImg<char> s_path;
60033     cimg::mutex(7);
60034     if (reinit_path) s_path.assign();
60035     if (user_path) {
60036       if (!s_path) s_path.assign(1024);
60037       std::strncpy(s_path,user_path,1023);
60038     } else if (!s_path) {
60039       s_path.assign(1024);
60040       bool path_found = false;
60041       std::FILE *file = 0;
60042 #if cimg_OS==2
60043       if (!path_found) {
60044         std::strcpy(s_path,".\\ffmpeg.exe");
60045         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60046       }
60047       if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
60048 #else
60049       if (!path_found) {
60050         std::strcpy(s_path,"./ffmpeg");
60051         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60052       }
60053       if (!path_found) std::strcpy(s_path,"ffmpeg");
60054 #endif
60055       winformat_string(s_path);
60056     }
60057     cimg::mutex(7,0);
60058     return s_path;
60059   }
60060 
60061   //! Get/set path to the \c gzip binary.
60062   /**
60063      \param user_path Specified path, or \c 0 to get the path currently used.
60064      \param reinit_path Force path to be recalculated (may take some time).
60065      \return Path containing the \c gzip binary.
60066   **/
60067   inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
60068     static CImg<char> s_path;
60069     cimg::mutex(7);
60070     if (reinit_path) s_path.assign();
60071     if (user_path) {
60072       if (!s_path) s_path.assign(1024);
60073       std::strncpy(s_path,user_path,1023);
60074     } else if (!s_path) {
60075       s_path.assign(1024);
60076       bool path_found = false;
60077       std::FILE *file = 0;
60078 #if cimg_OS==2
60079       if (!path_found) {
60080         std::strcpy(s_path,".\\gzip.exe");
60081         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60082       }
60083       if (!path_found) std::strcpy(s_path,"gzip.exe");
60084 #else
60085       if (!path_found) {
60086         std::strcpy(s_path,"./gzip");
60087         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60088       }
60089       if (!path_found) std::strcpy(s_path,"gzip");
60090 #endif
60091       winformat_string(s_path);
60092     }
60093     cimg::mutex(7,0);
60094     return s_path;
60095   }
60096 
60097   //! Get/set path to the \c gunzip binary.
60098   /**
60099      \param user_path Specified path, or \c 0 to get the path currently used.
60100      \param reinit_path Force path to be recalculated (may take some time).
60101      \return Path containing the \c gunzip binary.
60102   **/
60103   inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
60104     static CImg<char> s_path;
60105     cimg::mutex(7);
60106     if (reinit_path) s_path.assign();
60107     if (user_path) {
60108       if (!s_path) s_path.assign(1024);
60109       std::strncpy(s_path,user_path,1023);
60110     } else if (!s_path) {
60111       s_path.assign(1024);
60112       bool path_found = false;
60113       std::FILE *file = 0;
60114 #if cimg_OS==2
60115       if (!path_found) {
60116         std::strcpy(s_path,".\\gunzip.exe");
60117         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60118       }
60119       if (!path_found) std::strcpy(s_path,"gunzip.exe");
60120 #else
60121       if (!path_found) {
60122         std::strcpy(s_path,"./gunzip");
60123         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60124       }
60125       if (!path_found) std::strcpy(s_path,"gunzip");
60126 #endif
60127       winformat_string(s_path);
60128     }
60129     cimg::mutex(7,0);
60130     return s_path;
60131   }
60132 
60133   //! Get/set path to the \c dcraw binary.
60134   /**
60135      \param user_path Specified path, or \c 0 to get the path currently used.
60136      \param reinit_path Force path to be recalculated (may take some time).
60137      \return Path containing the \c dcraw binary.
60138   **/
60139   inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
60140     static CImg<char> s_path;
60141     cimg::mutex(7);
60142     if (reinit_path) s_path.assign();
60143     if (user_path) {
60144       if (!s_path) s_path.assign(1024);
60145       std::strncpy(s_path,user_path,1023);
60146     } else if (!s_path) {
60147       s_path.assign(1024);
60148       bool path_found = false;
60149       std::FILE *file = 0;
60150 #if cimg_OS==2
60151       if (!path_found) {
60152         std::strcpy(s_path,".\\dcraw.exe");
60153         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60154       }
60155       if (!path_found) std::strcpy(s_path,"dcraw.exe");
60156 #else
60157       if (!path_found) {
60158         std::strcpy(s_path,"./dcraw");
60159         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60160       }
60161       if (!path_found) std::strcpy(s_path,"dcraw");
60162 #endif
60163       winformat_string(s_path);
60164     }
60165     cimg::mutex(7,0);
60166     return s_path;
60167   }
60168 
60169   //! Get/set path to the \c wget binary.
60170   /**
60171      \param user_path Specified path, or \c 0 to get the path currently used.
60172      \param reinit_path Force path to be recalculated (may take some time).
60173      \return Path containing the \c wget binary.
60174   **/
60175   inline const char *wget_path(const char *const user_path, const bool reinit_path) {
60176     static CImg<char> s_path;
60177     cimg::mutex(7);
60178     if (reinit_path) s_path.assign();
60179     if (user_path) {
60180       if (!s_path) s_path.assign(1024);
60181       std::strncpy(s_path,user_path,1023);
60182     } else if (!s_path) {
60183       s_path.assign(1024);
60184       bool path_found = false;
60185       std::FILE *file = 0;
60186 #if cimg_OS==2
60187       if (!path_found) {
60188         std::strcpy(s_path,".\\wget.exe");
60189         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60190       }
60191       if (!path_found) std::strcpy(s_path,"wget.exe");
60192 #else
60193       if (!path_found) {
60194         std::strcpy(s_path,"./wget");
60195         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60196       }
60197       if (!path_found) std::strcpy(s_path,"wget");
60198 #endif
60199       winformat_string(s_path);
60200     }
60201     cimg::mutex(7,0);
60202     return s_path;
60203   }
60204 
60205   //! Get/set path to the \c curl binary.
60206   /**
60207      \param user_path Specified path, or \c 0 to get the path currently used.
60208      \param reinit_path Force path to be recalculated (may take some time).
60209      \return Path containing the \c curl binary.
60210   **/
60211   inline const char *curl_path(const char *const user_path, const bool reinit_path) {
60212     static CImg<char> s_path;
60213     cimg::mutex(7);
60214     if (reinit_path) s_path.assign();
60215     if (user_path) {
60216       if (!s_path) s_path.assign(1024);
60217       std::strncpy(s_path,user_path,1023);
60218     } else if (!s_path) {
60219       s_path.assign(1024);
60220       bool path_found = false;
60221       std::FILE *file = 0;
60222 #if cimg_OS==2
60223       if (!path_found) {
60224         std::strcpy(s_path,".\\curl.exe");
60225         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60226       }
60227       if (!path_found) std::strcpy(s_path,"curl.exe");
60228 #else
60229       if (!path_found) {
60230         std::strcpy(s_path,"./curl");
60231         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60232       }
60233       if (!path_found) std::strcpy(s_path,"curl");
60234 #endif
60235       winformat_string(s_path);
60236     }
60237     cimg::mutex(7,0);
60238     return s_path;
60239   }
60240 
60241   // [internal] Sorting function, used by cimg::files().
60242   inline int _sort_files(const void* a, const void* b) {
60243     const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
60244     return std::strcmp(sa._data,sb._data);
60245   }
60246 
60247   //! Return list of files/directories in specified directory.
60248   /**
60249      \param path Path to the directory. Set to 0 for current directory.
60250      \param is_pattern Tell if specified path has a matching pattern in it.
60251      \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
60252      \param include_path Tell if \c path must be included in resulting filenames.
60253      \return A list of filenames.
60254   **/
60255   inline CImgList<char> files(const char *const path, const bool is_pattern=false,
60256                               const unsigned int mode=2, const bool include_path=false) {
60257     if (!path || !*path) return files("*",true,mode,include_path);
60258     CImgList<char> res;
60259 
60260     // If path is a valid folder name, ignore argument 'is_pattern'.
60261     const bool _is_pattern = is_pattern && !cimg::is_directory(path);
60262     bool is_root = false, is_current = false;
60263     cimg::unused(is_root,is_current);
60264 
60265     // Clean format of input path.
60266     CImg<char> pattern, _path = CImg<char>::string(path);
60267 #if cimg_OS==2
60268     for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
60269 #endif
60270     char *pd = _path;
60271     for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
60272     *pd = 0;
60273     unsigned int lp = (unsigned int)std::strlen(_path);
60274     if (!_is_pattern && lp && _path[lp - 1]=='/') {
60275       _path[lp - 1] = 0; --lp;
60276 #if cimg_OS!=2
60277       is_root = !*_path;
60278 #endif
60279     }
60280 
60281     // Separate folder path and matching pattern.
60282     if (_is_pattern) {
60283       const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
60284       CImg<char>::string(_path).move_to(pattern);
60285       if (bpos) {
60286         _path[bpos - 1] = 0; // End 'path' at last slash.
60287 #if cimg_OS!=2
60288         is_root = !*_path;
60289 #endif
60290       } else { // No path to folder specified, assuming current folder.
60291         is_current = true; *_path = 0;
60292       }
60293       lp = (unsigned int)std::strlen(_path);
60294     }
60295 
60296     // Windows version.
60297 #if cimg_OS==2
60298     if (!_is_pattern) {
60299       pattern.assign(lp + 3);
60300       std::memcpy(pattern,_path,lp);
60301       pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
60302     }
60303     WIN32_FIND_DATAA file_data;
60304     const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
60305     if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
60306     do {
60307       const char *const filename = file_data.cFileName;
60308       if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
60309         const unsigned int lf = (unsigned int)std::strlen(filename);
60310         const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
60311         if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
60312           if (include_path) {
60313             CImg<char> full_filename((lp?lp+1:0) + lf + 1);
60314             if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
60315             std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
60316             full_filename.move_to(res);
60317           } else CImg<char>(filename,lf + 1).move_to(res);
60318         }
60319       }
60320     } while (FindNextFileA(dir,&file_data));
60321     FindClose(dir);
60322 
60323     // Unix version (posix).
60324 #elif cimg_OS == 1
60325     DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
60326     if (!dir) return CImgList<char>::const_empty();
60327     struct dirent *ent;
60328     while ((ent=readdir(dir))!=0) {
60329       const char *const filename = ent->d_name;
60330       if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
60331         const unsigned int lf = (unsigned int)std::strlen(filename);
60332         CImg<char> full_filename(lp + lf + 2);
60333 
60334         if (!is_current) {
60335           full_filename.assign(lp + lf + 2);
60336           if (lp) std::memcpy(full_filename,_path,lp);
60337           full_filename[lp] = '/';
60338           std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
60339         } else full_filename.assign(filename,lf + 1);
60340 
60341         struct stat st;
60342         if (stat(full_filename,&st)==-1) continue;
60343         const bool is_directory = (st.st_mode & S_IFDIR)!=0;
60344         if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
60345           if (include_path) {
60346             if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
60347               full_filename.move_to(res);
60348           } else {
60349             if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
60350               CImg<char>(filename,lf + 1).move_to(res);
60351           }
60352         }
60353       }
60354     }
60355     closedir(dir);
60356 #endif
60357 
60358     // Sort resulting list by lexicographic order.
60359     if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
60360 
60361     return res;
60362   }
60363 
60364   //! Try to guess format from an image file.
60365   /**
60366      \param file Input file (can be \c 0 if \c filename is set).
60367      \param filename Filename, as a C-string (can be \c 0 if \c file is set).
60368      \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
60369   **/
60370   inline const char *ftype(std::FILE *const file, const char *const filename) {
60371     if (!file && !filename)
60372       throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
60373     static const char
60374       *const _pnm = "pnm",
60375       *const _pfm = "pfm",
60376       *const _bmp = "bmp",
60377       *const _gif = "gif",
60378       *const _jpg = "jpg",
60379       *const _off = "off",
60380       *const _pan = "pan",
60381       *const _png = "png",
60382       *const _tif = "tif",
60383       *const _inr = "inr",
60384       *const _dcm = "dcm";
60385     const char *f_type = 0;
60386     CImg<char> header;
60387     const unsigned int omode = cimg::exception_mode();
60388     cimg::exception_mode(0);
60389     try {
60390       header._load_raw(file,filename,512,1,1,1,false,false,0);
60391       const unsigned char *const uheader = (unsigned char*)header._data;
60392       if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF.
60393       else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE.
60394       else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE.
60395       else if (!std::strncmp(header.data() + 128,"DICM",4)) f_type = _dcm; // DICOM.
60396       else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg;  // JPEG.
60397       else if (header[0]=='B' && header[1]=='M') f_type = _bmp;  // BMP.
60398       else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF.
60399                (header[4]=='7' || header[4]=='9')) f_type = _gif;
60400       else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&  // PNG.
60401                uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
60402       else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // TIFF.
60403       else { // PNM or PFM.
60404         CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
60405         cimglist_for(_header,l) {
60406           if (_header(l,0)=='#') continue;
60407           if (_header[l]._height==2 && _header(l,0)=='P') {
60408             const char c = _header(l,1);
60409             if (c=='f' || c=='F') { f_type = _pfm; break; }
60410             if (c>='1' && c<='9') { f_type = _pnm; break; }
60411           }
60412           f_type = 0; break;
60413         }
60414       }
60415     } catch (CImgIOException&) { }
60416     cimg::exception_mode(omode);
60417     return f_type;
60418   }
60419 
60420   //! Load file from network as a local temporary file.
60421   /**
60422      \param url URL of the filename, as a C-string.
60423      \param[out] filename_local C-string containing the path to a local copy of \c filename.
60424      \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
60425      \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
60426      \param referer Referer used, as a C-string.
60427      \return Value of \c filename_local.
60428      \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
60429   **/
60430   inline char *load_network(const char *const url, char *const filename_local,
60431                             const unsigned int timeout, const bool try_fallback,
60432                             const char *const referer) {
60433     if (!url)
60434       throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
60435     if (!filename_local)
60436       throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
60437 
60438     const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
60439     CImg<char> ext = CImg<char>::string(_ext);
60440     std::FILE *file = 0;
60441     *filename_local = 0;
60442     if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
60443     else cimg::strwindows_reserved(ext);
60444     do {
60445       cimg_snprintf(filename_local,256,"%s%c%s%s",
60446                     cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
60447       if ((file=std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
60448     } while (file);
60449 
60450 #ifdef cimg_use_curl
60451     const unsigned int omode = cimg::exception_mode();
60452     cimg::exception_mode(0);
60453     try {
60454       CURL *curl = 0;
60455       CURLcode res;
60456       curl = curl_easy_init();
60457       if (curl) {
60458         file = cimg::fopen(filename_local,"wb");
60459         curl_easy_setopt(curl,CURLOPT_URL,url);
60460         curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
60461         curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
60462         curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
60463         curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
60464         curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
60465         if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
60466         if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
60467         if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
60468         res = curl_easy_perform(curl);
60469         curl_easy_cleanup(curl);
60470         cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
60471         const cimg_ulong siz = cimg::ftell(file);
60472         cimg::fclose(file);
60473         if (siz>0 && res==CURLE_OK) {
60474           cimg::exception_mode(omode);
60475           return filename_local;
60476         } else std::remove(filename_local);
60477       }
60478     } catch (...) { }
60479     cimg::exception_mode(omode);
60480     if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
60481 #endif
60482 
60483     CImg<char> command((unsigned int)std::strlen(url) + 64);
60484     cimg::unused(try_fallback);
60485 
60486     // Try with 'curl' first.
60487     if (timeout) {
60488       if (referer)
60489         cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
60490                       cimg::curl_path(),referer,timeout,filename_local,url);
60491       else
60492         cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
60493                       cimg::curl_path(),timeout,filename_local,url);
60494     } else {
60495       if (referer)
60496         cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"",
60497                       cimg::curl_path(),referer,filename_local,url);
60498       else
60499         cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"",
60500                       cimg::curl_path(),filename_local,url);
60501     }
60502     cimg::system(command);
60503 
60504     if (!(file = std_fopen(filename_local,"rb"))) {
60505 
60506       // Try with 'wget' otherwise.
60507       if (timeout) {
60508         if (referer)
60509           cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60510                         cimg::wget_path(),referer,timeout,filename_local,url);
60511         else
60512           cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60513                         cimg::wget_path(),timeout,filename_local,url);
60514       } else {
60515         if (referer)
60516           cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60517                         cimg::wget_path(),referer,filename_local,url);
60518         else
60519           cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60520                         cimg::wget_path(),filename_local,url);
60521       }
60522       cimg::system(command);
60523 
60524       if (!(file = std_fopen(filename_local,"rb")))
60525         throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
60526                               "'wget' or 'curl'.",url);
60527       cimg::fclose(file);
60528 
60529       // Try gunzip it.
60530       cimg_snprintf(command,command._width,"%s.gz",filename_local);
60531       std::rename(filename_local,command);
60532       cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"",
60533                     gunzip_path(),filename_local);
60534       cimg::system(command);
60535       file = std_fopen(filename_local,"rb");
60536       if (!file) {
60537         cimg_snprintf(command,command._width,"%s.gz",filename_local);
60538         std::rename(command,filename_local);
60539         file = std_fopen(filename_local,"rb");
60540       }
60541     }
60542     cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
60543     if (std::ftell(file)<=0)
60544       throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
60545                             "'wget' or 'curl'.",url);
60546     cimg::fclose(file);
60547     return filename_local;
60548   }
60549 
60550   // Implement a tic/toc mechanism to display elapsed time of algorithms.
60551   inline cimg_ulong tictoc(const bool is_tic) {
60552     cimg::mutex(2);
60553     static CImg<cimg_ulong> times(64);
60554     static unsigned int pos = 0;
60555     const cimg_ulong t1 = cimg::time();
60556     if (is_tic) {
60557       // Tic
60558       times[pos++] = t1;
60559       if (pos>=times._width)
60560         throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
60561       cimg::mutex(2,0);
60562       return t1;
60563     }
60564 
60565     // Toc
60566     if (!pos)
60567       throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
60568     const cimg_ulong
60569       t0 = times[--pos],
60570       dt = t1>=t0?(t1 - t0):cimg::type<cimg_ulong>::max();
60571     const unsigned int
60572       edays = (unsigned int)(dt/86400000.0),
60573       ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
60574       emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
60575       esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
60576       ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
60577     if (!edays && !ehours && !emin && !esec)
60578       std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
60579                    cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
60580     else {
60581       if (!edays && !ehours && !emin)
60582         std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
60583                      cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
60584       else {
60585         if (!edays && !ehours)
60586           std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
60587                        cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
60588         else{
60589           if (!edays)
60590             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
60591                          cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
60592           else{
60593             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
60594                          cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
60595           }
60596         }
60597       }
60598     }
60599     cimg::mutex(2,0);
60600     return dt;
60601   }
60602 
60603   // Return a temporary string describing the size of a memory buffer.
60604   inline const char *strbuffersize(const cimg_ulong size) {
60605     static CImg<char> res(256);
60606     cimg::mutex(5);
60607     if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
60608     else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
60609     else if (size<1024*1024*1024LU) {
60610       const float nsize = size/(1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
60611     } else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
60612     cimg::mutex(5,0);
60613     return res;
60614   }
60615 
60616   //! Display a simple dialog box, and wait for the user's response.
60617   /**
60618      \param title Title of the dialog window.
60619      \param msg Main message displayed inside the dialog window.
60620      \param button1_label Label of the 1st button.
60621      \param button2_label Label of the 2nd button (\c 0 to hide button).
60622      \param button3_label Label of the 3rd button (\c 0 to hide button).
60623      \param button4_label Label of the 4th button (\c 0 to hide button).
60624      \param button5_label Label of the 5th button (\c 0 to hide button).
60625      \param button6_label Label of the 6th button (\c 0 to hide button).
60626      \param logo Image logo displayed at the left of the main message.
60627      \param is_centered Tells if the dialog window must be centered on the screen.
60628      \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
60629      \note
60630      - Up to 6 buttons can be defined in the dialog window.
60631      - The function returns when a user clicked one of the button or closed the dialog window.
60632      - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box.
60633      At least one button must be specified.
60634   **/
60635   template<typename t>
60636   inline int dialog(const char *const title, const char *const msg,
60637                     const char *const button1_label, const char *const button2_label,
60638                     const char *const button3_label, const char *const button4_label,
60639                     const char *const button5_label, const char *const button6_label,
60640                     const CImg<t>& logo, const bool is_centered=false) {
60641 #if cimg_display==0
60642     cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
60643                  logo._data,is_centered);
60644     throw CImgIOException("cimg::dialog(): No display available.");
60645 #else
60646     static const unsigned char
60647       black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
60648 
60649     // Create buttons and canvas graphics
60650     CImgList<unsigned char> buttons, cbuttons, sbuttons;
60651     if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
60652       if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
60653         if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
60654           if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
60655             if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
60656               if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
60657               }}}}}}
60658     if (!buttons._width)
60659       throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
60660     cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
60661 
60662     unsigned int bw = 0, bh = 0;
60663     cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
60664     bw+=8; bh+=8;
60665     if (bw<64) bw = 64;
60666     if (bw>128) bw = 128;
60667     if (bh<24) bh = 24;
60668     if (bh>48) bh = 48;
60669 
60670     CImg<unsigned char> button(bw,bh,1,3);
60671     button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
60672     button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
60673     button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
60674     button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
60675     CImg<unsigned char> sbutton(bw,bh,1,3);
60676     sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
60677     sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
60678     sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
60679     sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
60680     sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
60681     sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
60682     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);
60683     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);
60684     CImg<unsigned char> cbutton(bw,bh,1,3);
60685     cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
60686       draw_rectangle(2,2,bw - 3,bh - 3,gray);
60687     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);
60688     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);
60689 
60690     cimglist_for(buttons,ll) {
60691       CImg<unsigned char>(cbutton).
60692         draw_image(1 + (bw  -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
60693         move_to(cbuttons);
60694       CImg<unsigned char>(sbutton).
60695         draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
60696         move_to(sbuttons);
60697       CImg<unsigned char>(button).
60698         draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
60699         move_to(buttons[ll]);
60700     }
60701 
60702     CImg<unsigned char> canvas;
60703     if (msg)
60704       ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
60705 
60706     const unsigned int
60707       bwall = (buttons._width - 1)*(12 + bw) + bw,
60708       w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
60709       h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
60710       lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
60711       ly = (h - 12 - bh - logo._height)/2,
60712       tx = lx + logo._width + 12,
60713       ty = (h - 12 - bh - canvas._height)/2,
60714       bx = (w - bwall)/2,
60715       by = h - 12 - bh;
60716 
60717     if (canvas._data)
60718       canvas = CImg<unsigned char>(w,h,1,3).
60719         draw_rectangle(0,0,w - 1,h - 1,gray).
60720         draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
60721         draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
60722         draw_image(tx,ty,canvas);
60723     else
60724       canvas = CImg<unsigned char>(w,h,1,3).
60725         draw_rectangle(0,0,w - 1,h - 1,gray).
60726         draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
60727         draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
60728     if (logo._data) canvas.draw_image(lx,ly,logo);
60729 
60730     unsigned int xbuttons[6] = { 0 };
60731     cimglist_for(buttons,lll) { xbuttons[lll] = bx + (bw + 12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
60732 
60733     // Open window and enter events loop
60734     CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
60735     if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
60736                                (CImgDisplay::screen_height() - disp.height())/2);
60737     bool stop_flag = false, refresh = false;
60738     int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
60739     while (!disp.is_closed() && !stop_flag) {
60740       if (refresh) {
60741         if (clicked>=0)
60742           CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
60743         else {
60744           if (selected>=0)
60745             CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
60746           else canvas.display(disp);
60747         }
60748         refresh = false;
60749       }
60750       disp.wait(15);
60751       if (disp.is_resized()) disp.resize(disp,false);
60752 
60753       if (disp.button()&1)  {
60754         oclicked = clicked;
60755         clicked = -1;
60756         cimglist_for(buttons,l)
60757           if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
60758               disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
60759             clicked = selected = l;
60760             refresh = true;
60761           }
60762         if (clicked!=oclicked) refresh = true;
60763       } else if (clicked>=0) stop_flag = true;
60764 
60765       if (disp.key()) {
60766         oselected = selected;
60767         switch (disp.key()) {
60768         case cimg::keyESC : selected = -1; stop_flag = true; break;
60769         case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
60770         case cimg::keyTAB :
60771         case cimg::keyARROWRIGHT :
60772         case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
60773         case cimg::keyARROWLEFT :
60774         case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
60775         }
60776         disp.set_key();
60777         if (selected!=oselected) refresh = true;
60778       }
60779     }
60780     if (!disp) selected = -1;
60781     return selected;
60782 #endif
60783   }
60784 
60785   //! Display a simple dialog box, and wait for the user's response \specialization.
60786   inline int dialog(const char *const title, const char *const msg,
60787                     const char *const button1_label, const char *const button2_label, const char *const button3_label,
60788                     const char *const button4_label, const char *const button5_label, const char *const button6_label,
60789                     const bool is_centered) {
60790     return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
60791                   CImg<unsigned char>::_logo40x38(),is_centered);
60792   }
60793 
60794   //! Evaluate math expression.
60795   /**
60796      \param expression C-string describing the formula to evaluate.
60797      \param x Value of the pre-defined variable \c x.
60798      \param y Value of the pre-defined variable \c y.
60799      \param z Value of the pre-defined variable \c z.
60800      \param c Value of the pre-defined variable \c c.
60801      \return Result of the formula evaluation.
60802      \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
60803      \par Example
60804      \code
60805      const double
60806      res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2),  // will return '1'.
60807      res2 = cimg::eval(0,1,1);                    // will return '1' too.
60808      \endcode
60809   **/
60810   inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
60811     static const CImg<float> empty;
60812     return empty.eval(expression,x,y,z,c);
60813   }
60814 
60815   template<typename t>
60816   inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
60817     static const CImg<float> empty;
60818     return empty.eval(expression,xyzc);
60819   }
60820 
60821   // End of cimg:: namespace
60822 }
60823 
60824   // End of cimg_library:: namespace
60825 }
60826 
60827 //! Short alias name.
60828 namespace cil = cimg_library_suffixed;
60829 
60830 #ifdef _cimg_redefine_False
60831 #define False 0
60832 #endif
60833 #ifdef _cimg_redefine_True
60834 #define True 1
60835 #endif
60836 #ifdef _cimg_redefine_min
60837 #define min(a,b) (((a)<(b))?(a):(b))
60838 #endif
60839 #ifdef _cimg_redefine_max
60840 #define max(a,b) (((a)>(b))?(a):(b))
60841 #endif
60842 #ifdef _cimg_redefine_PI
60843 #define PI 3.141592653589793238462643383
60844 #endif
60845 #ifdef _MSC_VER
60846 #pragma warning(pop)
60847 #endif
60848 
60849 #endif
60850 // Local Variables:
60851 // mode: c++
60852 // End:
60853