1 /*
2  #
3  #  File            : CImg.h
4  #                    ( C++ header file )
5  #
6  #  Description     : 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 Tschumperlé
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://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://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://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 298
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 #define cimg_str(x) #x
84 #define cimg_str2(x) cimg_str(x)
85 
86 // Detect/configure OS variables.
87 //
88 // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
89 //                      '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
90 //                      '2' for Microsoft Windows.
91 //                      (auto-detection is performed if 'cimg_OS' is not set by the user).
92 #ifndef cimg_OS
93 #if defined(unix)        || defined(__unix)      || defined(__unix__) \
94  || defined(linux)       || defined(__linux)     || defined(__linux__) \
95  || defined(sun)         || defined(__sun) \
96  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
97  || defined(__FreeBSD__) || defined (__DragonFly__) \
98  || defined(sgi)         || defined(__sgi) \
99  || defined(__OSX__)     || defined(__MACOSX__)  || defined(__APPLE__) \
100  || defined(__CYGWIN__)
101 #define cimg_OS 1
102 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
103    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
104 #define cimg_OS 2
105 #else
106 #define cimg_OS 0
107 #endif
108 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
109 #error CImg Library: Invalid configuration variable 'cimg_OS'.
110 #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
111 #endif
112 #ifndef cimg_date
113 #define cimg_date __DATE__
114 #endif
115 #ifndef cimg_time
116 #define cimg_time __TIME__
117 #endif
118 
119 // Disable silly warnings on some Microsoft VC++ compilers.
120 #ifdef _MSC_VER
121 #pragma warning(push)
122 #pragma warning(disable:4127)
123 #pragma warning(disable:4244)
124 #pragma warning(disable:4311)
125 #pragma warning(disable:4312)
126 #pragma warning(disable:4319)
127 #pragma warning(disable:4512)
128 #pragma warning(disable:4571)
129 #pragma warning(disable:4640)
130 #pragma warning(disable:4706)
131 #pragma warning(disable:4710)
132 #pragma warning(disable:4800)
133 #pragma warning(disable:4804)
134 #pragma warning(disable:4820)
135 #pragma warning(disable:4996)
136 
137 #ifndef _CRT_SECURE_NO_DEPRECATE
138 #define _CRT_SECURE_NO_DEPRECATE 1
139 #endif
140 #ifndef _CRT_SECURE_NO_WARNINGS
141 #define _CRT_SECURE_NO_WARNINGS 1
142 #endif
143 #ifndef _CRT_NONSTDC_NO_DEPRECATE
144 #define _CRT_NONSTDC_NO_DEPRECATE 1
145 #endif
146 #endif
147 
148 // Define correct string functions for each compiler and OS.
149 #if cimg_OS==2 && defined(_MSC_VER)
150 #define cimg_sscanf std::sscanf
151 #define cimg_sprintf std::sprintf
152 #define cimg_snprintf cimg::_snprintf
153 #define cimg_vsnprintf cimg::_vsnprintf
154 #else
155 #include <stdio.h>
156 #if defined(__MACOSX__) || defined(__APPLE__)
157 #define cimg_sscanf cimg::_sscanf
158 #define cimg_sprintf cimg::_sprintf
159 #define cimg_snprintf cimg::_snprintf
160 #define cimg_vsnprintf cimg::_vsnprintf
161 #else
162 #define cimg_sscanf std::sscanf
163 #define cimg_sprintf std::sprintf
164 #define cimg_snprintf snprintf
165 #define cimg_vsnprintf vsnprintf
166 #endif
167 #endif
168 
169 // Include OS-specific headers.
170 #if cimg_OS==1
171 #include <sys/types.h>
172 #include <sys/time.h>
173 #include <sys/stat.h>
174 #include <unistd.h>
175 #include <dirent.h>
176 #include <fnmatch.h>
177 #elif cimg_OS==2
178 #ifndef NOMINMAX
179 #define NOMINMAX
180 #endif
181 #ifndef WIN32_LEAN_AND_MEAN
182 #define WIN32_LEAN_AND_MEAN
183 #endif
184 #include <windows.h>
185 #ifndef _WIN32_IE
186 #define _WIN32_IE 0x0400
187 #endif
188 #include <shlobj.h>
189 #include <process.h>
190 #include <io.h>
191 enum {FALSE_WIN = 0};
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 OpenMP support.
288 // (http://www.openmp.org)
289 //
290 // Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+).
291 //
292 // OpenMP directives are used in many CImg functions to get
293 // advantages of multi-core CPUs.
294 #if !defined(cimg_use_openmp)
295 #ifdef _OPENMP
296 #define cimg_use_openmp 1
297 #else
298 #define cimg_use_openmp 0
299 #endif
300 #endif
301 #if cimg_use_openmp!=0
302 #include <omp.h>
303 #define cimg_pragma_openmp(p) cimg_pragma(omp p)
304 #else
305 #define cimg_pragma_openmp(p)
306 #endif
307 
308 // Configure the 'abort' signal handler (does nothing by default).
309 // A typical signal handler can be defined in your own source like this:
310 // #define cimg_abort_test if (is_abort) throw CImgAbortException("")
311 //
312 // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method.
313 // 'cimg_abort_test2' does the same but is called more often (in inner loops).
314 #if defined(cimg_abort_test) && cimg_use_openmp!=0
315 
316 // Define abort macros to be used with OpenMP.
317 #ifndef _cimg_abort_init_openmp
318 #define _cimg_abort_init_openmp bool _cimg_abort_go_openmp = true; cimg::unused(_cimg_abort_go_openmp)
319 #endif
320 #ifndef _cimg_abort_try_openmp
321 #define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try
322 #endif
323 #ifndef _cimg_abort_catch_openmp
324 #define _cimg_abort_catch_openmp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; }
325 #endif
326 #ifndef _cimg_abort_catch_fill_openmp
327 #define _cimg_abort_catch_fill_openmp \
328   catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error); \
329                              cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; }
330 #endif
331 #ifdef cimg_abort_test2
332 #ifndef _cimg_abort_try_openmp2
333 #define _cimg_abort_try_openmp2 _cimg_abort_try_openmp
334 #endif
335 #ifndef _cimg_abort_catch_openmp2
336 #define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp
337 #endif
338 #endif
339 #endif
340 
341 #ifndef _cimg_abort_init_openmp
342 #define _cimg_abort_init_openmp
343 #endif
344 #ifndef _cimg_abort_try_openmp
345 #define _cimg_abort_try_openmp
346 #endif
347 #ifndef _cimg_abort_catch_openmp
348 #define _cimg_abort_catch_openmp
349 #endif
350 #ifndef _cimg_abort_try_openmp2
351 #define _cimg_abort_try_openmp2
352 #endif
353 #ifndef _cimg_abort_catch_openmp2
354 #define _cimg_abort_catch_openmp2
355 #endif
356 #ifndef _cimg_abort_catch_fill_openmp
357 #define _cimg_abort_catch_fill_openmp
358 #endif
359 #ifndef cimg_abort_init
360 #define cimg_abort_init
361 #endif
362 #ifndef cimg_abort_test
363 #define cimg_abort_test
364 #endif
365 #ifndef cimg_abort_test2
366 #define cimg_abort_test2
367 #endif
368 
369 // Configure display framework.
370 //
371 // Define 'cimg_display' to: '0' to disable display capabilities.
372 //                           '1' to use the X-Window framework (X11).
373 //                           '2' to use the Microsoft GDI32 framework.
374 #ifndef cimg_display
375 #if cimg_OS==0
376 #define cimg_display 0
377 #elif cimg_OS==1
378 #define cimg_display 1
379 #elif cimg_OS==2
380 #define cimg_display 2
381 #endif
382 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
383 #error CImg Library: Configuration variable 'cimg_display' is badly defined.
384 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
385 #endif
386 
387 // Include display-specific headers.
388 #if cimg_display==1
389 #include <X11/Xlib.h>
390 #include <X11/Xutil.h>
391 #include <X11/keysym.h>
392 #include <pthread.h>
393 #ifdef cimg_use_xshm
394 #include <sys/ipc.h>
395 #include <sys/shm.h>
396 #include <X11/extensions/XShm.h>
397 #endif
398 #ifdef cimg_use_xrandr
399 #include <X11/extensions/Xrandr.h>
400 #endif
401 #endif
402 #ifndef cimg_appname
403 #define cimg_appname "CImg"
404 #endif
405 
406 // Configure OpenCV support.
407 // (http://opencv.willowgarage.com/wiki/)
408 //
409 // Define 'cimg_use_opencv' to enable OpenCV support.
410 //
411 // OpenCV library may be used to access images from cameras
412 // (see method 'CImg<T>::load_camera()').
413 #ifdef cimg_use_opencv
414 #ifdef True
415 #undef True
416 #define _cimg_redefine_True
417 #endif
418 #ifdef False
419 #undef False
420 #define _cimg_redefine_False
421 #endif
422 #ifdef Status
423 #undef Status
424 #define _cimg_redefine_Status
425 #endif
426 #include <cstddef>
427 #include <opencv2/opencv.hpp>
428 #if CV_MAJOR_VERSION>=3
429 #define _cimg_fourcc cv::VideoWriter::fourcc
430 #define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH
431 #define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT
432 #define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT
433 #else
434 #define _cimg_fourcc CV_FOURCC
435 #define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH
436 #define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT
437 #define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT
438 #endif
439 #endif
440 
441 // Configure LibPNG support.
442 // (http://www.libpng.org)
443 //
444 // Define 'cimg_use_png' to enable LibPNG support.
445 //
446 // PNG library may be used to get a native support of '.png' files.
447 // (see methods 'CImg<T>::{load,save}_png()'.
448 #ifdef cimg_use_png
449 extern "C" {
450 #include "png.h"
451 }
452 #endif
453 
454 // Configure LibJPEG support.
455 // (http://en.wikipedia.org/wiki/Libjpeg)
456 //
457 // Define 'cimg_use_jpeg' to enable LibJPEG support.
458 //
459 // JPEG library may be used to get a native support of '.jpg' files.
460 // (see methods 'CImg<T>::{load,save}_jpeg()').
461 #ifdef cimg_use_jpeg
462 extern "C" {
463 #include "jpeglib.h"
464 #include "setjmp.h"
465 }
466 #endif
467 
468 // Configure LibTIFF support.
469 // (http://www.libtiff.org)
470 //
471 // Define 'cimg_use_tiff' to enable LibTIFF support.
472 //
473 // TIFF library may be used to get a native support of '.tif' files.
474 // (see methods 'CImg[List]<T>::{load,save}_tiff()').
475 #ifdef cimg_use_tiff
476 extern "C" {
477 #define uint64 uint64_hack_
478 #define int64 int64_hack_
479 #include "tiffio.h"
480 #undef uint64
481 #undef int64
482 }
483 #endif
484 
485 // Configure HEIF support
486 // (https://github.com/strukturag/libheif)
487 //
488 // Define 'cimg_use_heif' to enable HEIF support.
489 //
490 // HEIF library may be used to get a native support of '.heic' and '.avif' files.
491 // (see method 'CImg<T>::load_heif()').
492 #ifdef cimg_use_heif
493 #include <libheif/heif_cxx.h>
494 #endif
495 
496 // Configure LibMINC2 support.
497 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
498 //
499 // Define 'cimg_use_minc2' to enable LibMINC2 support.
500 //
501 // MINC2 library may be used to get a native support of '.mnc' files.
502 // (see methods 'CImg<T>::{load,save}_minc2()').
503 #ifdef cimg_use_minc2
504 #include "minc_io_simple_volume.h"
505 #include "minc_1_simple.h"
506 #include "minc_1_simple_rw.h"
507 #endif
508 
509 // Configure Zlib support.
510 // (http://www.zlib.net)
511 //
512 // Define 'cimg_use_zlib' to enable Zlib support.
513 //
514 // Zlib library may be used to allow compressed data in '.cimgz' files
515 // (see methods 'CImg[List]<T>::{load,save}_cimg()').
516 #ifdef cimg_use_zlib
517 extern "C" {
518 #include "zlib.h"
519 }
520 #endif
521 
522 // Configure libcurl support.
523 // (http://curl.haxx.se/libcurl/)
524 //
525 // Define 'cimg_use_curl' to enable libcurl support.
526 //
527 // Libcurl may be used to get a native support of file downloading from the network.
528 // (see method 'cimg::load_network()'.)
529 #ifdef cimg_use_curl
530 #include "curl/curl.h"
531 #endif
532 
533 // Configure Magick++ support.
534 // (http://www.imagemagick.org/Magick++)
535 //
536 // Define 'cimg_use_magick' to enable Magick++ support.
537 //
538 // Magick++ library may be used to get a native support of various image file formats.
539 // (see methods 'CImg<T>::{load,save}()').
540 #ifdef cimg_use_magick
541 #include "Magick++.h"
542 #endif
543 
544 // Configure FFTW3 support.
545 // (http://www.fftw.org)
546 //
547 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
548 //
549 // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
550 // of image data, without restriction on the image size.
551 // (see method 'CImg[List]<T>::FFT()').
552 #ifdef cimg_use_fftw3
553 extern "C" {
554 #include "fftw3.h"
555 }
556 #endif
557 
558 // Configure LibBoard support.
559 // (http://libboard.sourceforge.net/)
560 //
561 // Define 'cimg_use_board' to enable Board support.
562 //
563 // Board library may be used to draw 3D objects in vector-graphics canvas
564 // that can be saved as '.ps' or '.svg' files afterwards.
565 // (see method 'CImg<T>::draw_object3d()').
566 #ifdef cimg_use_board
567 #include "Board.h"
568 #endif
569 
570 // Configure OpenEXR support.
571 // (http://www.openexr.com/)
572 //
573 // Define 'cimg_use_openexr' to enable OpenEXR support.
574 //
575 // OpenEXR library may be used to get a native support of '.exr' files.
576 // (see methods 'CImg<T>::{load,save}_exr()').
577 #ifdef cimg_use_openexr
578 #if __GNUC__>=5
579 #pragma GCC diagnostic push
580 #pragma GCC diagnostic ignored "-Wdeprecated"
581 #pragma GCC diagnostic ignored "-Wdeprecated-copy"
582 #pragma GCC diagnostic ignored "-Wshadow"
583 #endif
584 #include "ImfRgbaFile.h"
585 #include "ImfInputFile.h"
586 #include "ImfChannelList.h"
587 #include "ImfMatrixAttribute.h"
588 #include "ImfArray.h"
589 #if __GNUC__>=5
590 #pragma GCC diagnostic pop
591 #endif
592 #endif
593 
594 // Configure TinyEXR support.
595 // (https://github.com/syoyo/tinyexr)
596 //
597 // Define 'cimg_use_tinyexr' to enable TinyEXR support.
598 //
599 // TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images.
600 #ifdef cimg_use_tinyexr
601 #ifndef TINYEXR_IMPLEMENTATION
602 #define TINYEXR_IMPLEMENTATION
603 #endif
604 #include "tinyexr.h"
605 #endif
606 
607 // Lapack configuration.
608 // (http://www.netlib.org/lapack)
609 //
610 // Define 'cimg_use_lapack' to enable LAPACK support.
611 //
612 // Lapack library may be used in several CImg methods to speed up
613 // matrix computations (eigenvalues, inverse, ...).
614 #ifdef cimg_use_lapack
615 extern "C" {
616   extern void sgetrf_(int*, int*, float*, int*, int*, int*);
617   extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
618   extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
619   extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
620   extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
621   extern void dgetrf_(int*, int*, double*, int*, int*, int*);
622   extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
623   extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
624   extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
625                       int*, double*, int*, double*, int*, int*);
626   extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
627   extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
628   extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
629 }
630 #endif
631 
632 // Check if min/max/PI macros are defined.
633 //
634 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
635 // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
636 // so it '#undef' these macros if necessary, and restore them to reasonable
637 // values at the end of this file.
638 #ifdef min
639 #undef min
640 #define _cimg_redefine_min
641 #endif
642 #ifdef max
643 #undef max
644 #define _cimg_redefine_max
645 #endif
646 #ifdef PI
647 #undef PI
648 #define _cimg_redefine_PI
649 #endif
650 
651 // Define 'cimg_library' namespace suffix.
652 //
653 // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
654 // with several versions of the library at the same time.
655 #ifdef cimg_namespace_suffix
656 #define __cimg_library_suffixed(s) cimg_library_##s
657 #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
658 #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
659 #else
660 #define cimg_library_suffixed cimg_library
661 #endif
662 
663 /*------------------------------------------------------------------------------
664   #
665   # Define user-friendly macros.
666   #
667   # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
668   # code. They are useful to parse command line options, or to write image loops.
669   #
670   ------------------------------------------------------------------------------*/
671 
672 // Macros to define program usage, and retrieve command line arguments.
673 #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
674 #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
675 #define cimg_option(name,_default,usage) cimg_library_suffixed::cimg::option(name,argc,argv,_default,usage)
676 
677 // Macros to define and manipulate local neighborhoods.
678 #define CImg_2x2(I,T) T I[4]; \
679                       T& I##cc = I[0]; T& I##nc = I[1]; \
680                       T& I##cn = I[2]; T& I##nn = I[3]; \
681                       I##cc = I##nc = \
682                       I##cn = I##nn = 0
683 
684 #define CImg_3x3(I,T) T I[9]; \
685                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
686                       T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
687                       T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
688                       I##pp = I##cp = I##np = \
689                       I##pc = I##cc = I##nc = \
690                       I##pn = I##cn = I##nn = 0
691 
692 #define CImg_4x4(I,T) T I[16]; \
693                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
694                       T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
695                       T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
696                       T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
697                       I##pp = I##cp = I##np = I##ap = \
698                       I##pc = I##cc = I##nc = I##ac = \
699                       I##pn = I##cn = I##nn = I##an = \
700                       I##pa = I##ca = I##na = I##aa = 0
701 
702 #define CImg_5x5(I,T) T I[25]; \
703                       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]; \
704                       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]; \
705                       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]; \
706                       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]; \
707                       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]; \
708                       I##bb = I##pb = I##cb = I##nb = I##ab = \
709                       I##bp = I##pp = I##cp = I##np = I##ap = \
710                       I##bc = I##pc = I##cc = I##nc = I##ac = \
711                       I##bn = I##pn = I##cn = I##nn = I##an = \
712                       I##ba = I##pa = I##ca = I##na = I##aa = 0
713 
714 #define CImg_2x2x2(I,T) T I[8]; \
715                       T& I##ccc = I[0]; T& I##ncc = I[1]; \
716                       T& I##cnc = I[2]; T& I##nnc = I[3]; \
717                       T& I##ccn = I[4]; T& I##ncn = I[5]; \
718                       T& I##cnn = I[6]; T& I##nnn = I[7]; \
719                       I##ccc = I##ncc = \
720                       I##cnc = I##nnc = \
721                       I##ccn = I##ncn = \
722                       I##cnn = I##nnn = 0
723 
724 #define CImg_3x3x3(I,T) T I[27]; \
725                       T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
726                       T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
727                       T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
728                       T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
729                       T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
730                       T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
731                       T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
732                       T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
733                       T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
734                       I##ppp = I##cpp = I##npp = \
735                       I##pcp = I##ccp = I##ncp = \
736                       I##pnp = I##cnp = I##nnp = \
737                       I##ppc = I##cpc = I##npc = \
738                       I##pcc = I##ccc = I##ncc = \
739                       I##pnc = I##cnc = I##nnc = \
740                       I##ppn = I##cpn = I##npn = \
741                       I##pcn = I##ccn = I##ncn = \
742                       I##pnn = I##cnn = I##nnn = 0
743 
744 #define cimg_def2x2(img,x,y) \
745   int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \
746       _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1
747 
748 #define cimg_def3x3(img,x,y) \
749   cimg_def2x2(img,x,y); \
750   int _p1##x = x>1?x - 1:0, \
751       _p1##y = y>1?y - 1:0
752 
753 #define cimg_def4x4(img,x,y) \
754   cimg_def3x3(img,x,y); \
755   int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \
756       _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1
757 
758 #define cimg_def5x5(img,x,y) \
759   cimg_def4x4(img,x,y); \
760   int _p2##x = x>2?x - 2:0, \
761       _p2##y = y>2?y - 2:0
762 
763 #define cimg_def6x6(img,x,y) \
764   cimg_def5x5(img,x,y); \
765   int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \
766       _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1
767 
768 #define cimg_def7x7(img,x,y) \
769   cimg_def6x6(img,x,y); \
770   int _p3##x = x>3?x - 3:0, \
771       _p3##y = y>3?y - 3:0
772 
773 #define cimg_def8x8(img,x,y) \
774   cimg_def7x7(img,x,y); \
775   int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \
776       _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1
777 
778 #define cimg_def9x9(img,x,y) \
779   cimg_def8x8(img,x,y); \
780   int _p4##x = x>4?x - 4:0, \
781       _p4##y = y>4?y - 4:0
782 
783 #define cimg_def2x2x2(img,x,y,z) \
784   cimg_def2x2(img,x,y); \
785   int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1
786 
787 #define cimg_def3x3x3(img,x,y,z) \
788   cimg_def2x2x2(img,x,y,z); \
789   int _p1##x = x>1?x - 1:0, \
790       _p1##y = y>1?y - 1:0, \
791       _p1##z = z>1?z - 1:0
792 
793 #define cimg_get2x2(img,x,y,z,c,I,T) \
794   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), \
795   I[3] = (T)(img)(_n1##x,_n1##y,z,c)
796 
797 #define cimg_get3x3(img,x,y,z,c,I,T) \
798   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), \
799   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), \
800   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)
801 
802 #define cimg_get4x4(img,x,y,z,c,I,T) \
803   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), \
804   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), \
805   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), \
806   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), \
807   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), \
808   I[15] = (T)(img)(_n2##x,_n2##y,z,c)
809 
810 #define cimg_get5x5(img,x,y,z,c,I,T) \
811   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), \
812   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), \
813   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), \
814   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), \
815   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), \
816   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), \
817   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), \
818   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), \
819   I[24] = (T)(img)(_n2##x,_n2##y,z,c)
820 
821 #define cimg_get6x6(img,x,y,z,c,I,T) \
822   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), \
823   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), \
824   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), \
825   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), \
826   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), \
827   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), \
828   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), \
829   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), \
830   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), \
831   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), \
832   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), \
833   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)
834 
835 #define cimg_get7x7(img,x,y,z,c,I,T) \
836   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), \
837   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), \
838   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), \
839   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), \
840   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), \
841   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), \
842   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), \
843   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), \
844   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), \
845   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), \
846   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), \
847   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), \
848   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), \
849   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), \
850   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), \
851   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), \
852   I[48] = (T)(img)(_n3##x,_n3##y,z,c)
853 
854 #define cimg_get8x8(img,x,y,z,c,I,T) \
855   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), \
856   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), \
857   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), \
858   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), \
859   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), \
860   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), \
861   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), \
862   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), \
863   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), \
864   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), \
865   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), \
866   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), \
867   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), \
868   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), \
869   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), \
870   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), \
871   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), \
872   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), \
873   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), \
874   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), \
875   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), \
876   I[63] = (T)(img)(_n4##x,_n4##y,z,c);
877 
878 #define cimg_get9x9(img,x,y,z,c,I,T) \
879   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), \
880   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), \
881   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), \
882   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), \
883   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), \
884   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), \
885   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), \
886   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), \
887   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), \
888   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), \
889   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), \
890   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), \
891   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), \
892   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), \
893   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), \
894   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), \
895   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), \
896   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), \
897   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), \
898   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), \
899   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), \
900   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), \
901   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), \
902   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), \
903   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), \
904   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), \
905   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)
906 
907 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
908   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), \
909   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), \
910   I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
911 
912 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
913   I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
914   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), \
915   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), \
916   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), \
917   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), \
918   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), \
919   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), \
920   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), \
921   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), \
922   I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
923 
924 // Macros to perform various image loops.
925 //
926 // These macros are simpler to use than loops with C++ iterators.
927 #define cimg_for(img,ptrs,T_ptrs) \
928   for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
929 #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
930 #define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off)
931 #define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off)
932 
933 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
934 #define cimg_forX(img,x) cimg_for1((img)._width,x)
935 #define cimg_forY(img,y) cimg_for1((img)._height,y)
936 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
937 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
938 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
939 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
940 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
941 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
942 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
943 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
944 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
945 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
946 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
947 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
948 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
949 
950 #define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i)
951 #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
952 #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
953 #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
954 #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
955 #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
956 #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
957 #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
958 #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
959 #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
960 #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
961 #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
962 #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
963 #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
964 #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
965 #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
966 
967 #define cimg_for_in1(bound,i0,i1,i) \
968  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i)
969 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
970 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
971 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
972 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
973 #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)
974 #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)
975 #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)
976 #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)
977 #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)
978 #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)
979 #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)
980 #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)
981 #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)
982 #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)
983 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
984   cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
985 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x)
986 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y)
987 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth  - 1 - (n),z)
988 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c)
989 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
990 #define cimg_for_insideXYZ(img,x,y,z,n) \
991   cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
992 #define cimg_for_insideXYZC(img,x,y,z,c,n) \
993   cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
994 
995 #define cimg_for_out1(boundi,i0,i1,i) \
996  for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i)
997 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
998  for (int j = 0; j<(int)(boundj); ++j) \
999  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
1000   ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i))
1001 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
1002  for (int k = 0; k<(int)(boundk); ++k) \
1003  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
1004  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
1005   ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i))
1006 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
1007  for (int l = 0; l<(int)(boundl); ++l) \
1008  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
1009  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
1010  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \
1011   i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i))
1012 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
1013 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
1014 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
1015 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
1016 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
1017 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
1018 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
1019 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
1020 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
1021 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
1022 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
1023   cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
1024 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
1025   cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
1026 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
1027   cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
1028 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
1029   cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
1030 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1031  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
1032 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x)
1033 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y)
1034 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z)
1035 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c)
1036 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
1037 #define cimg_for_borderXYZ(img,x,y,z,n) \
1038   cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
1039 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
1040  cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \
1041                   (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c)
1042 
1043 #define cimg_for_spiralXY(img,x,y) \
1044  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
1045       --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\
1046       ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1)
1047 
1048 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
1049  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
1050       _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \
1051       _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \
1052       _counter = _dx, \
1053       _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
1054       _counter>=0; \
1055       --_counter, x+=_steep? \
1056       (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
1057       (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
1058 
1059 #define cimg_for2(bound,i) \
1060  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
1061       _n1##i<(int)(bound) || i==--_n1##i; \
1062       ++i, ++_n1##i)
1063 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
1064 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
1065 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
1066 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
1067 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
1068 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
1069 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
1070 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
1071 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
1072 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
1073 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
1074 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
1075 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
1076 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
1077 
1078 #define cimg_for_in2(bound,i0,i1,i) \
1079  for (int i = (int)(i0)<0?0:(int)(i0), \
1080       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
1081       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
1082       ++i, ++_n1##i)
1083 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
1084 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
1085 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
1086 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
1087 #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)
1088 #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)
1089 #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)
1090 #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)
1091 #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)
1092 #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)
1093 #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)
1094 #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)
1095 #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)
1096 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1097   cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1098 
1099 #define cimg_for3(bound,i) \
1100  for (int i = 0, _p1##i = 0, \
1101       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
1102       _n1##i<(int)(bound) || i==--_n1##i; \
1103       _p1##i = i++, ++_n1##i)
1104 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
1105 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
1106 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
1107 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
1108 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
1109 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
1110 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
1111 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
1112 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
1113 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
1114 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
1115 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
1116 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
1117 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
1118 
1119 #define cimg_for_in3(bound,i0,i1,i) \
1120  for (int i = (int)(i0)<0?0:(int)(i0), \
1121       _p1##i = i - 1<0?0:i - 1, \
1122       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
1123       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
1124       _p1##i = i++, ++_n1##i)
1125 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
1126 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
1127 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
1128 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
1129 #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)
1130 #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)
1131 #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)
1132 #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)
1133 #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)
1134 #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)
1135 #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)
1136 #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)
1137 #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)
1138 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1139   cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1140 
1141 #define cimg_for4(bound,i) \
1142  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1143       _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
1144       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
1145       _p1##i = i++, ++_n1##i, ++_n2##i)
1146 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
1147 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
1148 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
1149 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
1150 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
1151 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
1152 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
1153 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
1154 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
1155 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
1156 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
1157 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
1158 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
1159 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
1160 
1161 #define cimg_for_in4(bound,i0,i1,i) \
1162  for (int i = (int)(i0)<0?0:(int)(i0), \
1163       _p1##i = i - 1<0?0:i - 1, \
1164       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1165       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
1166       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
1167       _p1##i = i++, ++_n1##i, ++_n2##i)
1168 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
1169 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
1170 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
1171 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
1172 #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)
1173 #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)
1174 #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)
1175 #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)
1176 #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)
1177 #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)
1178 #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)
1179 #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)
1180 #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)
1181 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1182   cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1183 
1184 #define cimg_for5(bound,i) \
1185  for (int i = 0, _p2##i = 0, _p1##i = 0, \
1186       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1187       _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
1188       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
1189       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
1190 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
1191 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
1192 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
1193 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
1194 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
1195 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
1196 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
1197 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
1198 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
1199 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
1200 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
1201 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
1202 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
1203 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
1204 
1205 #define cimg_for_in5(bound,i0,i1,i) \
1206  for (int i = (int)(i0)<0?0:(int)(i0), \
1207       _p2##i = i - 2<0?0:i - 2, \
1208       _p1##i = i - 1<0?0:i - 1, \
1209       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1210       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
1211       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
1212       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
1213 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
1214 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
1215 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
1216 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
1217 #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)
1218 #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)
1219 #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)
1220 #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)
1221 #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)
1222 #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)
1223 #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)
1224 #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)
1225 #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)
1226 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1227   cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1228 
1229 #define cimg_for6(bound,i) \
1230  for (int i = 0, _p2##i = 0, _p1##i = 0, \
1231       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1232       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1233       _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
1234       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1235       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1236 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
1237 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
1238 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
1239 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
1240 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
1241 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
1242 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
1243 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
1244 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
1245 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
1246 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
1247 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
1248 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
1249 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
1250 
1251 #define cimg_for_in6(bound,i0,i1,i) \
1252  for (int i = (int)(i0)<0?0:(int)(i0), \
1253       _p2##i = i - 2<0?0:i - 2, \
1254       _p1##i = i - 1<0?0:i - 1, \
1255       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1256       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1257       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
1258       i<=(int)(i1) && \
1259       (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1260       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1261 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
1262 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
1263 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
1264 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
1265 #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)
1266 #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)
1267 #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)
1268 #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)
1269 #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)
1270 #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)
1271 #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)
1272 #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)
1273 #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)
1274 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1275   cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1276 
1277 #define cimg_for7(bound,i) \
1278  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1279       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1280       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1281       _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
1282       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1283       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1284 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
1285 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
1286 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
1287 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
1288 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
1289 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
1290 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
1291 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
1292 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
1293 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
1294 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
1295 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
1296 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
1297 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
1298 
1299 #define cimg_for_in7(bound,i0,i1,i) \
1300  for (int i = (int)(i0)<0?0:(int)(i0), \
1301       _p3##i = i - 3<0?0:i - 3, \
1302       _p2##i = i - 2<0?0:i - 2, \
1303       _p1##i = i - 1<0?0:i - 1, \
1304       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1305       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1306       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
1307       i<=(int)(i1) && \
1308       (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1309       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1310 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
1311 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
1312 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
1313 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
1314 #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)
1315 #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)
1316 #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)
1317 #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)
1318 #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)
1319 #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)
1320 #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)
1321 #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)
1322 #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)
1323 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1324   cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1325 
1326 #define cimg_for8(bound,i) \
1327  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1328       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1329       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1330       _n3##i = 3>=(bound)?(int)(bound) - 1:3, \
1331       _n4##i = 4>=(bound)?(int)(bound) - 1:4; \
1332       _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1333       i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1334       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1335 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
1336 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
1337 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
1338 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1339 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1340 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1341 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1342 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1343 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1344 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1345 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1346 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1347 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1348 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1349 
1350 #define cimg_for_in8(bound,i0,i1,i) \
1351  for (int i = (int)(i0)<0?0:(int)(i0), \
1352       _p3##i = i - 3<0?0:i - 3, \
1353       _p2##i = i - 2<0?0:i - 2, \
1354       _p1##i = i - 1<0?0:i - 1, \
1355       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1356       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1357       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
1358       _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
1359       i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1360       i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1361       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1362 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1363 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1364 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1365 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1366 #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)
1367 #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)
1368 #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)
1369 #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)
1370 #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)
1371 #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)
1372 #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)
1373 #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)
1374 #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)
1375 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1376   cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1377 
1378 #define cimg_for9(bound,i) \
1379   for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1380        _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \
1381        _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \
1382        _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \
1383        _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \
1384        _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1385        i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1386        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1387 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1388 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1389 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1390 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1391 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1392 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1393 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1394 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1395 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1396 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1397 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1398 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1399 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1400 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1401 
1402 #define cimg_for_in9(bound,i0,i1,i) \
1403   for (int i = (int)(i0)<0?0:(int)(i0), \
1404        _p4##i = i - 4<0?0:i - 4, \
1405        _p3##i = i - 3<0?0:i - 3, \
1406        _p2##i = i - 2<0?0:i - 2, \
1407        _p1##i = i - 1<0?0:i - 1, \
1408        _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1409        _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1410        _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
1411        _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
1412        i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1413        i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1414        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1415 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1416 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1417 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1418 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1419 #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)
1420 #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)
1421 #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)
1422 #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)
1423 #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)
1424 #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)
1425 #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)
1426 #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)
1427 #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)
1428 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1429   cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1430 
1431 #define cimg_for2x2(img,x,y,z,c,I,T) \
1432   cimg_for2((img)._height,y) for (int x = 0, \
1433    _n1##x = (int)( \
1434    (I[0] = (T)(img)(0,y,z,c)), \
1435    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1436    1>=(img)._width?(img).width() - 1:1);  \
1437    (_n1##x<(img).width() && ( \
1438    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1439    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1440    x==--_n1##x; \
1441    I[0] = I[1], \
1442    I[2] = I[3], \
1443    ++x, ++_n1##x)
1444 
1445 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1446   cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1447    _n1##x = (int)( \
1448    (I[0] = (T)(img)(x,y,z,c)), \
1449    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1450    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
1451    x<=(int)(x1) && ((_n1##x<(img).width() && (  \
1452    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1453    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1454    x==--_n1##x); \
1455    I[0] = I[1], \
1456    I[2] = I[3], \
1457    ++x, ++_n1##x)
1458 
1459 #define cimg_for3x3(img,x,y,z,c,I,T) \
1460   cimg_for3((img)._height,y) for (int x = 0, \
1461    _p1##x = 0, \
1462    _n1##x = (int)( \
1463    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1464    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1465    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
1466    1>=(img)._width?(img).width() - 1:1); \
1467    (_n1##x<(img).width() && ( \
1468    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1469    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1470    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1471    x==--_n1##x; \
1472    I[0] = I[1], I[1] = I[2], \
1473    I[3] = I[4], I[4] = I[5], \
1474    I[6] = I[7], I[7] = I[8], \
1475    _p1##x = x++, ++_n1##x)
1476 
1477 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1478   cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1479    _p1##x = x - 1<0?0:x - 1, \
1480    _n1##x = (int)( \
1481    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1482    (I[3] = (T)(img)(_p1##x,y,z,c)), \
1483    (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1484    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1485    (I[4] = (T)(img)(x,y,z,c)), \
1486    (I[7] = (T)(img)(x,_n1##y,z,c)), \
1487    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
1488    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1489    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1490    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1491    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1492    x==--_n1##x);            \
1493    I[0] = I[1], I[1] = I[2], \
1494    I[3] = I[4], I[4] = I[5], \
1495    I[6] = I[7], I[7] = I[8], \
1496    _p1##x = x++, ++_n1##x)
1497 
1498 #define cimg_for4x4(img,x,y,z,c,I,T) \
1499   cimg_for4((img)._height,y) for (int x = 0, \
1500    _p1##x = 0, \
1501    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1502    _n2##x = (int)( \
1503    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1504    (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1505    (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1506    (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1507    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1508    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1509    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1510    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1511    2>=(img)._width?(img).width() - 1:2); \
1512    (_n2##x<(img).width() && ( \
1513    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1514    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1515    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1516    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1517    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1518    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1519    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1520    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1521    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1522    _p1##x = x++, ++_n1##x, ++_n2##x)
1523 
1524 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1525   cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1526    _p1##x = x - 1<0?0:x - 1, \
1527    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1528    _n2##x = (int)( \
1529    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1530    (I[4] = (T)(img)(_p1##x,y,z,c)), \
1531    (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1532    (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1533    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1534    (I[5] = (T)(img)(x,y,z,c)), \
1535    (I[9] = (T)(img)(x,_n1##y,z,c)), \
1536    (I[13] = (T)(img)(x,_n2##y,z,c)), \
1537    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1538    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1539    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1540    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1541    x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
1542    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1543    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1544    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1545    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1546    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1547    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1548    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1549    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1550    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1551    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1552    _p1##x = x++, ++_n1##x, ++_n2##x)
1553 
1554 #define cimg_for5x5(img,x,y,z,c,I,T) \
1555  cimg_for5((img)._height,y) for (int x = 0, \
1556    _p2##x = 0, _p1##x = 0, \
1557    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1558    _n2##x = (int)( \
1559    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1560    (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1561    (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1562    (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1563    (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1564    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1565    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1566    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1567    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1568    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)),  \
1569    2>=(img)._width?(img).width() - 1:2); \
1570    (_n2##x<(img).width() && ( \
1571    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1572    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1573    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1574    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1575    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1576    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1577    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1578    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1579    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1580    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1581    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1582    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1583 
1584 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1585  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1586    _p2##x = x - 2<0?0:x - 2, \
1587    _p1##x = x - 1<0?0:x - 1, \
1588    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1589    _n2##x = (int)( \
1590    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1591    (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1592    (I[10] = (T)(img)(_p2##x,y,z,c)), \
1593    (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1594    (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1595    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1596    (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1597    (I[11] = (T)(img)(_p1##x,y,z,c)), \
1598    (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1599    (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1600    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1601    (I[7] = (T)(img)(x,_p1##y,z,c)), \
1602    (I[12] = (T)(img)(x,y,z,c)), \
1603    (I[17] = (T)(img)(x,_n1##y,z,c)), \
1604    (I[22] = (T)(img)(x,_n2##y,z,c)), \
1605    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1606    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1607    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1608    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1609    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1610    x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
1611    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1612    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1613    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1614    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1615    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1616    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1617    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1618    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1619    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1620    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1621    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1622    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1623    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1624 
1625 #define cimg_for6x6(img,x,y,z,c,I,T) \
1626  cimg_for6((img)._height,y) for (int x = 0, \
1627    _p2##x = 0, _p1##x = 0, \
1628    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1629    _n2##x = 2>=(img)._width?(img).width() - 1:2, \
1630    _n3##x = (int)( \
1631    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1632    (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1633    (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1634    (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1635    (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1636    (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1637    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1638    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1639    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1640    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1641    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1642    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1643    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1644    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1645    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1646    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1647    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1648    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1649    3>=(img)._width?(img).width() - 1:3); \
1650    (_n3##x<(img).width() && ( \
1651    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1652    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1653    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1654    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1655    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1656    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1657    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1658    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1659    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1660    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1661    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1662    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1663    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1664    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1665 
1666 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1667   cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1668    _p2##x = x - 2<0?0:x - 2, \
1669    _p1##x = x - 1<0?0:x - 1, \
1670    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1671    _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
1672    _n3##x = (int)( \
1673    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1674    (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1675    (I[12] = (T)(img)(_p2##x,y,z,c)), \
1676    (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1677    (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1678    (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1679    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1680    (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1681    (I[13] = (T)(img)(_p1##x,y,z,c)), \
1682    (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1683    (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1684    (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1685    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1686    (I[8] = (T)(img)(x,_p1##y,z,c)), \
1687    (I[14] = (T)(img)(x,y,z,c)), \
1688    (I[20] = (T)(img)(x,_n1##y,z,c)), \
1689    (I[26] = (T)(img)(x,_n2##y,z,c)), \
1690    (I[32] = (T)(img)(x,_n3##y,z,c)), \
1691    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1692    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1693    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1694    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1695    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1696    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1697    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1698    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1699    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1700    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1701    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1702    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1703    x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
1704    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1705    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1706    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1707    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1708    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1709    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1710    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1711    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1712    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1713    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1714    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1715    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1716    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1717    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1718    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1719 
1720 #define cimg_for7x7(img,x,y,z,c,I,T) \
1721   cimg_for7((img)._height,y) for (int x = 0, \
1722    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1723    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1724    _n2##x = 2>=(img)._width?(img).width() - 1:2, \
1725    _n3##x = (int)( \
1726    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1727    (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1728    (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1729    (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1730    (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1731    (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1732    (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1733    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1734    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1735    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1736    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1737    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1738    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1739    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1740    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1741    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1742    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1743    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1744    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1745    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1746    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1747    3>=(img)._width?(img).width() - 1:3); \
1748    (_n3##x<(img).width() && ( \
1749    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1750    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1751    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1752    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1753    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1754    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1755    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1756    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1757    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1758    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1759    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1760    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1761    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1762    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1763    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1764    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1765 
1766 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1767   cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1768    _p3##x = x - 3<0?0:x - 3, \
1769    _p2##x = x - 2<0?0:x - 2, \
1770    _p1##x = x - 1<0?0:x - 1, \
1771    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1772    _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
1773    _n3##x = (int)( \
1774    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1775    (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1776    (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1777    (I[21] = (T)(img)(_p3##x,y,z,c)), \
1778    (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1779    (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1780    (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1781    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1782    (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1783    (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1784    (I[22] = (T)(img)(_p2##x,y,z,c)), \
1785    (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1786    (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1787    (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1788    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1789    (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1790    (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1791    (I[23] = (T)(img)(_p1##x,y,z,c)), \
1792    (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1793    (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1794    (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1795    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1796    (I[10] = (T)(img)(x,_p2##y,z,c)), \
1797    (I[17] = (T)(img)(x,_p1##y,z,c)), \
1798    (I[24] = (T)(img)(x,y,z,c)), \
1799    (I[31] = (T)(img)(x,_n1##y,z,c)), \
1800    (I[38] = (T)(img)(x,_n2##y,z,c)), \
1801    (I[45] = (T)(img)(x,_n3##y,z,c)), \
1802    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1803    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1804    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1805    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1806    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1807    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1808    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1809    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1810    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1811    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1812    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1813    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1814    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1815    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1816    x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
1817    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1818    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1819    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1820    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1821    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1822    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1823    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1824    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1825    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1826    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1827    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1828    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1829    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1830    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1831    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1832    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1833    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1834 
1835 #define cimg_for8x8(img,x,y,z,c,I,T) \
1836   cimg_for8((img)._height,y) for (int x = 0, \
1837    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1838    _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
1839    _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
1840    _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
1841    _n4##x = (int)( \
1842    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1843    (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1844    (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1845    (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1846    (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1847    (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1848    (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1849    (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1850    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1851    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1852    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1853    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1854    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1855    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1856    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1857    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1858    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1859    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1860    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1861    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1862    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1863    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1864    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1865    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1866    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1867    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1868    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1869    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1870    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1871    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1872    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1873    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1874    4>=((img)._width)?(img).width() - 1:4); \
1875    (_n4##x<(img).width() && ( \
1876    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1877    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1878    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1879    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1880    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1881    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1882    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1883    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1884    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1885    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], \
1886    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], \
1887    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], \
1888    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], \
1889    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], \
1890    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], \
1891    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], \
1892    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], \
1893    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1894 
1895 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1896   cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1897    _p3##x = x - 3<0?0:x - 3, \
1898    _p2##x = x - 2<0?0:x - 2, \
1899    _p1##x = x - 1<0?0:x - 1, \
1900    _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
1901    _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
1902    _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
1903    _n4##x = (int)( \
1904    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1905    (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1906    (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1907    (I[24] = (T)(img)(_p3##x,y,z,c)), \
1908    (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1909    (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1910    (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1911    (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1912    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1913    (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1914    (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1915    (I[25] = (T)(img)(_p2##x,y,z,c)), \
1916    (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1917    (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1918    (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1919    (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1920    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1921    (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1922    (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1923    (I[26] = (T)(img)(_p1##x,y,z,c)), \
1924    (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1925    (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1926    (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1927    (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1928    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1929    (I[11] = (T)(img)(x,_p2##y,z,c)), \
1930    (I[19] = (T)(img)(x,_p1##y,z,c)), \
1931    (I[27] = (T)(img)(x,y,z,c)), \
1932    (I[35] = (T)(img)(x,_n1##y,z,c)), \
1933    (I[43] = (T)(img)(x,_n2##y,z,c)), \
1934    (I[51] = (T)(img)(x,_n3##y,z,c)), \
1935    (I[59] = (T)(img)(x,_n4##y,z,c)), \
1936    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1937    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1938    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1939    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1940    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1941    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1942    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1943    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1944    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1945    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1946    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1947    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1948    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1949    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1950    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1951    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1952    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1953    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1954    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1955    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1956    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1957    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1958    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1959    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1960    x + 4>=(img).width()?(img).width() - 1:x + 4); \
1961    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1962    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1963    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1964    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1965    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1966    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1967    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1968    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1969    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1970    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1971    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], \
1972    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], \
1973    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], \
1974    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], \
1975    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], \
1976    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], \
1977    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], \
1978    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], \
1979    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1980 
1981 #define cimg_for9x9(img,x,y,z,c,I,T) \
1982   cimg_for9((img)._height,y) for (int x = 0, \
1983    _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1984    _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
1985    _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
1986    _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
1987    _n4##x = (int)( \
1988    (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1989    (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1990    (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1991    (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1992    (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1993    (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1994    (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1995    (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1996    (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1997    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1998    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1999    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
2000    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
2001    (I[41] = (T)(img)(_n1##x,y,z,c)), \
2002    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
2003    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
2004    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
2005    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
2006    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
2007    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
2008    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
2009    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
2010    (I[42] = (T)(img)(_n2##x,y,z,c)), \
2011    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
2012    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
2013    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
2014    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
2015    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
2016    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
2017    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
2018    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
2019    (I[43] = (T)(img)(_n3##x,y,z,c)), \
2020    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
2021    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
2022    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
2023    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
2024    4>=((img)._width)?(img).width() - 1:4); \
2025    (_n4##x<(img).width() && ( \
2026    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
2027    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
2028    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
2029    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
2030    (I[44] = (T)(img)(_n4##x,y,z,c)), \
2031    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
2032    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
2033    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
2034    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
2035    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
2036    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], \
2037    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], \
2038    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], \
2039    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], \
2040    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], \
2041    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], \
2042    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], \
2043    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], \
2044    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], \
2045    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], \
2046    I[79] = I[80], \
2047    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
2048 
2049 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
2050   cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
2051    _p4##x = x - 4<0?0:x - 4, \
2052    _p3##x = x - 3<0?0:x - 3, \
2053    _p2##x = x - 2<0?0:x - 2, \
2054    _p1##x = x - 1<0?0:x - 1, \
2055    _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
2056    _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
2057    _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
2058    _n4##x = (int)( \
2059    (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
2060    (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
2061    (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
2062    (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
2063    (I[36] = (T)(img)(_p4##x,y,z,c)), \
2064    (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
2065    (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
2066    (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
2067    (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
2068    (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
2069    (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
2070    (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
2071    (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
2072    (I[37] = (T)(img)(_p3##x,y,z,c)), \
2073    (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
2074    (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
2075    (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
2076    (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
2077    (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
2078    (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
2079    (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
2080    (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
2081    (I[38] = (T)(img)(_p2##x,y,z,c)), \
2082    (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
2083    (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
2084    (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
2085    (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
2086    (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
2087    (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
2088    (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
2089    (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
2090    (I[39] = (T)(img)(_p1##x,y,z,c)), \
2091    (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
2092    (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
2093    (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
2094    (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
2095    (I[4] = (T)(img)(x,_p4##y,z,c)), \
2096    (I[13] = (T)(img)(x,_p3##y,z,c)), \
2097    (I[22] = (T)(img)(x,_p2##y,z,c)), \
2098    (I[31] = (T)(img)(x,_p1##y,z,c)), \
2099    (I[40] = (T)(img)(x,y,z,c)), \
2100    (I[49] = (T)(img)(x,_n1##y,z,c)), \
2101    (I[58] = (T)(img)(x,_n2##y,z,c)), \
2102    (I[67] = (T)(img)(x,_n3##y,z,c)), \
2103    (I[76] = (T)(img)(x,_n4##y,z,c)), \
2104    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
2105    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
2106    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
2107    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
2108    (I[41] = (T)(img)(_n1##x,y,z,c)), \
2109    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
2110    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
2111    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
2112    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
2113    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
2114    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
2115    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
2116    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
2117    (I[42] = (T)(img)(_n2##x,y,z,c)), \
2118    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
2119    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
2120    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
2121    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
2122    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
2123    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
2124    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
2125    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
2126    (I[43] = (T)(img)(_n3##x,y,z,c)), \
2127    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
2128    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
2129    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
2130    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
2131    x + 4>=(img).width()?(img).width() - 1:x + 4); \
2132    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
2133    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
2134    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
2135    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
2136    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
2137    (I[44] = (T)(img)(_n4##x,y,z,c)), \
2138    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
2139    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
2140    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
2141    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
2142    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
2143    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], \
2144    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], \
2145    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], \
2146    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], \
2147    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], \
2148    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], \
2149    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], \
2150    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], \
2151    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], \
2152    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], \
2153    I[79] = I[80], \
2154    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
2155 
2156 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
2157  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
2158    _n1##x = (int)( \
2159    (I[0] = (T)(img)(0,y,z,c)), \
2160    (I[2] = (T)(img)(0,_n1##y,z,c)), \
2161    (I[4] = (T)(img)(0,y,_n1##z,c)), \
2162    (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
2163    1>=(img)._width?(img).width() - 1:1); \
2164    (_n1##x<(img).width() && ( \
2165    (I[1] = (T)(img)(_n1##x,y,z,c)), \
2166    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
2167    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
2168    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2169    x==--_n1##x; \
2170    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
2171    ++x, ++_n1##x)
2172 
2173 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
2174  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), \
2175    _n1##x = (int)( \
2176    (I[0] = (T)(img)(x,y,z,c)), \
2177    (I[2] = (T)(img)(x,_n1##y,z,c)), \
2178    (I[4] = (T)(img)(x,y,_n1##z,c)), \
2179    (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
2180    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
2181    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
2182    (I[1] = (T)(img)(_n1##x,y,z,c)), \
2183    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
2184    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
2185    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2186    x==--_n1##x); \
2187    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
2188    ++x, ++_n1##x)
2189 
2190 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
2191  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
2192    _p1##x = 0, \
2193    _n1##x = (int)( \
2194    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
2195    (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)),  \
2196    (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
2197    (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
2198    (I[12] = I[13] = (T)(img)(0,y,z,c)), \
2199    (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
2200    (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
2201    (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
2202    (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
2203    1>=(img)._width?(img).width() - 1:1); \
2204    (_n1##x<(img).width() && ( \
2205    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
2206    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
2207    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
2208    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
2209    (I[14] = (T)(img)(_n1##x,y,z,c)), \
2210    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
2211    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
2212    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
2213    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2214    x==--_n1##x; \
2215    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
2216    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
2217    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
2218    _p1##x = x++, ++_n1##x)
2219 
2220 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
2221  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), \
2222    _p1##x = x - 1<0?0:x - 1, \
2223    _n1##x = (int)( \
2224    (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
2225    (I[3] = (T)(img)(_p1##x,y,_p1##z,c)),  \
2226    (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
2227    (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
2228    (I[12] = (T)(img)(_p1##x,y,z,c)), \
2229    (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
2230    (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
2231    (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
2232    (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
2233    (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
2234    (I[4] = (T)(img)(x,y,_p1##z,c)),  \
2235    (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
2236    (I[10] = (T)(img)(x,_p1##y,z,c)), \
2237    (I[13] = (T)(img)(x,y,z,c)), \
2238    (I[16] = (T)(img)(x,_n1##y,z,c)), \
2239    (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
2240    (I[22] = (T)(img)(x,y,_n1##z,c)), \
2241    (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
2242    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
2243    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
2244    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
2245    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
2246    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
2247    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
2248    (I[14] = (T)(img)(_n1##x,y,z,c)), \
2249    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
2250    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
2251    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
2252    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2253    x==--_n1##x); \
2254    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
2255    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
2256    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
2257    _p1##x = x++, ++_n1##x)
2258 
2259 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
2260 #define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l)
2261 #define cimglist_for_in(list,l0,l1,l) \
2262   for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \
2263   l<=_max##l; ++l)
2264 
2265 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
2266 
2267 // Macros used to display error messages when exceptions are thrown.
2268 // You should not use these macros is your own code.
2269 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
2270 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
2271 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
2272 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
2273 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
2274 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
2275 
2276 /*------------------------------------------------
2277  #
2278  #
2279  #  Define cimg_library:: namespace
2280  #
2281  #
2282  -------------------------------------------------*/
2283 //! Contains <i>all classes and functions</i> of the \CImg library.
2284 /**
2285    This namespace is defined to avoid functions and class names collisions
2286    that could happen with the inclusion of other C++ header files.
2287    Anyway, it should not happen often and you should reasonably start most of your
2288    \CImg-based programs with
2289    \code
2290    #include "CImg.h"
2291    using namespace cimg_library;
2292    \endcode
2293    to simplify the declaration of \CImg Library objects afterwards.
2294 **/
2295 namespace cimg_library_suffixed {
2296 
2297   // Declare the four classes of the CImg Library.
2298   template<typename T=float> struct CImg;
2299   template<typename T=float> struct CImgList;
2300   struct CImgDisplay;
2301   struct CImgException;
2302 
2303   // Declare cimg:: namespace.
2304   // This is an incomplete namespace definition here. It only contains some
2305   // necessary stuff to ensure a correct declaration order of the classes and functions
2306   // defined afterwards.
2307   namespace cimg {
2308 
2309     // Define character sequences for colored terminal output.
2310 #ifdef cimg_use_vt100
2311     static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
2312     static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
2313     static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
2314     static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
2315     static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
2316     static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
2317     static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
2318     static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
2319     static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
2320     static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
2321     static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
2322 #else
2323     static const char t_normal[] = { 0 };
2324     static const char *const t_black = cimg::t_normal,
2325       *const t_red = cimg::t_normal,
2326       *const t_green = cimg::t_normal,
2327       *const t_yellow = cimg::t_normal,
2328       *const t_blue = cimg::t_normal,
2329       *const t_magenta = cimg::t_normal,
2330       *const t_cyan = cimg::t_normal,
2331       *const t_white = cimg::t_normal,
2332       *const t_bold = cimg::t_normal,
2333       *const t_underscore = cimg::t_normal;
2334 #endif
2335 
2336     inline std::FILE* output(std::FILE *file=0);
2337     inline void info();
2338 
2339     //! Avoid warning messages due to unused parameters. Do nothing actually.
2340     template<typename T>
unused(const T &,...)2341     inline void unused(const T&, ...) {}
2342 
2343     // [internal] Lock/unlock a mutex for managing concurrent threads.
2344     // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
2345     // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg.
2346     inline int mutex(const unsigned int n, const int lock_mode=1);
2347 
exception_mode(const unsigned int value,const bool is_set)2348     inline unsigned int& exception_mode(const unsigned int value, const bool is_set) {
2349       static unsigned int mode = cimg_verbosity;
2350       if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); }
2351       return mode;
2352     }
2353 
2354     // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
2355     inline FILE* _stdin(const bool throw_exception=true);
2356     inline FILE* _stdout(const bool throw_exception=true);
2357     inline FILE* _stderr(const bool throw_exception=true);
2358 
2359     // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character
2360     // at the end of the string.
2361 #if cimg_OS==2 && defined(_MSC_VER)
_snprintf(char * const s,const size_t size,const char * const format,...)2362     inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
2363       va_list ap;
2364       va_start(ap,format);
2365       const int result = _vsnprintf(s,size,format,ap);
2366       va_end(ap);
2367       return result;
2368     }
2369 
_vsnprintf(char * const s,const size_t size,const char * const format,va_list ap)2370     inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) {
2371       int result = -1;
2372       cimg::mutex(6);
2373       if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap);
2374       if (result==-1) result = _vscprintf(format,ap);
2375       cimg::mutex(6,0);
2376       return result;
2377     }
2378 
2379     // Mutex-protected version of sscanf, sprintf and snprintf.
2380     // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX.
2381 #elif defined(__MACOSX__) || defined(__APPLE__)
_sscanf(const char * const s,const char * const format,...)2382     inline int _sscanf(const char *const s, const char *const format, ...) {
2383       cimg::mutex(6);
2384       va_list args;
2385       va_start(args,format);
2386       const int result = std::vsscanf(s,format,args);
2387       va_end(args);
2388       cimg::mutex(6,0);
2389       return result;
2390     }
2391 
_sprintf(char * const s,const char * const format,...)2392     inline int _sprintf(char *const s, const char *const format, ...) {
2393       cimg::mutex(6);
2394       va_list args;
2395       va_start(args,format);
2396       const int result = std::vsprintf(s,format,args);
2397       va_end(args);
2398       cimg::mutex(6,0);
2399       return result;
2400     }
2401 
_snprintf(char * const s,const size_t n,const char * const format,...)2402     inline int _snprintf(char *const s, const size_t n, const char *const format, ...) {
2403       cimg::mutex(6);
2404       va_list args;
2405       va_start(args,format);
2406       const int result = std::vsnprintf(s,n,format,args);
2407       va_end(args);
2408       cimg::mutex(6,0);
2409       return result;
2410     }
2411 
_vsnprintf(char * const s,const size_t size,const char * format,va_list ap)2412     inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) {
2413       cimg::mutex(6);
2414       const int result = std::vsnprintf(s,size,format,ap);
2415       cimg::mutex(6,0);
2416       return result;
2417     }
2418 #endif
2419 
2420     //! Set current \CImg exception mode.
2421     /**
2422        The way error messages are handled by \CImg can be changed dynamically, using this function.
2423        \param mode Desired exception mode. Possible values are:
2424        - \c 0: Hide library messages (quiet mode).
2425        - \c 1: Print library messages on the console.
2426        - \c 2: Display library messages on a dialog window.
2427        - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!).
2428        - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!).
2429      **/
exception_mode(const unsigned int mode)2430     inline unsigned int& exception_mode(const unsigned int mode) {
2431       return exception_mode(mode,true);
2432     }
2433 
2434     //! Return current \CImg exception mode.
2435     /**
2436        \note By default, return the value of configuration macro \c cimg_verbosity
2437     **/
exception_mode()2438     inline unsigned int& exception_mode() {
2439       return exception_mode(0,false);
2440     }
2441 
openmp_mode(const unsigned int value,const bool is_set)2442     inline unsigned int openmp_mode(const unsigned int value, const bool is_set) {
2443       static unsigned int mode = 2;
2444       if (is_set)  { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); }
2445       return mode;
2446     }
2447 
2448     //! Set current \CImg openmp mode.
2449     /**
2450        The way openmp-based methods are handled by \CImg can be changed dynamically, using this function.
2451        \param mode Desired openmp mode. Possible values are:
2452        - \c 0: Never parallelize.
2453        - \c 1: Always parallelize.
2454        - \c 2: Adaptive parallelization mode (default behavior).
2455      **/
openmp_mode(const unsigned int mode)2456     inline unsigned int openmp_mode(const unsigned int mode) {
2457       return openmp_mode(mode,true);
2458     }
2459 
2460     //! Return current \CImg openmp mode.
openmp_mode()2461     inline unsigned int openmp_mode() {
2462       return openmp_mode(0,false);
2463     }
2464 
2465 #ifndef cimg_openmp_sizefactor
2466 #define cimg_openmp_sizefactor 1
2467 #endif
2468 #define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))))
2469 #define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size))
2470 #ifdef _MSC_VER
2471 // Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0).
2472 #define cimg_openmp_collapse(k)
2473 #else
2474 #define cimg_openmp_collapse(k) collapse(k)
2475 #endif
2476 
2477 #if cimg_OS==2
2478 // Disable parallelization of simple loops on Windows, due to noticed performance drop.
2479 #define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr);
2480 #else
2481 #define cimg_openmp_for(instance,expr,min_size) \
2482     cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \
2483       cimg_rof((instance),ptr,T) *ptr = (T)(expr);
2484 #endif
2485 
2486     // Display a simple dialog box, and wait for the user's response.
2487     inline int dialog(const char *const title, const char *const msg,
2488                       const char *const button1_label="OK", const char *const button2_label=0,
2489                       const char *const button3_label=0, const char *const button4_label=0,
2490                       const char *const button5_label=0, const char *const button6_label=0,
2491                       const bool centering=false);
2492 
2493     // Evaluate math expression.
2494     inline double eval(const char *const expression,
2495                        const double x=0, const double y=0, const double z=0, const double c=0);
2496 
2497   } // namespace cimg { ...
2498 
2499   /*---------------------------------------
2500     #
2501     # Define the CImgException structures
2502     #
2503     --------------------------------------*/
2504   //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
2505   /**
2506      \par Overview
2507 
2508       CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException).
2509       CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
2510       These classes can be:
2511 
2512       - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal.
2513         This is the only \c non-derived exception class.
2514 
2515       - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid.
2516       This is probably one of the most thrown exception by \CImg.
2517       For instance, the following example throws a \c CImgArgumentException:
2518       \code
2519       CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels
2520       img.mirror('e');              // Try to mirror image along the (non-existing) 'e'-axis
2521       \endcode
2522 
2523       - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances.
2524 
2525       - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit
2526       the function requirements. For instance, the following example throws a \c CImgInstanceException:
2527       \code
2528       const CImg<float> img;           // Define an empty image
2529       const float value = img.at(0);   // Try to read first pixel value (does not exist)
2530       \endcode
2531 
2532       - \b CImgIOException: Thrown when an error occurred when trying to load or save image files.
2533       This happens when trying to read files that do not exist or with invalid formats.
2534       For instance, the following example throws a \c CImgIOException:
2535       \code
2536       const CImg<float> img("missing_file.jpg");  // Try to load a file that does not exist
2537       \endcode
2538 
2539       - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and
2540       when a \CImg function has to display a warning message (see cimg::warn()).
2541 
2542       It is not recommended to throw CImgException instances by yourself,
2543       since they are expected to be thrown only by \CImg.
2544       When an error occurs in a library function call, \CImg may display error messages on the screen or on the
2545       standard output, depending on the current \CImg exception mode.
2546       The \CImg exception mode can be get and set by functions cimg::exception_mode() and
2547       cimg::exception_mode(unsigned int).
2548 
2549       \par Exceptions handling
2550 
2551       In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
2552       This may lead the program to break (this is the default behavior), but you can bypass this behavior by
2553       handling the exceptions by yourself,
2554       using a usual <tt>try { ... } catch () { ... }</tt> bloc, as in the following example:
2555       \code
2556       #define "CImg.h"
2557       using namespace cimg_library;
2558       int main() {
2559         cimg::exception_mode(0);                                    // Enable quiet exception mode
2560         try {
2561           ...                                                       // Here, do what you want to stress CImg
2562         } catch (CImgException& e) {                                // You succeeded: something went wrong!
2563           std::fprintf(stderr,"CImg Library Error: %s",e.what());   // Display your custom error message
2564           ...                                                       // Do what you want now to save the ship!
2565           }
2566         }
2567       \endcode
2568   **/
2569   struct CImgException : public std::exception {
2570 #define _cimg_exception_err(etype,disp_flag) \
2571   std::va_list ap, ap2; \
2572   va_start(ap,format); va_start(ap2,format); \
2573   int size = cimg_vsnprintf(0,0,format,ap2); \
2574   if (size++>=0) { \
2575     delete[] _message; \
2576     _message = new char[(size_t)size]; \
2577     cimg_vsnprintf(_message,(size_t)size,format,ap); \
2578     if (cimg::exception_mode()) { \
2579       std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2580       if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
2581       catch (CImgException&) {} \
2582       if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
2583     } \
2584   } \
2585   va_end(ap); va_end(ap2);
2586 
2587     char *_message;
CImgExceptionCImgException2588     CImgException() { _message = new char[1]; *_message = 0; }
CImgExceptionCImgException2589     CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); }
CImgExceptionCImgException2590     CImgException(const CImgException& e):std::exception(e) {
2591       const size_t size = std::strlen(e._message);
2592       _message = new char[size + 1];
2593       std::strncpy(_message,e._message,size);
2594       _message[size] = 0;
2595     }
throwCImgException2596     ~CImgException() throw() { delete[] _message; }
2597     CImgException& operator=(const CImgException& e) {
2598       const size_t size = std::strlen(e._message);
2599       _message = new char[size + 1];
2600       std::strncpy(_message,e._message,size);
2601       _message[size] = 0;
2602       return *this;
2603     }
2604     //! Return a C-string containing the error message associated to the thrown exception.
whatCImgException2605     const char *what() const throw() { return _message; }
2606   }; // struct CImgException { ...
2607 
2608   // The CImgAbortException class is used to throw an exception when
2609   // a computationally-intensive function has been aborted by an external signal.
2610   struct CImgAbortException : public std::exception {
2611     char *_message;
CImgAbortExceptionCImgAbortException2612     CImgAbortException() { _message = new char[1]; *_message = 0; }
CImgAbortExceptionCImgAbortException2613     CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); }
CImgAbortExceptionCImgAbortException2614     CImgAbortException(const CImgAbortException& e):std::exception(e) {
2615       const size_t size = std::strlen(e._message);
2616       _message = new char[size + 1];
2617       std::strncpy(_message,e._message,size);
2618       _message[size] = 0;
2619     }
throwCImgAbortException2620     ~CImgAbortException() throw() { delete[] _message; }
2621     CImgAbortException& operator=(const CImgAbortException& e) {
2622       const size_t size = std::strlen(e._message);
2623       _message = new char[size + 1];
2624       std::strncpy(_message,e._message,size);
2625       _message[size] = 0;
2626       return *this;
2627     }
2628     //! Return a C-string containing the error message associated to the thrown exception.
whatCImgAbortException2629     const char *what() const throw() { return _message; }
2630   }; // struct CImgAbortException { ...
2631 
2632   // The CImgArgumentException class is used to throw an exception related
2633   // to invalid arguments encountered in a library function call.
2634   struct CImgArgumentException : public CImgException {
CImgArgumentExceptionCImgArgumentException2635     CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2636   }; // struct CImgArgumentException { ...
2637 
2638   // The CImgDisplayException class is used to throw an exception related
2639   // to display problems encountered in a library function call.
2640   struct CImgDisplayException : public CImgException {
CImgDisplayExceptionCImgDisplayException2641     CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2642   }; // struct CImgDisplayException { ...
2643 
2644   // The CImgInstanceException class is used to throw an exception related
2645   // to an invalid instance encountered in a library function call.
2646   struct CImgInstanceException : public CImgException {
CImgInstanceExceptionCImgInstanceException2647     CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2648   }; // struct CImgInstanceException { ...
2649 
2650   // The CImgIOException class is used to throw an exception related
2651   // to input/output file problems encountered in a library function call.
2652   struct CImgIOException : public CImgException {
CImgIOExceptionCImgIOException2653     CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2654   }; // struct CImgIOException { ...
2655 
2656   // The CImgWarningException class is used to throw an exception for warnings
2657   // encountered in a library function call.
2658   struct CImgWarningException : public CImgException {
CImgWarningExceptionCImgWarningException2659     CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2660   }; // struct CImgWarningException { ...
2661 
2662   /*-------------------------------------
2663     #
2664     # Define cimg:: namespace
2665     #
2666     -----------------------------------*/
2667   //! Contains \a low-level functions and variables of the \CImg Library.
2668   /**
2669      Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
2670      You may use them to access specific const values or environment variables internally used by \CImg.
2671      \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
2672      <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global
2673      namespace <tt>::</tt>.
2674   **/
2675   namespace cimg {
2676 
2677     // Define traits that will be used to determine the best data type to work in CImg functions.
2678     //
2679     template<typename T> struct type {
stringtype2680       static const char* string() {
2681         static const char* s[] = { "unknown",   "unknown8",   "unknown16",  "unknown24",
2682                                    "unknown32", "unknown40",  "unknown48",  "unknown56",
2683                                    "unknown64", "unknown72",  "unknown80",  "unknown88",
2684                                    "unknown96", "unknown104", "unknown112", "unknown120",
2685                                    "unknown128" };
2686         return s[(sizeof(T)<17)?sizeof(T):0];
2687       }
is_floattype2688       static bool is_float() { return false; }
is_inftype2689       static bool is_inf(const T) { return false; }
is_nantype2690       static bool is_nan(const T) { return false; }
is_finitetype2691       static bool is_finite(const T) { return true; }
mintype2692       static T min() { return ~max(); }
maxtype2693       static T max() { return (T)1<<(8*sizeof(T) - 1); }
inftype2694       static T inf() { return max(); }
cuttype2695       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
formattype2696       static const char* format() { return "%s"; }
format_stype2697       static const char* format_s() { return "%s"; }
formattype2698       static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
2699     };
2700 
2701     template<> struct type<bool> {
2702       static const char* string() { static const char *const s = "bool"; return s; }
2703       static bool is_float() { return false; }
2704       static bool is_inf(const bool) { return false; }
2705       static bool is_nan(const bool) { return false; }
2706       static bool is_finite(const bool) { return true; }
2707       static bool min() { return false; }
2708       static bool max() { return true; }
2709       static bool inf() { return max(); }
2710       static bool is_inf() { return false; }
2711       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2712       static const char* format() { return "%s"; }
2713       static const char* format_s() { return "%s"; }
2714       static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2715     };
2716 
2717     template<> struct type<unsigned char> {
2718       static const char* string() { static const char *const s = "unsigned char"; return s; }
2719       static bool is_float() { return false; }
2720       static bool is_inf(const unsigned char) { return false; }
2721       static bool is_nan(const unsigned char) { return false; }
2722       static bool is_finite(const unsigned char) { return true; }
2723       static unsigned char min() { return 0; }
2724       static unsigned char max() { return (unsigned char)-1; }
2725       static unsigned char inf() { return max(); }
2726       static unsigned char cut(const double val) {
2727         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2728       static const char* format() { return "%u"; }
2729       static const char* format_s() { return "%u"; }
2730       static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2731     };
2732 
2733 #if defined(CHAR_MAX) && CHAR_MAX==255
2734     template<> struct type<char> {
2735       static const char* string() { static const char *const s = "char"; return s; }
2736       static bool is_float() { return false; }
2737       static bool is_inf(const char) { return false; }
2738       static bool is_nan(const char) { return false; }
2739       static bool is_finite(const char) { return true; }
2740       static char min() { return 0; }
2741       static char max() { return (char)-1; }
2742       static char inf() { return max(); }
2743       static char cut(const double val) {
2744         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2745       static const char* format() { return "%u"; }
2746       static const char* format_s() { return "%u"; }
2747       static unsigned int format(const char val) { return (unsigned int)val; }
2748     };
2749 #else
2750     template<> struct type<char> {
2751       static const char* string() { static const char *const s = "char"; return s; }
2752       static bool is_float() { return false; }
2753       static bool is_inf(const char) { return false; }
2754       static bool is_nan(const char) { return false; }
2755       static bool is_finite(const char) { return true; }
2756       static char min() { return ~max(); }
2757       static char max() { return (char)((unsigned char)-1>>1); }
2758       static char inf() { return max(); }
2759       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2760       static const char* format() { return "%d"; }
2761       static const char* format_s() { return "%d"; }
2762       static int format(const char val) { return (int)val; }
2763     };
2764 #endif
2765 
2766     template<> struct type<signed char> {
2767       static const char* string() { static const char *const s = "signed char"; return s; }
2768       static bool is_float() { return false; }
2769       static bool is_inf(const signed char) { return false; }
2770       static bool is_nan(const signed char) { return false; }
2771       static bool is_finite(const signed char) { return true; }
2772       static signed char min() { return ~max(); }
2773       static signed char max() { return (signed char)((unsigned char)-1>>1); }
2774       static signed char inf() { return max(); }
2775       static signed char cut(const double val) {
2776         return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2777       static const char* format() { return "%d"; }
2778       static const char* format_s() { return "%d"; }
2779       static int format(const signed char val) { return (int)val; }
2780     };
2781 
2782     template<> struct type<unsigned short> {
2783       static const char* string() { static const char *const s = "unsigned short"; return s; }
2784       static bool is_float() { return false; }
2785       static bool is_inf(const unsigned short) { return false; }
2786       static bool is_nan(const unsigned short) { return false; }
2787       static bool is_finite(const unsigned short) { return true; }
2788       static unsigned short min() { return 0; }
2789       static unsigned short max() { return (unsigned short)-1; }
2790       static unsigned short inf() { return max(); }
2791       static unsigned short cut(const double val) {
2792         return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2793       static const char* format() { return "%u"; }
2794       static const char* format_s() { return "%u"; }
2795       static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2796     };
2797 
2798     template<> struct type<short> {
2799       static const char* string() { static const char *const s = "short"; return s; }
2800       static bool is_float() { return false; }
2801       static bool is_inf(const short) { return false; }
2802       static bool is_nan(const short) { return false; }
2803       static bool is_finite(const short) { return true; }
2804       static short min() { return ~max(); }
2805       static short max() { return (short)((unsigned short)-1>>1); }
2806       static short inf() { return max(); }
2807       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2808       static const char* format() { return "%d"; }
2809       static const char* format_s() { return "%d"; }
2810       static int format(const short val) { return (int)val; }
2811     };
2812 
2813     template<> struct type<unsigned int> {
2814       static const char* string() { static const char *const s = "unsigned int"; return s; }
2815       static bool is_float() { return false; }
2816       static bool is_inf(const unsigned int) { return false; }
2817       static bool is_nan(const unsigned int) { return false; }
2818       static bool is_finite(const unsigned int) { return true; }
2819       static unsigned int min() { return 0; }
2820       static unsigned int max() { return (unsigned int)-1; }
2821       static unsigned int inf() { return max(); }
2822       static unsigned int cut(const double val) {
2823         return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2824       static const char* format() { return "%u"; }
2825       static const char* format_s() { return "%u"; }
2826       static unsigned int format(const unsigned int val) { return val; }
2827     };
2828 
2829     template<> struct type<int> {
2830       static const char* string() { static const char *const s = "int"; return s; }
2831       static bool is_float() { return false; }
2832       static bool is_inf(const int) { return false; }
2833       static bool is_nan(const int) { return false; }
2834       static bool is_finite(const int) { return true; }
2835       static int min() { return ~max(); }
2836       static int max() { return (int)(~0U>>1); }
2837       static int inf() { return max(); }
2838       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2839       static const char* format() { return "%d"; }
2840       static const char* format_s() { return "%d"; }
2841       static int format(const int val) { return val; }
2842     };
2843 
2844     template<> struct type<cimg_uint64> {
2845       static const char* string() { static const char *const s = "unsigned int64"; return s; }
2846       static bool is_float() { return false; }
2847       static bool is_inf(const cimg_uint64) { return false; }
2848       static bool is_nan(const cimg_uint64) { return false; }
2849       static bool is_finite(const cimg_uint64) { return true; }
2850       static cimg_uint64 min() { return 0; }
2851       static cimg_uint64 max() { return (cimg_uint64)-1; }
2852       static cimg_uint64 inf() { return max(); }
2853       static cimg_uint64 cut(const double val) {
2854         return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
2855       static const char* format() { return cimg_fuint64; }
2856       static const char* format_s() { return cimg_fuint64; }
2857       static cimg_uint64 format(const cimg_uint64 val) { return val; }
2858     };
2859 
2860     template<> struct type<cimg_int64> {
2861       static const char* string() { static const char *const s = "int64"; return s; }
2862       static bool is_float() { return false; }
2863       static bool is_inf(const cimg_int64) { return false; }
2864       static bool is_nan(const cimg_int64) { return false; }
2865       static bool is_finite(const cimg_int64) { return true; }
2866       static cimg_int64 min() { return ~max(); }
2867       static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
2868       static cimg_int64 inf() { return max(); }
2869       static cimg_int64 cut(const double val) {
2870         return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
2871       }
2872       static const char* format() { return cimg_fint64; }
2873       static const char* format_s() { return cimg_fint64; }
2874       static long format(const long val) { return (long)val; }
2875     };
2876 
2877     template<> struct type<double> {
2878       static const char* string() { static const char *const s = "double"; return s; }
2879       static bool is_float() { return true; }
2880       static bool is_inf(const double val) {
2881 #ifdef isinf
2882         return (bool)isinf(val);
2883 #else
2884         return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
2885 #endif
2886       }
2887       static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
2888         if (sizeof(double)==8) {
2889           cimg_uint64 u;
2890           std::memcpy(&u,&val,sizeof(double));
2891           return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
2892         }
2893 #ifdef isnan
2894         return (bool)isnan(val);
2895 #else
2896         return !(val==val);
2897 #endif
2898       }
2899       static bool is_finite(const double val) {
2900 #ifdef isfinite
2901         return (bool)isfinite(val);
2902 #else
2903         return !is_nan(val) && !is_inf(val);
2904 #endif
2905       }
2906       static double min() { return -DBL_MAX; }
2907       static double max() { return DBL_MAX; }
2908       static double inf() {
2909 #ifdef INFINITY
2910         return (double)INFINITY;
2911 #else
2912         return max()*max();
2913 #endif
2914       }
2915       static double nan() {
2916 #ifdef NAN
2917         return (double)NAN;
2918 #else
2919         const double val_nan = -std::sqrt(-1.); return val_nan;
2920 #endif
2921       }
2922       static double cut(const double val) { return val; }
2923       static const char* format() { return "%.17g"; }
2924       static const char* format_s() { return "%g"; }
2925       static double format(const double val) { return val; }
2926     };
2927 
2928     template<> struct type<float> {
2929       static const char* string() { static const char *const s = "float"; return s; }
2930       static bool is_float() { return true; }
2931       static bool is_inf(const float val) {
2932 #ifdef isinf
2933         return (bool)isinf(val);
2934 #else
2935         return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
2936 #endif
2937       }
2938       static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
2939         if (sizeof(float)==4) {
2940           unsigned int u;
2941           std::memcpy(&u,&val,sizeof(float));
2942           return (u&0x7fffffff)>0x7f800000;
2943         }
2944 #ifdef isnan
2945         return (bool)isnan(val);
2946 #else
2947         return !(val==val);
2948 #endif
2949       }
2950       static bool is_finite(const float val) {
2951 #ifdef isfinite
2952         return (bool)isfinite(val);
2953 #else
2954         return !is_nan(val) && !is_inf(val);
2955 #endif
2956       }
2957       static float min() { return -FLT_MAX; }
2958       static float max() { return FLT_MAX; }
2959       static float inf() { return (float)cimg::type<double>::inf(); }
2960       static float nan() { return (float)cimg::type<double>::nan(); }
2961       static float cut(const double val) { return (float)val; }
2962       static float cut(const float val) { return (float)val; }
2963       static const char* format() { return "%.9g"; }
2964       static const char* format_s() { return "%g"; }
2965       static double format(const float val) { return (double)val; }
2966     };
2967 
2968     template<> struct type<long double> {
2969       static const char* string() { static const char *const s = "long double"; return s; }
2970       static bool is_float() { return true; }
2971       static bool is_inf(const long double val) {
2972 #ifdef isinf
2973         return (bool)isinf(val);
2974 #else
2975         return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
2976 #endif
2977       }
2978       static bool is_nan(const long double val) {
2979 #ifdef isnan
2980         return (bool)isnan(val);
2981 #else
2982         return !(val==val);
2983 #endif
2984       }
2985       static bool is_finite(const long double val) {
2986 #ifdef isfinite
2987         return (bool)isfinite(val);
2988 #else
2989         return !is_nan(val) && !is_inf(val);
2990 #endif
2991       }
2992       static long double min() { return -LDBL_MAX; }
2993       static long double max() { return LDBL_MAX; }
2994       static long double inf() { return max()*max(); }
2995       static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; }
2996       static long double cut(const long double val) { return val; }
2997       static const char* format() { return "%.17g"; }
2998       static const char* format_s() { return "%g"; }
2999       static double format(const long double val) { return (double)val; }
3000     };
3001 
3002 #ifdef cimg_use_half
3003     template<> struct type<half> {
3004       static const char* string() { static const char *const s = "half"; return s; }
3005       static bool is_float() { return true; }
3006       static bool is_inf(const long double val) {
3007 #ifdef isinf
3008         return (bool)isinf(val);
3009 #else
3010         return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
3011 #endif
3012       }
3013       static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
3014         if (sizeof(half)==2) {
3015           short u;
3016           std::memcpy(&u,&val,sizeof(short));
3017           return (bool)((u&0x7fff)>0x7c00);
3018         }
3019         return cimg::type<float>::is_nan((float)val);
3020       }
3021       static bool is_finite(const half val) {
3022 #ifdef isfinite
3023         return (bool)isfinite(val);
3024 #else
3025         return !is_nan(val) && !is_inf(val);
3026 #endif
3027       }
3028       static half min() { return (half)-65504; }
3029       static half max() { return (half)65504; }
3030       static half inf() { return max()*max(); }
3031       static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; }
3032       static half cut(const double val) { return (half)val; }
3033       static const char* format() { return "%.9g"; }
3034       static const char* format_s() { return "%g"; }
3035       static double format(const half val) { return (double)val; }
3036     };
3037 #endif
3038 
3039     template<typename T, typename t> struct superset { typedef T type; };
3040     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
3041     template<> struct superset<bool,char> { typedef char type; };
3042     template<> struct superset<bool,signed char> { typedef signed char type; };
3043     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
3044     template<> struct superset<bool,short> { typedef short type; };
3045     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
3046     template<> struct superset<bool,int> { typedef int type; };
3047     template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
3048     template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
3049     template<> struct superset<bool,float> { typedef float type; };
3050     template<> struct superset<bool,double> { typedef double type; };
3051     template<> struct superset<unsigned char,char> { typedef short type; };
3052     template<> struct superset<unsigned char,signed char> { typedef short type; };
3053     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
3054     template<> struct superset<unsigned char,short> { typedef short type; };
3055     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
3056     template<> struct superset<unsigned char,int> { typedef int type; };
3057     template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
3058     template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
3059     template<> struct superset<unsigned char,float> { typedef float type; };
3060     template<> struct superset<unsigned char,double> { typedef double type; };
3061     template<> struct superset<signed char,unsigned char> { typedef short type; };
3062     template<> struct superset<signed char,char> { typedef short type; };
3063     template<> struct superset<signed char,unsigned short> { typedef int type; };
3064     template<> struct superset<signed char,short> { typedef short type; };
3065     template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
3066     template<> struct superset<signed char,int> { typedef int type; };
3067     template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
3068     template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
3069     template<> struct superset<signed char,float> { typedef float type; };
3070     template<> struct superset<signed char,double> { typedef double type; };
3071     template<> struct superset<char,unsigned char> { typedef short type; };
3072     template<> struct superset<char,signed char> { typedef short type; };
3073     template<> struct superset<char,unsigned short> { typedef int type; };
3074     template<> struct superset<char,short> { typedef short type; };
3075     template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
3076     template<> struct superset<char,int> { typedef int type; };
3077     template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
3078     template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
3079     template<> struct superset<char,float> { typedef float type; };
3080     template<> struct superset<char,double> { typedef double type; };
3081     template<> struct superset<unsigned short,char> { typedef int type; };
3082     template<> struct superset<unsigned short,signed char> { typedef int type; };
3083     template<> struct superset<unsigned short,short> { typedef int type; };
3084     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
3085     template<> struct superset<unsigned short,int> { typedef int type; };
3086     template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
3087     template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
3088     template<> struct superset<unsigned short,float> { typedef float type; };
3089     template<> struct superset<unsigned short,double> { typedef double type; };
3090     template<> struct superset<short,unsigned short> { typedef int type; };
3091     template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
3092     template<> struct superset<short,int> { typedef int type; };
3093     template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
3094     template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
3095     template<> struct superset<short,float> { typedef float type; };
3096     template<> struct superset<short,double> { typedef double type; };
3097     template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
3098     template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
3099     template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
3100     template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
3101     template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
3102     template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
3103     template<> struct superset<unsigned int,float> { typedef float type; };
3104     template<> struct superset<unsigned int,double> { typedef double type; };
3105     template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
3106     template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
3107     template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
3108     template<> struct superset<int,float> { typedef float type; };
3109     template<> struct superset<int,double> { typedef double type; };
3110     template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
3111     template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
3112     template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
3113     template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
3114     template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
3115     template<> struct superset<cimg_uint64,float> { typedef double type; };
3116     template<> struct superset<cimg_uint64,double> { typedef double type; };
3117     template<> struct superset<cimg_int64,float> { typedef double type; };
3118     template<> struct superset<cimg_int64,double> { typedef double type; };
3119     template<> struct superset<float,cimg_uint64> { typedef double type; };
3120     template<> struct superset<float,cimg_int64> { typedef double type; };
3121     template<> struct superset<float,double> { typedef double type; };
3122 
3123 #ifdef cimg_use_half
3124     template<> struct superset<half,unsigned short> { typedef float type; };
3125     template<> struct superset<half,short> { typedef float type; };
3126     template<> struct superset<half,unsigned int> { typedef float type; };
3127     template<> struct superset<half,int> { typedef float type; };
3128     template<> struct superset<half,cimg_uint64> { typedef float type; };
3129     template<> struct superset<half,cimg_int64> { typedef float type; };
3130     template<> struct superset<half,float> { typedef float type; };
3131     template<> struct superset<half,double> { typedef double type; };
3132 #endif
3133 
3134     template<typename t1, typename t2, typename t3> struct superset2 {
3135       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
3136     };
3137 
3138     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
3139       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
3140     };
3141 
3142     template<typename t1, typename t2> struct last { typedef t2 type; };
3143 
3144 #define _cimg_Tt typename cimg::superset<T,t>::type
3145 #define _cimg_Tfloat typename cimg::superset<T,float>::type
3146 #define _cimg_tfloat typename cimg::superset<t,float>::type
3147 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
3148 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
3149 
3150     // Define variables used internally by CImg.
3151 #if cimg_display==1
3152     struct X11_static {
3153       unsigned int nb_wins;
3154       pthread_t *events_thread;
3155       pthread_cond_t wait_event;
3156       pthread_mutex_t wait_event_mutex;
3157       CImgDisplay **wins;
3158       Display *display;
3159       unsigned int nb_bits;
3160       bool is_blue_first;
3161       bool is_shm_enabled;
3162       bool byte_order;
3163 
3164 #ifdef cimg_use_xrandr
3165       XRRScreenSize *resolutions;
3166       Rotation curr_rotation;
3167       unsigned int curr_resolution;
3168       unsigned int nb_resolutions;
3169 #endif
3170       X11_static():nb_wins(0),events_thread(0),display(0),
3171                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
3172 #ifdef __FreeBSD__
3173         XInitThreads();
3174 #endif
3175         wins = new CImgDisplay*[1024];
3176         pthread_mutex_init(&wait_event_mutex,0);
3177         pthread_cond_init(&wait_event,0);
3178 
3179 #ifdef cimg_use_xrandr
3180         resolutions = 0;
3181         curr_rotation = 0;
3182         curr_resolution = nb_resolutions = 0;
3183 #endif
3184       }
3185 
3186       ~X11_static() {
3187         delete[] wins;
3188         /*
3189           if (events_thread) {
3190           pthread_cancel(*events_thread);
3191           delete events_thread;
3192           }
3193           if (display) { } // XCloseDisplay(display); }
3194           pthread_cond_destroy(&wait_event);
3195           pthread_mutex_unlock(&wait_event_mutex);
3196           pthread_mutex_destroy(&wait_event_mutex);
3197         */
3198       }
3199     }; // struct X11_static { ...
3200 #if defined(cimg_module)
3201     X11_static& X11_attr();
3202 #elif defined(cimg_main)
3203     X11_static& X11_attr() { static X11_static val; return val; }
3204 #else
3205     inline X11_static& X11_attr() { static X11_static val; return val; }
3206 #endif
3207 
3208 #elif cimg_display==2
3209     struct Win32_static {
3210       HANDLE wait_event;
3211       Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); }
3212     }; // struct Win32_static { ...
3213 #if defined(cimg_module)
3214     Win32_static& Win32_attr();
3215 #elif defined(cimg_main)
3216     Win32_static& Win32_attr() { static Win32_static val; return val; }
3217 #else
3218     inline Win32_static& Win32_attr() { static Win32_static val; return val; }
3219 #endif
3220 #endif
3221 #define cimg_lock_display() cimg::mutex(15)
3222 #define cimg_unlock_display() cimg::mutex(15,0)
3223 
3224     struct Mutex_static {
3225 #if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1)
3226       pthread_mutex_t mutex[32];
3227       Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
3228       void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
3229       void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
3230       int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
3231 #elif cimg_OS==2
3232       HANDLE mutex[32];
3233       Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); }
3234       void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
3235       void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
3236       int trylock(const unsigned int) { return 0; }
3237 #else
3238       Mutex_static() {}
3239       void lock(const unsigned int) {}
3240       void unlock(const unsigned int) {}
3241       int trylock(const unsigned int) { return 0; }
3242 #endif
3243     }; // struct Mutex_static { ...
3244 #if defined(cimg_module)
3245     Mutex_static& Mutex_attr();
3246 #elif defined(cimg_main)
3247     Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
3248 #else
3249     inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
3250 #endif
3251 
3252 #if defined(cimg_use_magick)
3253     struct Magick_static {
3254       Magick_static() {
3255         Magick::InitializeMagick("");
3256       }
3257     }; // struct Magick_static { ...
3258     static Magick_static _Magick_static;
3259 #endif
3260 
3261 #if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread)
3262     struct FFTW3_static {
3263       FFTW3_static() {
3264         fftw_init_threads();
3265       }
3266     }; // struct FFTW3_static { ...
3267     static FFTW3_static _FFTW3_static;
3268 #endif
3269 
3270 #if cimg_display==1
3271     // Define keycodes for X11-based graphical systems.
3272     const unsigned int keyESC        = XK_Escape;
3273     const unsigned int keyF1         = XK_F1;
3274     const unsigned int keyF2         = XK_F2;
3275     const unsigned int keyF3         = XK_F3;
3276     const unsigned int keyF4         = XK_F4;
3277     const unsigned int keyF5         = XK_F5;
3278     const unsigned int keyF6         = XK_F6;
3279     const unsigned int keyF7         = XK_F7;
3280     const unsigned int keyF8         = XK_F8;
3281     const unsigned int keyF9         = XK_F9;
3282     const unsigned int keyF10        = XK_F10;
3283     const unsigned int keyF11        = XK_F11;
3284     const unsigned int keyF12        = XK_F12;
3285     const unsigned int keyPAUSE      = XK_Pause;
3286     const unsigned int key1          = XK_1;
3287     const unsigned int key2          = XK_2;
3288     const unsigned int key3          = XK_3;
3289     const unsigned int key4          = XK_4;
3290     const unsigned int key5          = XK_5;
3291     const unsigned int key6          = XK_6;
3292     const unsigned int key7          = XK_7;
3293     const unsigned int key8          = XK_8;
3294     const unsigned int key9          = XK_9;
3295     const unsigned int key0          = XK_0;
3296     const unsigned int keyBACKSPACE  = XK_BackSpace;
3297     const unsigned int keyINSERT     = XK_Insert;
3298     const unsigned int keyHOME       = XK_Home;
3299     const unsigned int keyPAGEUP     = XK_Page_Up;
3300     const unsigned int keyTAB        = XK_Tab;
3301     const unsigned int keyQ          = XK_q;
3302     const unsigned int keyW          = XK_w;
3303     const unsigned int keyE          = XK_e;
3304     const unsigned int keyR          = XK_r;
3305     const unsigned int keyT          = XK_t;
3306     const unsigned int keyY          = XK_y;
3307     const unsigned int keyU          = XK_u;
3308     const unsigned int keyI          = XK_i;
3309     const unsigned int keyO          = XK_o;
3310     const unsigned int keyP          = XK_p;
3311     const unsigned int keyDELETE     = XK_Delete;
3312     const unsigned int keyEND        = XK_End;
3313     const unsigned int keyPAGEDOWN   = XK_Page_Down;
3314     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
3315     const unsigned int keyA          = XK_a;
3316     const unsigned int keyS          = XK_s;
3317     const unsigned int keyD          = XK_d;
3318     const unsigned int keyF          = XK_f;
3319     const unsigned int keyG          = XK_g;
3320     const unsigned int keyH          = XK_h;
3321     const unsigned int keyJ          = XK_j;
3322     const unsigned int keyK          = XK_k;
3323     const unsigned int keyL          = XK_l;
3324     const unsigned int keyENTER      = XK_Return;
3325     const unsigned int keySHIFTLEFT  = XK_Shift_L;
3326     const unsigned int keyZ          = XK_z;
3327     const unsigned int keyX          = XK_x;
3328     const unsigned int keyC          = XK_c;
3329     const unsigned int keyV          = XK_v;
3330     const unsigned int keyB          = XK_b;
3331     const unsigned int keyN          = XK_n;
3332     const unsigned int keyM          = XK_m;
3333     const unsigned int keySHIFTRIGHT = XK_Shift_R;
3334     const unsigned int keyARROWUP    = XK_Up;
3335     const unsigned int keyCTRLLEFT   = XK_Control_L;
3336     const unsigned int keyAPPLEFT    = XK_Super_L;
3337     const unsigned int keyALT        = XK_Alt_L;
3338     const unsigned int keySPACE      = XK_space;
3339     const unsigned int keyALTGR      = XK_Alt_R;
3340     const unsigned int keyAPPRIGHT   = XK_Super_R;
3341     const unsigned int keyMENU       = XK_Menu;
3342     const unsigned int keyCTRLRIGHT  = XK_Control_R;
3343     const unsigned int keyARROWLEFT  = XK_Left;
3344     const unsigned int keyARROWDOWN  = XK_Down;
3345     const unsigned int keyARROWRIGHT = XK_Right;
3346     const unsigned int keyPAD0       = XK_KP_0;
3347     const unsigned int keyPAD1       = XK_KP_1;
3348     const unsigned int keyPAD2       = XK_KP_2;
3349     const unsigned int keyPAD3       = XK_KP_3;
3350     const unsigned int keyPAD4       = XK_KP_4;
3351     const unsigned int keyPAD5       = XK_KP_5;
3352     const unsigned int keyPAD6       = XK_KP_6;
3353     const unsigned int keyPAD7       = XK_KP_7;
3354     const unsigned int keyPAD8       = XK_KP_8;
3355     const unsigned int keyPAD9       = XK_KP_9;
3356     const unsigned int keyPADADD     = XK_KP_Add;
3357     const unsigned int keyPADSUB     = XK_KP_Subtract;
3358     const unsigned int keyPADMUL     = XK_KP_Multiply;
3359     const unsigned int keyPADDIV     = XK_KP_Divide;
3360 
3361 #elif cimg_display==2
3362     // Define keycodes for Windows.
3363     const unsigned int keyESC        = VK_ESCAPE;
3364     const unsigned int keyF1         = VK_F1;
3365     const unsigned int keyF2         = VK_F2;
3366     const unsigned int keyF3         = VK_F3;
3367     const unsigned int keyF4         = VK_F4;
3368     const unsigned int keyF5         = VK_F5;
3369     const unsigned int keyF6         = VK_F6;
3370     const unsigned int keyF7         = VK_F7;
3371     const unsigned int keyF8         = VK_F8;
3372     const unsigned int keyF9         = VK_F9;
3373     const unsigned int keyF10        = VK_F10;
3374     const unsigned int keyF11        = VK_F11;
3375     const unsigned int keyF12        = VK_F12;
3376     const unsigned int keyPAUSE      = VK_PAUSE;
3377     const unsigned int key1          = '1';
3378     const unsigned int key2          = '2';
3379     const unsigned int key3          = '3';
3380     const unsigned int key4          = '4';
3381     const unsigned int key5          = '5';
3382     const unsigned int key6          = '6';
3383     const unsigned int key7          = '7';
3384     const unsigned int key8          = '8';
3385     const unsigned int key9          = '9';
3386     const unsigned int key0          = '0';
3387     const unsigned int keyBACKSPACE  = VK_BACK;
3388     const unsigned int keyINSERT     = VK_INSERT;
3389     const unsigned int keyHOME       = VK_HOME;
3390     const unsigned int keyPAGEUP     = VK_PRIOR;
3391     const unsigned int keyTAB        = VK_TAB;
3392     const unsigned int keyQ          = 'Q';
3393     const unsigned int keyW          = 'W';
3394     const unsigned int keyE          = 'E';
3395     const unsigned int keyR          = 'R';
3396     const unsigned int keyT          = 'T';
3397     const unsigned int keyY          = 'Y';
3398     const unsigned int keyU          = 'U';
3399     const unsigned int keyI          = 'I';
3400     const unsigned int keyO          = 'O';
3401     const unsigned int keyP          = 'P';
3402     const unsigned int keyDELETE     = VK_DELETE;
3403     const unsigned int keyEND        = VK_END;
3404     const unsigned int keyPAGEDOWN   = VK_NEXT;
3405     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
3406     const unsigned int keyA          = 'A';
3407     const unsigned int keyS          = 'S';
3408     const unsigned int keyD          = 'D';
3409     const unsigned int keyF          = 'F';
3410     const unsigned int keyG          = 'G';
3411     const unsigned int keyH          = 'H';
3412     const unsigned int keyJ          = 'J';
3413     const unsigned int keyK          = 'K';
3414     const unsigned int keyL          = 'L';
3415     const unsigned int keyENTER      = VK_RETURN;
3416     const unsigned int keySHIFTLEFT  = VK_SHIFT;
3417     const unsigned int keyZ          = 'Z';
3418     const unsigned int keyX          = 'X';
3419     const unsigned int keyC          = 'C';
3420     const unsigned int keyV          = 'V';
3421     const unsigned int keyB          = 'B';
3422     const unsigned int keyN          = 'N';
3423     const unsigned int keyM          = 'M';
3424     const unsigned int keySHIFTRIGHT = VK_SHIFT;
3425     const unsigned int keyARROWUP    = VK_UP;
3426     const unsigned int keyCTRLLEFT   = VK_CONTROL;
3427     const unsigned int keyAPPLEFT    = VK_LWIN;
3428     const unsigned int keyALT        = VK_LMENU;
3429     const unsigned int keySPACE      = VK_SPACE;
3430     const unsigned int keyALTGR      = VK_CONTROL;
3431     const unsigned int keyAPPRIGHT   = VK_RWIN;
3432     const unsigned int keyMENU       = VK_APPS;
3433     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
3434     const unsigned int keyARROWLEFT  = VK_LEFT;
3435     const unsigned int keyARROWDOWN  = VK_DOWN;
3436     const unsigned int keyARROWRIGHT = VK_RIGHT;
3437     const unsigned int keyPAD0       = 0x60;
3438     const unsigned int keyPAD1       = 0x61;
3439     const unsigned int keyPAD2       = 0x62;
3440     const unsigned int keyPAD3       = 0x63;
3441     const unsigned int keyPAD4       = 0x64;
3442     const unsigned int keyPAD5       = 0x65;
3443     const unsigned int keyPAD6       = 0x66;
3444     const unsigned int keyPAD7       = 0x67;
3445     const unsigned int keyPAD8       = 0x68;
3446     const unsigned int keyPAD9       = 0x69;
3447     const unsigned int keyPADADD     = VK_ADD;
3448     const unsigned int keyPADSUB     = VK_SUBTRACT;
3449     const unsigned int keyPADMUL     = VK_MULTIPLY;
3450     const unsigned int keyPADDIV     = VK_DIVIDE;
3451 
3452 #else
3453     // Define random keycodes when no display is available.
3454     // (should rarely be used then!).
3455     const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent)
3456     const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent)
3457     const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent)
3458     const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent)
3459     const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent)
3460     const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent)
3461     const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent)
3462     const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent)
3463     const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent)
3464     const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent)
3465     const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent)
3466     const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent)
3467     const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent)
3468     const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent)
3469     const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent)
3470     const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent)
3471     const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent)
3472     const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent)
3473     const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent)
3474     const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent)
3475     const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent)
3476     const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent)
3477     const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent)
3478     const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent)
3479     const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent)
3480     const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent)
3481     const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent)
3482     const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent)
3483     const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent)
3484     const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent)
3485     const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent)
3486     const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent)
3487     const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent)
3488     const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent)
3489     const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent)
3490     const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent)
3491     const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent)
3492     const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent)
3493     const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent)
3494     const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent)
3495     const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent)
3496     const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent)
3497     const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent)
3498     const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent)
3499     const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent)
3500     const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent)
3501     const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent)
3502     const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent)
3503     const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent)
3504     const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent)
3505     const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent)
3506     const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent)
3507     const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent)
3508     const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent)
3509     const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent)
3510     const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent)
3511     const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent)
3512     const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent)
3513     const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent)
3514     const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent)
3515     const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent)
3516     const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent)
3517     const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent)
3518     const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent)
3519     const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent)
3520     const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent)
3521     const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent)
3522     const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent)
3523     const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent)
3524     const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent)
3525     const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent)
3526     const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent)
3527     const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent)
3528     const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent)
3529     const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent)
3530     const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent)
3531     const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent)
3532     const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent)
3533     const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent)
3534     const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent)
3535     const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent)
3536     const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent)
3537     const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent)
3538     const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent)
3539     const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent)
3540     const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent)
3541     const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent)
3542     const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent)
3543 #endif
3544 
3545     const double PI = 3.14159265358979323846;   //!< Value of the mathematical constant PI
3546 
3547     // Define a 10x13 binary font (small sans).
3548     static const char *const data_font_small[] = {
3549       "                      UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw     Mw                    (wnwnwuwpwuypwuwoy"
3550       "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y    Hw   cwnw  >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w "
3551       "ry    qw %wuw  !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow"
3552       "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq"
3553       "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd"
3554       "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp"
3555       "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw "
3556       "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwsw<wrwowrwuwqwrwqwswrwswpwmwmwrwswrwowl"
3557       "wqwtwownxsxsxswqwswqwswqwswrwswqwrwowpwrwrwqwtwswswswswqwswmwpwmwlwoxuxSw_wfwdwYwkw(w0wmwmwGwtwoxnwNw uwswpwuwp"
3558       "wmwmwswq{rwrwrwtwtwrwswfydwdyZwnwtwrwqwrwswowowdwrwqxuwSwrwfwuwnwlwnw[yuw[wowtwgwswqwswqwswewuwowuwowuwowuwowuw"
3559       "nwowuwowswqwmwmwmwjwmwnwmwowswrxswqwswqwswqwswqwswqwswrwrwqwswrwrwrwrwrwrwrwrwqwswqzpwtw #w DwPwtwtwswqwswuwuwu"
3560       "wswswuwswqwGwqxtwf{qzr~r{qzqwrwpxowtwrw rzcwnwuwq}rwuwqwtwuwqwtwmwnwlwnynwOwowswowkwmwpwuwpwmwjwpwswqwswowmwjwi"
3561       "wjxswsytwrwuwqwrwrwmwrwqwmwnwmwrwowlwqwuwnwnxsxswuwtwrwqwrwswrwqwswswqwjwpwrwqwswrwtwtwqwuwowuwmwowmwlwpxsx]ypz"
3562       "oyozpypzozqznwmwowtwnwqzuyrzoypzozqwuxoypzpwswrwrwrwtwtwswrwrwrwq{owmwmwQyuwqwtwmwoxnypzqxswowowswqwswqwtxr|rwt"
3563       "wtwqyp{q{qwswpwuwownwnwqwsxuwuxswrwrwtwtwswqwrwmwuwuwnwnwowtwpwuwuwewnzpwn{pwuwnwnxgwtxtwrwtwowtw_wuytwgynwmwlw"
3564       "gwswpyuw[wowtwqwtwpwtwpwtwowuwmwnwuwowuwowuwowuwowuwowuwqxuwpwlwmwmwmwjwmwnwmwowrwswuwtwrwqwswqwswqwswqwswqwrwt"
3565       "wqwswuwswrwrwrwrwrwrwrwpwuwpwswqwuwnyoyoyoyoyoyqyuyqyoyoyoyoymwqwjwmwnypzoyoyoyoyoynwnzqwswqwswqwswqwswrwrwqzqw"
3566       "rw^}s}swtwtwswtwtwswtwtwK}rwuwe{s~t~s}rwtwqwrwpxowtwrw qwawewtwpwuwpxuwpycwlwnynwOwowswowkwpypwtwpzpzmwoypwsw[y"
3567       "r}rymwrwtwtwtwrwuwq{qwmwrwq{q{rwm|owlwqxmwnwuwuwuwswuwtwrwqwrwswrwqwswswqylwpwrwqwswrwuwuwuwpwmwmwnwmwlwMwqwswq"
3568       "wmwswqwswpwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqxnwswpwnwswrwrwrwtwtwrwtwqwrwmwqxlwlx]xuxrwtyqwuwlwpwtwpwmw"
3569       "swqwtxpxowswrwqwswtwuxrwtwqwtwtwrwswrwswnwo{pwuwnxpwnwqwswtwtwswrwrwtwtwswuyuwswjwkwowpwrwowcwowuwnwnwswqxuxowo"
3570       "wtwhwuwrwrzpwtwq}jwuwtwuw_}qyoxfwswpyuwowdyoxowtwryuwqyuwqyuwmwnwuwowuwowuwowuwowuwowuwqwt{twl{q{q{q{nwmwnwmwpz"
3571       "twswuwtwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwuwkwmwmwmwmwmwowswswmwswqwswqwswqwswnwqwjwmwowswqws"
3572       "wqwswqwswqwswqwswqwswgwtxqwswqwswqwswqwswrwrwqwswrwrw^wtwtwswqwswuwuwuwswuwswswqwHwowuwf}t~s|r}swrwrwrwqwtwpwtw"
3573       "r~#zcwewtwoynwuxtwtwswgwlwowuwuwr}gyexowswowlwlwrwswlwqwswowowswpz^yayqwqwtwtwuwrwswrwrwrwmwrwqwmwnwsyswrwowlwq"
3574       "wuwnwnwuwuwuwswtwuwrwqwrzqwqwszmyowpwrwpwuwqwuwuwuwpwmwmwnwlwmwPzqwswqwmwswq{pwnwswqwswowmwoxlwqwswswswswqwswqw"
3575       "swqwswqwlxnwnwswqwtwqwuwuwuwqxowtwmwnwmwmwoytwiwtwtwswswpwtxqzpwswpxowswpwuwowuwpwswrwtwtwswtwtwrwtwqwtwtwrwswr"
3576       "wswnwowswqwswowownwqwswtwtwswrwqwuwuwrwuyuwt~pwq~pwq~pwcwowuwozpwswowewswiwuwrwiwtwjwjwuytw\\wRwswoxuwHwtwpwswq"
3577       "wtxqwswqxowswqwswqwswqwswqwswqwswrwtwpwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwow"
3578       "owswqwtwozpzpzpzpzpzr~swm{q{q{q{nwqwjwmwowswqwswqwswqwswqwswqwswqwswr}rwuwuwqwswqwswqwswqwswqwtwpwswqwtw\\wuwuw"
3579       "qwswqwswqwswqwswJ}qxf}t~rzp{rwrwrwrwqwtwpwtwrw qwawg}owuwpwuwtwuwswuwfwlwmwmwPwnwswowmwkwr|mwqwswowowswmw^yo}oy"
3580       "qwqwszq{rwrwrwmwrwqwmwnwqwswrwowlwqwtwownwtwtwswtwuwrwqwrwnwqwswtwkwowpwrwpwuwqwuwuwuwqwuwnwnwmwlwmwQwswqwswqwm"
3581       "wswqwlwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqwjwownwswqwtwqwuwuwuwqxowtwnwmwmwmwpwtyhwtwtwswswpwswqwtwpwswqw"
3582       "mwswpwuwpwtwpwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswqwswpwnwnwqwsxuwuxswrwpyqwqwswjwkwqwuwuwrwrwqwuwuwewowuwnwn"
3583       "wswq{ownxuwiwtxtwrzpwtwkwjwuwtwuw\\wRwswnwuwSzpwtwowtxqwrwrwtxrxn{q{q{q{q{q{s{pwlwmwmwmwjwmwnwmwowrwswtwuwrwqws"
3584       "wqwswqwswqwswqwrwtwqwuwswswrwrwrwrwrwrwrwowozpwswqwswqwswqwswqwswqwswqwswswswowmwmwmwmwjwqwjwmwowswqwswqwswqwsw"
3585       "qwswqwswqwswgwuwuwqwswqwswqwswqwswqwtwpwswqwtw[yoyoyoyoyGwmwdwuwuwpxnxnyqwrwqwtwpwtwoxpw rwswSwuwmwuwpwuwtwuxsw"
3586       "ewlwcwPwnxuxownwnwswnwlwqwswowowswnwZygygwkwswrwrwqwswrwswpwmwmwrwswrwowlwqwswpwnwqwswsxqwswqwmwswrwswqwrwowpxt"
3587       "xowowswqwswowowlwlwmwQwswqwswqwmwswqwswpwnwswqwswowmwowtwnwqwswswswswqwswqwswqwswqwmwswpwnwswpxowswqwtwoxnwlwmw"
3588       "mw[xuxrwtxpwswqwtwpwswqwmwswpypwtwpwswrwtwtwsxuwuxrwtwqwtwtwrwswrwswnwnwuwpwswqwmwmwswq{rwrwowowswqwkwlwoypwtwo"
3589       "ydwowuwnwn{owmwlwgwrwfwtw^wrw6wswnwuwJwtwowtzswrwrwtzswmwswqwswqwswqwswqwswqwswswswowswqwmwmwmwjwmwnwmwowswrwsx"
3590       "qwswqwswqwswqwswqwswrwrwqwswrxtxrxtxrxtxrxtxowowmwswqwswqwswqwswqwswqwswqwswswtxowmwswqwswqwswqwswnwqwjwmwowswq"
3591       "wswqwswqwswqwswqwswqwswowoxtwqwswqwswqwswqwswpxowswpx Wwlwbwnxcwpwrwqzpwtwoxo|!ydwfwtwozpwsxszuxgxnxcwmwcwoxmyp"
3592       "{q{pymwpzoyowmypymwmwjwiwkwowrwrwqws{oyqzo{qwlzrwrwowlwqwrwq{rwqwswsxpypwlyqwrwqznwoznwowswrxsxpwp}qwkwnwPzqzoy"
3593       "ozpyowmzqwswowmwowswowqwswswswswpypzozqwlynxozpxowswrwrwpwn{owmwmwQxuxqzoxnyoypwswowpwrwqzpxuxq{qwtxq{qzpylwoyq"
3594       "}r{qwnyuypwpwrwownydwcwcwcwnzq{rwqwpwmwkwgzHz]}U|owuw@wqwswrytwqwqyqwqwswqwswqwswqwswqwswqwuwr{ryp{q{q{q{nwmwnw"
3595       "mwozqwsxpyoyoyoyoygwuypzpzpzpznwowmwuypzpzpzpzpzpzryuzryoyoyoyoymwqwjwmwnypwswpyoyoyoyoyfzozpzpzpzpwnzow    \\w"
3596       "OwnwXw[w SwGz kx0x lxdx gw[w=wiw*wbyowoyGwKwowewawcwow  YwOwoz Ewjwuwdw 7w   9w  Iwnwlw    \\w      0|*y[x=wiw,"
3597       "xWw=wKwowewawcwow  Yw  hwVx 8w   9w  Jxmwnxp" };
3598 
3599     // Define a 26x32 font (normal sans).
3600     static const char *const data_font_normal[] = {
3601       "                                                      #{}~}a{|y~f{|y~}f{|}|x{}|j{|y}y{|y}g{}y~}|2y~|a{}~}f{}y~|"
3602       "gy}|yy}|i{}~}a{}~}f{}y~}gy}|yx}N{|}|x{}|hy~|ay~|fx~|g{}y~|y{}~j{|y~|yy~}5{}~}a{}~}f{}y~}gy~}yy~}e{|y~          "
3603       "                                                      2{}~}c{|y~f{|y~}~}h{}w~y}~|j{}y~y{}y~h{}~y}y~|2y~|c{}~}f{"
3604       "}~y}~|hy~}yy~}hy~|c{|~}f{|~y}~|hy~}y{}y~O{}w~y}~|gy~|cy~|fy~|}~|i{|~y}y~}~}j{|y~|yy~}4{}~|c{}~}f{}~|}~}hy~}yy~}"
3605       "ey~|  g{|}y~}                                                              J{}~|dy~|fy~y{}~|i{~}{|}y~}i{}y~y{}y"
3606       "~i{|~}x{~}2{|y~d{|~}f{|~}yy~hy~}yy~}gy~cy~f{|~}y{}~|iy~}y{}y~P{|~}{|}y~|ey~d{}~|fy~x{}~|j{}~y{|}~}i{|y}|yy}|3{}"
3607       "~|e{}~}f{}~|y{|~|iy}|yx}f{}~|  fy~y}~}           k{|y~|                       /{|y~|                        y{}"
3608       "~}   Xy|e{|}|f{|}wy|5{|~|x{}~1{|}|ey|ey|wy|M{|}|e{|}|fy|wy|    g{|}|3{|y~|_{}~}g{|y~2{}~|y{}~|5{|y~^y~}g{}y~N{|"
3609       "}|^{|}|g{|}| s{}~}_{|y~|gy~} Z{}~}_{|y~|gy~}    )y}|                       -{|y~                    Jy}|yy}|   "
3610       "X{}y~    4{|~}y{|~}     P{|  n{|y~`{|y~fx~}3{}~x{|~|4{}~}`{}~}g{|x~}N{}~}`{|y~|gx~| sy~|`y~|g{}x~| Z{}~}`y~}g{}"
3611       "x~|I{}y~  1{|x~|oi| r{|~|O{|d{|y}|j{|y}|u{|y}|h{|   \"{|}x~}|Ny~}g{|y~y{|~}g{|~}x{|~}i{|~l{|}y~}|s{|~}l{|}x~}|e"
3612       "{|y~by~g{}~}b{~} S{|y~i{|}x~}|i{|y}x~|i{|y}x~y}i{|y}w~}|d{}x~kq~|i{|}w~}|m{}o~k{|}x~y}h{|}x~}| B{|}w~}L{|x~j{|s"
3613       "~y}g{|}w~}|o{|s~y}|k{|o~n{|p~j{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|t{|x~n{|y~|i{|x~}r{|x~}s{|x~|sy~}l{|}x~y}|l{|"
3614       "s~}|i{|}x~y}|m{|s~}|hy}w~y}|ok~}r{}y~r{|y~|r{}y~|p{}y~vy~}t{|x~sy~}ux~rx~q{}y~|r{}y~|r{|l~l{}v~hy~|c{|v~|f{|}|L"
3615       "{}~}M{}y~@{}~}O{|}w~R{}y~`{|y~d{|y~h{}y~`{|y~    ay}y~}h{}~}h{}y~y} Wy}x~}|O{|y}w~}| xx~}  I{|}x~}f{|x~i{|o~m{|"
3616       "o~m{|y}x~}|f{}y~k{|m~}r{|y~|w{}y~vy~}n{|}x~y}|My}Iy}|J{}~| q{|}x~y}T{}y~r{}~}R{}w~}|j{|y~}yy~}O{|}w~} \\{|t~}h{"
3617       "|}y~}M{|}x~}|h{|}x~}|e{|y~L{|}t~|7y}y~}f{|}x~}Uy|y}|py}p{|n{|t{|}w~}r{|y~P{|x~e{|x~e{|x~e{|x~e{|x~f{}v~|jk~|o{|"
3618       "}w~}|m{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|x~|sy~}l{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}"
3619       "|O{|}x~y}|y{|~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~|r{}y~|p{|y~|b{|}x~}|h{}~}b{|y~|g{}~|}~|h{|y~}|"
3620       "{}~iy~}yy~}i{}~|y{}~|3{}~}b{|~}fy~{}~|i{|y~|{|y~}iy~|ay~}g{}~}y~gy~}yy~}i{|y~}wy|j{|y~}y{}~hy~|b{}~}g{|~}|~}h{|"
3621       "}y~}{|~}j{}y~y{}y~|4y~|b{}~}g{|~}{y~h{}y~y{|y~|f{|y~|k{}y~by~}y{}y~  ev~o{}k~} r{}~O{|~e{}v~l{}v~w{}w~}j{}~ Y{}"
3622       "o~  S{|s~}Oy~}g{|y~y{|~}g{}~|x{}~|i{|~m{|y~y}y~|ty~l{}t~}f{|y~c{}~}fy~b{~} S{}~}j{}t~}kt~|j{}r~|l{|r~}f{|w~kq~|"
3623       "j{}s~|n{}p~}m{|r~|l{|s~| D{}s~|i{|y}y~y}|hw~|k{|p~|k{|q~}q{|p~}|m{|o~n{|p~l{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|u{|"
3624       "x~m{|y~|i{|w~|sw~}s{|w~sy~}n{}r~}m{|q~}l{}r~}n{|q~}k{|q~|pk~}r{}y~r{|y~|r{|y~}py~}v{}y~t{}x~|u{|y~|u{|y~}t{}y~|"
3625       "py~}s{|y~}q{|l~l{}w~}h{}~}c{|v~|gw~}L{}~|N{}y~@{}~}P{|u~R{}y~`{|y~d{|y~h{}y~`{|y~  e{}y~  {}w~}h{}~}h{}v~ Ys~}Q"
3626       "{|r~| yv~  K{}t~|hw~|j{|o~m{|o~n{}r~|h{}y~k{|m~}r{|y~|w{}y~vy~}p{}r~}O{}x~Jy~|K{}x~|/{~|f{}t~}Ty~|t{|y~|Ss~j{|y"
3627       "~}yy~}i{|}v~}|j{}~w}y~ v{|}v~}|k{|t~}i{|y~}x~N{}~}|}y~|i{|y}y|}~}fy~|N{|u~y}y~|8{|~y}~}g{|y~x}y~W{|w~}q{}~}s{}x"
3628       "~}q{|y~t{|}x|}~}s{}~|Pw~|fw~|fw~|fw~|fw~|fw~|j{|k~|q{|q~}o{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|w"
3629       "~sy~}n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}R{}r~}|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|py~}s{|y~}o{|y~|cs~}h{"
3630       "}~}cy~|g{|~}yy~i{|y~}w~}iy~}yy~}hy~y}~}1y~|d{|y~f{}~|{|~}i{|y~|{|y~}i{|y~b{}~}g{|~}{|~}hy~}yy~}h{|y~y}y~}|k{|y~"
3631       "}y~}~}h{|y~c{|~}fy~y{|~|i{}~}v~|j{}y~y{}y~|4{|y~c{|~}fy~y{|~}i{}y~y{|y~|fy~|j{}y~by~}y{}y~  f{|y~{}~|p{|k~| r{~"
3632       "}Oy~}g{}u~}n{}t~y{}u~}l{}y~} \\{}m~  T{|x~}|{y|y~|Py~}g{|y~y{|~}gy~|xy~|i{|~my~|y{|y~u{}~}m{}y~}|y{|y}f{|y~d{|y"
3633       "~e{}~}hy|x{~}x{| Wy~|k{|y~}|{|}y~}lx~y}y~|jx~}x|}x~|m{|~}v|x~}gv~ky~s|j{}x~w|}~|nr|}y~|mx~}|{y|x~|mx~y|{|}y~| E"
3634       "y~}x|}x~k{}q~|k{|w~}k{|y~u|}x~l{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~r|m{}x~}v|}x~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|v{|"
3635       "x~l{|y~|i{|w~}t{|w~}s{|w~}ty~}o{}x~}|{y|}x~n{|y~v|}x~|n{}x~}|{y|}x~o{|y~v|}x~}m{|x~}v|}~|pt|y~}u|q{}y~r{|y~|qx~"
3636       "q{|y~|v{|y~|u{}x~|u{}y~|t{}y~|v{|y~}o{|y~|tx~op|}y~}l{}~}e{|y~`{|y~|h{}v~}L{}~|O{}y~@{}~}Py~}|O{}y~`{|y~d{|y~h{"
3637       "}y~`{|y~  e{}y~ !{|y~}e{}~}e{|y~| [{}y~|x{}y~|jy}~y}|ix~|w{|}| w{}y~|  M{}y~|y{|}y~i{|w~}ix~r|m{|y~q|p{|w~}x|}x"
3638       "~}l{|y}x~y}|n{|y~q|y~}r{|y~|w{}y~vy~}q{}x~}|{y|}x~Q{}v~Ky~|L{}v~|0{~|g{|y~}|y{|y}T{}y~t{}~}i{}~}h{}y~|x{|}P{}~y"
3639       "}x|y}~}k{|v{}~| x{}~y}x|y}~}Qy~x{|~}J{|y~cy~g{}~|Mt~y{}~|5{|~}gy~|x{}~}U{|~}r{|y~r{}y|~}qy~|ny~t{|~}P{|w~}g{|w~"
3640       "}g{|w~}g{|w~}g{|w~}fw~}j{}y~y|y~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}"
3641       "p{|w~}ty~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~T{}x~}|{y|v~|r{}y~r{|y~|q{}y~r{|y~|q"
3642       "{}y~r{|y~|q{}y~r{|y~|p{|y~|tx~n{|y~|d{}y~|x{}y~|h{}~|e{}~|f{~}x{|~}j{|}xx}hy}|yy}|h{|}y~}/y~dy~|g{|~}x{|~|j{|y}"
3643       "|yy}|h{|~}d{|~}f{}~x{}~|iy}|yy}|iy|w~|h{}~y{|}~}f{|~}e{|~}f{}~|x{}~|j{}|y{|y}|i{|y}y{|y}2{|~}dy~f{}~|x{}~|j{|y}"
3644       "y{|y}|g{}~|i{}y~by}|y{|y}5{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|f{}~}{y|ny~}q{}y~ r{|~O{}x~|hs~|p{|s~y|s~|n{|w"
3645       "~} ^{}y~}|  Ix~|u{}|Py~}g{|y~y{|~}gy~wy~k{|}v~y}|s{|y~w{}~|w{|y~ly~}_{|y~d{}~}dy~|iy~}y{~}{|y~|hy}| o{|y~jx~v{}"
3646       "y~l{|x{|y~|j{}|u{|x~d{}y~|i{}~|}y~ky~|d{|y~}]{}y~m{|y~|v{|y~}n{}y~|v{}y~ E{}u{}y~|n{|x~}|w{|}y~}|m{}y~}y~k{|y~|"
3647       "u{|y~}n{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}r{}~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~}y~t{}~}y~}s{|"
3648       "v~ty~}p{}y~}t{}y~}o{|y~|v{|x~o{}y~}t{}y~}p{|y~|v{|x~mx~r{|iy~}k{}y~r{|y~|q{|y~|r{}y~u{|y~|uy~}~}u{}y~rx~vx~m{}y"
3649       "~u{}y~|e{|y~}k{}~}dy~|a{|y~|i{}y~|{}y~| y{}y~@{}~}Py~|N{}y~0{}y~`{|y~  e{}y~ !{|y~d{}~}dy~} [y~}v{}~}ju~}jy~| n"
3650       "{}y~  N{|y~|v{}~}j{}y~}y~i{|y~}e{|y~|gx~}t{}y~}o{|}q~|p{|y~|ry~}r{|y~|w{}y~vy~}r{}y~}t{}y~}S{}t~Ly~|M{}t~|1{~|g"
3651       "{}y~Ly~|v{|y~|i{}~}hy~}L{|y~|t{|y~|g{|~} {{|y~|t{|y~|T{|~|wy~f{}~|ay~ey|y~7{}t~y{}~|5{|~}h{|~}vy~U{|~}r{}~|p{|~"
3652       "}r{|~}my~ty~O{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{|y~}y~jy~}yy~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{"
3653       "|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|v~ty~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}V{}y~}t{}x~q{}"
3654       "y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|o{}y~u{}y~|n{|y~|e{|y~|v{}~}   A{|}|ey|e{}|wy|Py~}y|y~}   ?{}~}h{}y~ p"
3655       "{}r~|l{}r~|l{}r~|l{}r~|l{}r~|h{}~}k{}y~qy~}1{|~}dy}P{}v~|is~|p{|r~}s~}nu~|h{|w}|k{|y}sy}|jx}|j{|y}t{|y}o{}y~|  "
3656       "H{|y~|Gy~}g{|y~y{|~}h{|~}x{|~}l{}r~}s{|~}w{}~}w{}~|ly~}_{|y~dy~|d{}~}h{|y~|~y}~}|g{}~| o{}~}k{|y~|uy~}i{|y~|a{}"
3657       "y~|e{|y~}j{|~}{}y~ky~|dy~}]{|y~}m{}y~tx~ny~}u{|y~|,{|X{|X{|y~|o{}y~|q{}y~my~}|y~|l{|y~|ty~}o{|x~p{|r{|y~|s{|x~o"
3658       "{|y~|e{|y~|g{|x~p{|q{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|y~|uy~|y~}s{|y~|y~}uy~}q{|x~r{}y~|p{|y~|u{}y~|"
3659       "q{|x~r{}y~|q{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|py~}s{|y~}ty~}v{|y~|y~uy~}r{|y~}x{}y~|ly~}w{|y~}e{|x~j{}~}d{}~}a{|y~|"
3660       "j{}y~|x{}y~| {{}y~@{}~}Py~|N{}y~0{}y~`{|y~  e{}y~ !{}y~d{}~}d{}~} \\{|y~u{}y~j{}x|}y~|kx~| o{|y~|  O{}~}u{|y~jy"
3661       "~}|y~|i{|y~}f{|y~|h{}y~|rx~|q{|w~y}y~y}x~}q{|y~|ry~}r{|y~|w{}y~vy~}s{|x~r{}y~|U{}y~|y~}x~My~|N{}y~|y~|x~|2{~|gy"
3662       "~}g{|p{}m{}y~v{}~}h{}~}h{}~}L{~}xy|}y|x{}~l{|}u~ {{~}p{}~T{|~|wy~f{}~|b{}~}g{}w~|7{}t~y{}~|5{|~}h{}~|v{}~|V{|~}"
3663       "s{|~}o{|~}ry~n{|}~|u{}~}Oy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|l{}y~|yy~}j{|x~p{|p{|y~|e{|y~|e{|y~|e{|"
3664       "y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|y~}uy~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|q{|}q{|"
3665       "}p{|x~s{}x~|r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|ny~}w{|y~}m{|s~}|l{|y~u{|y~    8{|w{|y~}  _{} G{}y~ r{|"
3666       "x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|i{}~|jy~|s{|y~|1y~}d{~|Q{|t~is~|p{}i~}os~j{|s~|m{"
3667       "|y~sy~|jw~j{|y~|u{}y~p{|y~|  Gx~Fy~}g{|y~y{|~}h{}~}x{}~}m{|y~}{|~x{|}s{|~}w{}~}x{|~}ky~}_{|y~e{|y~c{|y~f{}x~}|e"
3668       "{}~| oy~|k{}y~t{}y~i{|y~|a{|y~|dy~|jy~|{}y~ky~|e{|y~|]{}y~|m{}y~ty~}o{|y~|ty~}/{|}~}Xy~}|[{|y~|p{}y~|o{}y~o{|y~"
3669       "|{y~}l{|y~|ty~}o{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|e{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{|y~|i{|y~|}~}v{|y~{y~}s{"
3670       "|y~|}y~uy~}q{}y~|qx~p{|y~|u{}y~|q{}y~|qx~q{|y~|u{}y~|o{|y~|_y~}k{}y~r{|y~|p{}y~s{}y~|t{}y~v{|~}{y~|w{|y~|q{}y~|"
3671       "{|y~}k{|y~|xx~dx~|j{}~}d{|y~a{|y~|k{}y~|v{}y~|9{|y}x~y}j{}y~y{}x~}|h{|}x~y}|j{}x~}|{}~}k{|}x~}|j{|s~|i{}x~}|{}~"
3672       "}n{}y~y{}x~}|h{|y~d{|y~h{}y~u{|y~}j{|y~m{}y~y{}x~}w{|}y~}|p{}y~y{}x~}|i{|}x~}|k{}y~y{}x~}|i{}x~}|{}~}k{}y~y{}x~"
3673       "k{|}w~y}|k{|r~l{}~}t{}~}oy~}s{}y~r{}~}v{}y~}v{}~}r{|y~|u{|y~|oy~}s{}y~n{}p~h{}y~d{}~}d{}~} t{}x~}|y{|~}n{|y~u{}"
3674       "~}e{}y~k{|w~y}|g{|}w~y}l{}y~y{}x~}|n{}~}|s{}y~iy~}i{}~}t{}~}p{|y~|r{}y~n{|y}y{}y~}lm~p{}y~x{|y~x{|y~|k{}w~}|j{|"
3675       "}q~|q{}n~ny~|ty~|l{|y~|{y~}h{|y~}g{|y~|hy~}q{|y~}qx~}y{}y~y{|x~|r{|y~|ry~}r{|y~|w{}y~vy~}s{}y~|qx~V{|y~|{y~y|y~"
3676       "}Ny~|O{|y~|{y~|{y~}Ny~}e{|}w~}|jy~}h{|y~r{}~}my~|x{|y~|h{}~}h{|y~}Ny}x{}u~|yy}n{}y~w}y~ y}y{}v~}|xy}T{~}x{|~}f{"
3677       "}~|c{|~}fx|y}|Q{}~}t{}~}ns~y{}~|5{|~}h{}~|v{}~|V{|~}sy~n{|~}s{}~|p{}x~}u{|y~f{|y~|h{|y~|{y~}i{|y~|{y~}i{|y~|{y~"
3678       "}i{|y~|{y~}i{|y~|{y~}i{|y~|{y~}ly~}xy~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|r{|y~}r{|y~|"
3679       "}y~uy~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~qy~}s{|y~}q{}y~|t{}~}x~r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}"
3680       "y~r{|y~|n{|y~|xx~l{|q~}m{}y~w{|w~l{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}n{|y}x~y}w{|}x~}|l{|}x~y"
3681       "}|j{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|g{|y~d{|y~d{|y~d{|y~e{|}v~|l{}y~y{}x~}|i{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~"
3682       "}|g{|x~f{|}x~}|{}~|o{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}oy~}s{}y~n{}y~y{}x~}|my~}s{}y~;y~}xy~|y{|y~|py~}s{|y"
3683       "~|py~}s{|y~|py~}s{|y~|py~}s{|y~|j{}~|j{}y~sy~}1y~}d{|~Q{|s~}j{}t~o{|i~}p{}s~}kx~}y|}x~m{|y~sy~|k{|w~|jy~}uy~}py"
3684       "~}  Fy~}Fy~}g{|y~y{|~}m{|k~}q{}y~y{|~n{|~}w{}~|xy~j{}y~|`{|y~e{}~}by~|g{|x~}d{}~| p{|y~jy~}t{}y~i{|y~|a{|y~|e{|"
3685       "y~|k{}~}y{}y~ky~|{|g{}y~\\x~l{|y~|v{|y~|o{|y~|tx~i{}y~d{}y~a{|}w~}Xv~}|^x~p{|y~l{}~}p{}y~y{|y~|m{|y~|ty~}ox~e{|"
3686       "y~|qy~}p{|y~|e{|y~|gx~d{|y~|ry~}k{|y~|e{|y~|j{|y~|{}y~}h{|y~|i{|y~y|y~v{}~}{y~}s{|y~|{y~}vy~}qx~p{}y~|q{|y~|u{|"
3687       "y~|qx~p{}y~|r{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|p{|y~|ty~}s{}y~|w{}~|{}~|w{|y~|py~}{x~i{}y~y{}y~|e{}y~|i{}~}cy~|b{|y"
3688       "~|l{}y~|t{}y~|;{|r~|l{}y~|t~|j{}s~|m{|t~|}~}l{}s~|l{|s~|k{|t~|}~}n{}y~|t~|i{|y~d{|y~h{}y~v{}y~}i{|y~m{}y~|t~y{}"
3689       "u~}q{}y~|t~|l{|s~}l{}y~|t~|l{|t~|}~}k{}y~|v~l{|r~k{|s~}l{}~}t{}~}o{}y~sy~}r{|y~v{}x~vy~}q{}y~|w{|y~}n{}y~sy~}n{"
3690       "|p~h{}y~d{}~}d{}~} v{|t~|{}~|n{|y~u{}~}e{|y~|k{|t~|j{}s~|m{}y~|t~|o{}x~|ty~}ix~i{}~}t{}~}py~}q{|y~|p{|y~}{}v~|n"
3691       "m~p{}y~x{|y~x{|y~|ls~|l{|o~|q{}n~o{|y~|t{}~}l{}y~y{}y~|h{}y~}h{|y~|i{|y~|p{}y~r{}y~|x{}y~x{|x~r{|y~|ry~}r{|y~|w"
3692       "{}y~vy~}sx~p{}y~|p{}b{}|yy~|{|}b{}|hy~|i{|}s{}|ly|yy~|y{}My~}g{|r~k{|y~|gx~|}x~}|}y~|m{}y~xy~}g{}~}g{}x~|Q{|~|y"
3693       "y~}|yx|y{|~|p{|~}w{|y~gy|w{|<{|~|y{}~}x|y~|y{|~|U{}y~y}y~e{}~|d{|y~a{}~Q{}~}t{}~}n{}t~y{}~|5{|~}h{}~|v{}~|m{|v{"
3694       "|k{|~}t{}~|n{|~}t{|~}ox|}y~v{}~|f{|y~|h{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{}y~m{|y~|xy"
3695       "~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|{y~}vy~}qx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~"
3696       "|sx~p{}y~|r{|y~}u{|x~px~t{}~}{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|m{}y~y{}y~|l{|y~v|}x~}n{}y~x{}y~|"
3697       "k{|r~|l{|r~|l{|r~|l{|r~|l{|r~|l{|r~|q{|r~|{}s~n{}s~|l{}s~|k{}s~|k{}s~|k{}s~|i{|y~d{|y~d{|y~d{|y~g{|r~l{}y~|t~|l"
3698       "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}h{|x~h{|q~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}o{}y~sy~}n{}y~|t~|n{}y~sy~}<{}~"
3699       "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}t{|~y|y~q{}~}q{|y~j{}~|j{|y~|u{|y~|<q|}y~w|p{|y}uy}Qq~}k{|u~}o{|i~|q{|"
3700       "q~}m{}y~uy~}n{|y~sy~|k{}w~|j{}y~v{|y~|q{|y~  H{}o~My~}fy|xy|m{|k~}q{}~}y{|~n{|y~wy~|y{}~|ix~_y|ey~|by~}i{|}~}~}"
3701       "y~}f{}~| p{|~}jy~}t{|y~|j{|y~|a{}y~f{|}y~}k{|y~x{}y~kt~}|ky~}{|w~}|e{|y~|kx~|x{|y~}n{|y~|tx~i{}y~d{}y~d{|}v~}|q"
3702       "k|p{|}w~}|a{}y~|p{}~|w{|}y~}|{y|xy~|qy~}xy~}m{|y~|u{|y~|p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}d{|y~|ry~}k{|y~|e{|y~|"
3703       "j{|y~|}y~}g{|y~|i{|y~|{y~|wy~|{y~}s{|y~|{}y~vy~}r{|y~}p{|y~|q{|y~|u{}y~|r{|y~}p{|y~|r{|y~|u{}y~mx~}`y~}k{}y~r{|"
3704       "y~|oy~}u{|y~|s{|y~|wy~|{|~}w{}y~o{|v~|hy~}|y~}e{}y~}h{}~}c{}~}b{|y~|m{}y~|r{|y~|<{|}y|x{|}y~l{}w~|y{|x~|lx~}|yy"
3705       "|}|mx~|y{|}x~}mx~y|y{|x~iy~|h{|x~|y{|}x~}n{}w~|y{|x~i{|y~d{|y~h{}y~w{}y~|h{|y~m{}w~|y{|y~}|~}|{|}y~|r{}w~|y{|x~"
3706       "lx~|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}w~w|ly~}|xy|}i{}y~g{}~}t{}~}o{|y~|u{|y~|r{|y~|ww~vy~|px~wx~m{|y~|u{|y~|f{|"
3707       "y~}h{}y~d{}~}d{}~}6{|}x~|x{}x~|o{|y~}|{|}y~{y~m{|y~v{|y~|dy~|l{}~}x{|x~|l{}y~}|yy|}|m{}w~|y{|x~n{|}~}u{|y~|j{|x"
3708       "~|j{}~}t{}~}q{|y~|py~}q{|x~y|y~y|x~ny|y~}w|}y~}|p{}y~x{|y~x{|y~|mx~|y{|x~|n{|x~|x{}x~y|pu|y~}v|o{|y~s{}y~ly~}xy"
3709       "~}g{}y~|i{|y~|i{}y~o{}y~|sx~w{}y~w{}y~|s{|y~|ry~}r{|y~|w{}y~w{|y~}t{|y~}p{|y~|qy~}_y~|`{|y~|iy~|j{|y~}u{|y~|iy~"
3710       "|Jy~}h{|x~y|~|{|}k{|y~|fp~|ky~}{|y~f{}~}h{}~y}y~}|Sy}y{}~}qy}p{|~}w{|y~h{|~|x{}~<y}x{}~}x{|~}xy}T{|}y~}d{}~|e{|"
3711       "~}`{}~|R{}~}t{}~}n{}t~y{}~|5{|~}h{|~}vy~l{|~|xy}l{|~}u{|~}m{|~}ty~|k{}~|x{|~}e{|y~|hy~}xy~}jy~}xy~}jy~}xy~}jy~}"
3712       "xy~}jy~}xy~}jy~}xy~|n{}y~wy~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~r{|y~|{}y~vy~}r{|"
3713       "y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|q{|y~}w{|x~p{|y~}u{|~}y{|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y"
3714       "~r{|y~|q{}y~r{|y~|ly~}|y~}k{|y~|ux~n{}y~y{|y~|j{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y"
3715       "|x{|}y~q{|}y|x{|}v~|x{|y~}px~}|yy|}|mx~y|y{|x~lx~y|y{|x~lx~y|y{|x~lx~y|y{|x~i{|y~d{|y~d{|y~d{|y~gx~|x{|y~}m{}w~"
3716       "|y{|x~lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}i{|x~hx~|y{|}y~}m{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~"
3717       "}t{}~}o{|y~|u{|y~|n{}w~|y{|x~|o{|y~|u{|y~|={|y~vy~|w{}~|s{|y~o{}~|s{|y~{}y~}y{|y~}{}~|s{|y~t{|y~}{}~|s{|y~o{}~|"
3718       "ky~|iy~}u{}y~;k~}qw~u{~|R{}p~|k{}w~}mi~q{|o~|ny~|u{}y~n{|y~sy~|ky~}y~}j{|y~|w{}y~p{}~}  Hx}y~t}|My~}M{|y~x{|~}l"
3719       "{}y~y{|~m{}~}y{}~}y{|~}i{|w~I{|y~|b{}y~j{}~}|{~}{|y~|h{}~| p{}~|jy~}t{|y~|j{|y~|b{|y~}j{}u~}jy~|x{}y~kr~}ly~y}t"
3720       "~}f{}y~i{}t~|ly~}u{|x~|j{}y~d{}y~f{}v~}|nk~}n{|}w~}|e{}y~|p{|~}w{|u~y}~x{|~}r{|y~|x{}y~m{|y~|xy|}y~}o{|y~|e{|y~"
3721       "|q{}y~p{|y~r|m{|y~s|o{|y~|d{|y~q|y~}k{|y~|e{|y~|j{|v~}f{|y~|i{|y~|{}~}x{|~}yy~}s{|y~|yy~}wy~}r{|y~|p{|y~}q{|y~|"
3722       "ux~q{|y~|p{|y~}r{|y~|v{|y~}m{|v~}y|ey~}k{}y~r{|y~|o{}y~u{}y~qy~}x{|y~y{|y~wy~}n{}x~}g{|v~e{|y~}g{}~}c{|y~b{|y~|"
3723       " o{}~}m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|uy~}jy~|h{}y~u{}y~}n{}x~v{|y~|j{|y~d{|y~h{}y~x{}y~|g{|y~m{}x~v{}x~|vy~}r{}"
3724       "x~v{|y~|n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}x~h{|y~a{}y~g{}~}t{}~}ny~}u{}y~py~|x{|y~}~|x{|y~o{|y~}y{}y~|l{}~}u{}~"
3725       "}ex~g{}y~d{}~}d{}~}6y~y}y~|{}y~}y~}p{}y~vy~}y~m{|y~x{|}x~c{}~}m{|y~u{}y~l{}~}e{}x~v{|y~|n{|y~u{}~}i{}x~}j{}~}t{"
3726       "}~}q{}y~o{}y~q{}y~|{|y~y{|y~}my~|w{|y~|o{}y~x{|y~x{|y~|n{}y~ux~n{}y~u{}y~|iy~|j{|n~m{|y~|x{}y~f{}y~|j{|y~|i{}y~"
3727       "o{|y~|t{|y~}w{}y~w{|y~}s{|y~|ry~}qy~}w{}y~w{|y~|t{|y~|{r~y|y~}rx~|_y~|_{}y~|jy~|k{|x~s{}y~|jy~|Jx|h{}y~|y{~|h{|"
3728       "y~|f{|y~}|y{}y~|n{|u~{u~|j{}~}i{|~}y{|}y~}T{~|yy~p{|~p{|~}wx~i{|y~|y{}y~ok|X{~|x{}~}x{|~}x{|~?k~}m{}~}_y~|R{}~}"
3729       "t{}~}mt~y{}~|ix|J{|~}gy~|x{}~}l{|y~|y{}~}m{|~}uy~|u{|t{|~}u{}~}j{}~|xy~cy|h{|y~|x{}y~k{|y~|x{}y~k{|y~|x{}y~k{|y"
3730       "~|x{}y~k{|y~|x{}y~k{|y~|x{}y~ny~}wy~}r|t{|y~|c{|y~r|m{|y~r|m{|y~r|m{|y~r|i{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{"
3731       "|y~|yy~}wy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}p{|y~}y{|x~o{|y~|v{|y~wy~}s{}y~r{|y~|q{"
3732       "}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|l{|v~j{|y~|u{}y~|o{}y~y{|y~`{}~}d{}~}d{}~}d{}~}d{}~}d{}~}i{}x~u{|y~|r{}y~|f{}y~|"
3733       "uy~}n{}y~|uy~}n{}y~|uy~}n{}y~|uy~}j{|y~d{|y~d{|y~d{|y~h{}y~|v{|y~|n{}x~v{|y~|n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y"
3734       "~|n{}y~|v{}y~|n{}y~|v{}y~|ix|i{}y~|w{|x~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}u{}~}m{}x~ux~n{}~}u{}~}<{"
3735       "|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}u{|y~}y{|~}s{|~}o{|~}ky~|i{}y~uy~};k~}q{|{y~|w{}~R{|n~o{}o~}|r{|"
3736       "k~}qm~|oy~|u{|y~n{|y~sy~|l{|y~|}y~iy~}wy~}p{}~}  F{|y~|Fy~}M{}~}x{}~}l{|y~}y|~lu~xy~|xx}|q{|y~|x~ty|S{|y~a{}y~j"
3737       "{}|x{~}x{}|h{}~| py~j{|y~|t{|y~|j{|y~|bx~i{}v~}|k{}~}w{}y~k{}y|x{|x~}mw~}|y{|x~|gy~}h{}u~}l{}y~|v{}x~|jx|dx|i{|"
3738       "}w~}|kk~}l{|}v~}|i{}y~|o{}~|wy~}x{}x~wy~rx~w{|y~|n{|q~|n{|y~|e{|y~|q{}y~|q{|p~}n{|q~o{|y~|tu|q{|m~}k{|y~|e{|y~|"
3739       "j{|v~e{|y~|i{|y~|yy~xy~|yy~}s{|y~|y{}y~|xy~}r{|y~|oy~}q{|y~|w{|x~}q{|y~|oy~}r{|y~v|}y~}k{|}t~}gy~}k{}y~r{|y~|o{"
3740       "|y~}vy~}q{}y~x{|~}xy~|y{|y~|n{|x~e{}x~|ex~f{}~}by~|c{|y~| o{|y~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~|t{}y~jy~|hy~}u{|y~"
3741       "}n{}y~|u{}~}j{|y~d{|y~h{}y~y{}y~|f{|y~m{}y~|v{|x~u{}y~r{}y~|u{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~|h{|y~a{}y~"
3742       "g{}~}t{}~}n{}y~uy~}p{}~}x{}~}|~}x{}~}n{|y~y|y~}k{|y~uy~|f{}y~f{}~}d{}~}d{}y~7{}~x{|y~|~}x{}~py~}v{}x~}m{|y~{|v~"
3743       "|i{}y~}|{}~}my~|ty~}m{}~}e{}y~|u{}~}my~|vy~|j{|y~}y~j{}~}t{}~}qx~o{|y~|ry~}y{|y~x{}y~my~|w{|y~|o{}y~x{|y~x{|y~|"
3744       "ny~|u{|y~|oy~}ty~}iy~|j{|n~mx~w{|y~|g{|y~}j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}qx~w{}y~w{}y~|t{|y~|{r~|{y~"
3745       "}sx~|^y~|^{}y~|ky~|l{|x~q{}y~|ky~|5{|y~|x{~|h{|y~|f{}~}v{|~}mw}v~w}|j{}~}i{}~|w{|y~}U{~y{|y~p{|~ox~y}~}y~j{}y~|"
3746       "y{}y~nk~}Y{~w{}~}y{|}~|x{|~?k~}n{}y~v|ix}|}y~}Q{}~}t{}~}m{|u~y{}~|iy~}Ly|}~}y|i{|y~x}y~j{}y~|y{}y~n{|~}v{|~}v{|"
3747       "y~}u{|~}v{|y~y{}w~}wx|{|}y~x{}~|uy~}Wx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|l{}y~w{|y~|p{}y~|wo~t{|y~|c{|p"
3748       "~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|mq~u{}y~|s{|y~|y{}y~|xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{"
3749       "|y~|oy~}o{|y~}|x~n{|y~|vy~|wy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|k{}x~|j{|y~|u{|y~|o{}y~y{|y~|a{|y~d{"
3750       "|y~d{|y~d{|y~d{|y~d{|y~i{|y~|t{}~}ry~}ey~|t{}y~ny~|t{}y~ny~|t{}y~ny~|t{}y~j{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~|u{}"
3751       "~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}Ty~}w{|~}y~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{|y~uy~|m{}y~|u{"
3752       "|y~|o{|y~uy~|<{}~u|y~}w|{y~s{}~n|{y~s{}~|x{}w~}wy~s{}~|v{|y~}wy~s{}~|vy~}vy~ky~|i{|y~|w{|y~|4{|y~}i{}~}w{~}Rm~}"
3753       "qk~|r{}l~q{|m~|p{|y~sy~|o{|y~sy~|l{}y~{|y~|j{}y~x{|y~|p{}i~  V{|y~|F{}~}M{}~|x{}~|k{}v~}|m{|y}|x{}~}y{|v~}s{|y~"
3754       "|{|x~v{|y~S{}y~a{|y~e{~}jt|y~}u| w{|~}j{|y~|t{|y~|j{|y~|cx~|hw|}y~|m{|y~v{}y~cx~|nx~}ux~h{|y~|j{|y~}|{y|x~m{|x~"
3755       "|y{|}w~|={}w~}|E{|}v~|l{|y~|ny~w{}~}v{}y~wy~s{|y~|vy~}n{|q~}|o{|y~|e{|y~|q{}y~p{|p~}n{|q~o{|y~|u{|u~}r{|m~}k{|y"
3756       "~|e{|y~|j{|y~}x~f{|y~|i{|y~|y{}~|{|y~xy~}s{|y~|xy~}xy~}r{|y~|oy~}q{|q~}p{|y~|oy~}r{|r~}h{|y}u~|iy~}k{}y~r{|y~|n"
3757       "x~w{|y~|q{}y~x{}~|x{}~|y{|y~|nw~|ey~}e{}y~|f{}~}b{}~}c{|y~| v{|y}t~m{}y~sy~|o{|y~|f{|y~|ty~}o{|y~|t{|y~jy~|i{|y"
3758       "~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{x~|e{|y~m{}y~ty~}u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~|ty~}k{}y~g{|y~}b{}y~g"
3759       "{}~}t{}~}n{|y~|w{|y~o{|y~x{}~y|y~xy~}m{}w~}iy~|w{}y~f{}y~|g{|y~|d{}~}d{|y~}jy}y~}|t{|}X{~}w{|y~}v{~|r{|y~|v{|x~"
3760       "|m{|y~{|v~}|ku~|y~}n{|y~|t{}y~m{|y~}f{}y~t{}~}m{}y~w{}y~i{}y~{y~}k{}~}t{}~}qy~}ny~}s{|y~|y{|y~x{|y~my~|w{|y~|o{"
3761       "}y~x{|y~x{|y~|o{|y~sy~|p{|y~|t{}y~iy~|j{|y~s|}y~n{|y~}vy~}gx~|j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}q{}y~|x"
3762       "{}y~x{|y~}s{|y~|{r|yy~}tx~}l|ly~|mk|x~|ly~|m{|x~o|x~|ly~|5{}y~w{~|j{}r~}ky~|uy~i{|x~}e{}~}i{}~}v{|y~V{|~y{|y~o{"
3763       "~|o{}x~|{y}k{}y~|y{}~}mk~}Z{|~w{}u~|v{~|@t|y~}t|n{}t~i{|}x~y}P{}~}t{}~}k{|}x~y{}~|iy~}Lt~|i{|}x~}h{|y~|y{}y~|rt"
3764       "~|yy~ux~}wt~|y{}~|{|~}y|}y~x{}v~}|y{|y~u{}y~}m{|y}i{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}ly~|vy~}py~"
3765       "}vo~t{|y~|c{|p~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|m{}s~}u{}y~|s{|y~|xy~}xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|o"
3766       "y~}t{|y~|oy~}t{|y~|oy~}n{|v~m{|y~|wy~|vy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|u{}y~|o{}y~xx~|"
3767       "i{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~p{|y}t~s{|y~s{|y~|f{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~j{|y~d{"
3768       "|y~d{|y~d{|y~i{|y~|t{}~}n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~pk~}q{|y~|w{~}{}y~n{}~"
3769       "}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}my~|w{}y~l{}y~sy~|ny~|w{}y~;{}~|}p~{y~s{}~|}p~{y~s{}~|w{}x~vy~s{}~|w{|y~}vy"
3770       "~s{}~|vy~}vy~ky~|h{}~}w{}y~3y~}h{|y~x{|~|S{|l~r{}k~}qm~|p{}o~}o{|y~sy~|o{|y~sy~|ly~}yy~}j{|y~|y{|y~o{}i~  X{|y}"
3771       "y~u}K{}~}My~|xy~i{|}u~}i{|y~y{|y~|{|y~|t{}~}x{|x~w{|y~S{}y~a{|y~|f{~}jk~} x{}~}iy~}t{|y~|j{|y~|d{}y~|b{}y~|ny~|"
3772       "v{}y~c{|y~}nx~|u{}y~|ix~j{|y~|v{|y~}m{|t~y}y~|=x~}?{|x~}l{}y~m{~}wy~|v{|y~w{}~s{}y~u{}y~n{|y~|v{|}y~|p{|y~|e{|y"
3773       "~|q{}y~p{|y~|e{|y~|h{|y~|u{|u~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|}x~g{|y~|i{|y~|y{|y~{}~}xy~}s{|y~|x{|y~|yy~}r{|y~|p{"
3774       "|y~}q{|s~}|o{|y~|p{|y~}r{|q~|ey|w~iy~}k{}y~r{|y~|n{|y~|x{}y~p{|y~|yy~|x{}~}y{}y~n{}v~ey~}f{}y~|e{}~}b{|~}c{|y~|"
3775       " w{}q~m{}y~sy~}o{|y~e{|y~s{}~}o{|n~jy~|i{|y~s{}~}n{}y~t{}~}j{|y~d{|y~h{}v~c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}"
3776       "y~n{}y~sy~}p{|y~s{}~}k{}y~f{}w~}|f{}y~g{}~}t{}~}m{}~}w{}~}o{|y~|yy~yy~xy~|lw~h{}y~wy~}g{}y~|i{}w~}c{}~}c{|w~}o{"
3777       "|s~}w|}~}X{~|vy~|vy}r{|y~tx~l{|y~w{|}x~|m{}y~x{}x~|n{|y~s{}y~l{|}v~j{}y~t{}~}m{|y~|xy~}j{|y~|{}y~k{}~}t{}~}qy~}"
3778       "vy~|vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{|y~sy~}p{|y~s{}y~iy~|j{|y~s{}y~n{}y~u{}y~h{}y~|i{|y~|i{}y~|"
3779       "p{}y~s{|y~}w{}y~w{|y~}s{|y~|ry~}px~x{}y~x{}y~|s{|y~|p{|y~}u{}h~ly~|m{}h~ly~|mg~ly~|J{}~}i{}y~w{~|j{}r~}ky~ty~|i"
3780       "x~d{}~}i{}y~|vy~|W{|~y{|y~o{~|X{}y~xy~}^{|~}Z{|~w{}~}|}~}u{~|9{}~| v{}~}t{}~}hy~y{}~|iy~} s{|y~}y{}y~|st|y{}~|v"
3781       "{}~|~}wt|y{|~}sy~|wx|v{}~|vy}|~}m{|y~i{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~q{|y~|vy~}k{|y"
3782       "~|c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{|y~|x{|y~|yy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|"
3783       "y~}t{|y~|p{|y~}t{|y~|p{|y~}m{}x~|m{|y~|x{}~|v{|y~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|ux~n{}y"
3784       "~x{|x~}k{}q~l{}q~l{}q~l{}q~l{}q~l{}q~q{}f~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y"
3785       "~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~pk~}q{|y~wy~y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{}y~wy~"
3786       "}l{}y~sy~}n{}y~wy~};{}~|}q~}{y~s{}~|}q~}{y~s{}~|x{|w~}wy~s{}~|x{|y~}uy~s{}~|vy~}vy~ky~g{|y~|xy~|4{}y~fy~|yy}R{|"
3787       "l~ri~po~|n{}p~n{|y~sy~|o{|y~sy~|m{|y~|y{}y~iy~}y{}~}o{}~}  H{}r~}K{}~}N{|y~x{|y~f{|~}w~j{}~|y{}~}x{|y~ty~|w{|x~"
3788       "x{}~}S{}y~a{|y~|f{|ik~}T{}u~|Ly~|iy~}t{|y~|j{|y~|e{}y~|`y~}o{}~}u{}y~by~}nx~t{|y~|j{|y~}jy~}t{}y~k{}x~}|{}y~<v~"
3789       "}|hk|g{|}w~}l{}~}n{|~}wy~ty~wy~sy~}t|y~|o{|y~|t{}y~p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}py~}r{|y~|ry~}k{|y~|e{|y~|j"
3790       "{|y~|{}y~}h{|y~|i{|y~|xy~|y~|xy~}s{|y~|wy~}yy~}r{|y~}p{|y~|q{|y~w|k{|y~}p{|y~|r{|y~|w{}x~bx~|jy~}k{}y~r{|y~|my~"
3791       "}xy~}oy~}{|y~w{|y~yy~}o{|y~}{y~}fy~}g{|y~}d{}~}ay~c{|y~| x{}y~}|w{|y~m{}y~sy~}o{|y~e{}y~s{}~}o{|n~jy~|i{}y~s{}~"
3792       "}n{}y~t{}~}j{|y~d{|y~h{}w~}c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{}y~s{}y~n{}y~sy~}p{}y~s{}~}k{}y~e{|u~}h{}y~g{}~}t{}~}"
3793       "m{|y~wy~|ny~}{|~}y{}~|{|y~k{}y~}h{|y~|y{|y~|h{|y~}h{}w~|c{}~}c{|}x~}oy~}x|}r~|X{~|v{}~|v{~}r{}y~ty~}l{|y~t{}y~n"
3794       "{|y~|wx~|n{|y~s{}y~l{|u~j{}y~t{}~}ly~}y{|y~|j{}y~y{|y~|l{}~}t{}~}r{|y~}vy~|vy~}s{}y~x{|y~x{|y~|ny~|w{|y~|o{}y~x"
3795       "{|y~x{|y~|o{}y~sy~}p{|y~s{}y~iy~|j{|y~|t{}y~ny~}u{|y~|j{|y~}h{|y~|i{|y~}px~ry~}w{}y~w{}y~|s{|y~|ry~}p{|x~|{}y~y"
3796       "{}y~}r{|y~}p{|y~|u{|h~ly~|m{}h~ly~|m{}h~ly~|J{}~}i{}y~w{~|h{|y~|fy~|uy~mv}x~v}|Tx~|wy~U{~|yy~|q{|~or}ly~}xy~|^{"
3797       "|~}Y{~|x{}~}y{}~}w{|~8{}~| v{}~}t{}~}hy~y{}~| yr}h{}~}y{|y~|k{|~}v{|~y|~}ny~r{}~|p{|~}v{|~{|~}m{|y~iy~}t|y~|ny~"
3798       "}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|r{}y~u|y~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~"
3799       "|q{}y~r{|y~|wy~}yy~}r{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|n{|w~}m{|y~}y{}~}u{|y~|s{}y~r{|"
3800       "y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|w{|x~}n{}y~v{}x~|n{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~"
3801       "m{}y~}|w{|y~m{}y~}|w{|y~r{}y~}|w{|n~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{}y~s{}y~"
3802       "o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~pk|p{}y~x{}~|y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{|y~|y{|y~|l"
3803       "{}y~sy~}n{|y~|y{|y~|;{|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}y{}y~}u{|~}s{|~}vy|v{|~}ky~fy~}y{}y~9k~}n{"
3804       "}y~y{~|R{}l~ri~p{|q~}lq~m{|y~sy~|o{|y~sy~|m{}y~x{|y~|j{}y~yy~}o{|y~  Ey~}E{|Qj~j{|~y{|y~}l{|~}xy~|wy~u{|y~|v{|x"
3805       "~{|y~R{}y~a{|y~K{}~|M{}u~|M{|y~hy~}t{}y~i{|y~|f{}y~|_{}y~o{}n~}ey~}n{}y~t{|y~|j{}y~|jy~}t{|y~|e{}y~;{|}v~}jk~}k"
3806       "{|}v~}j{}~}n{|~}wy~ty~wy~t{|o~}o{|y~|t{|y~|px~e{|y~|qy~}p{|y~|e{|y~|gx~py~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{"
3807       "|y~|i{|y~|x{}w~wy~}s{|y~|w{|y~|{y~}qx~p{}y~|q{|y~|gx~p{}y~|r{|y~|v{|y~}c{|y~}jy~}k{}y~r{|y~|m{}y~y{}y~|o{}y~{|~"
3808       "}vy~{|y~|ox~y{|y~|gy~}h{|x~c{}~}a{}~|d{|y~| xy~|u{|y~m{}y~sy~}o{|y~e{|y~s{}~}o{|y~_y~|i{|y~s{}~}n{}y~t{}~}j{|y~"
3809       "d{|y~h{}y~|y~}d{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}y~n{}y~sy~}p{|y~s{}~}k{}y~b{|}w~i{}y~g{}~}t{}~}ly~|y{}y~m{}~"
3810       "}{}~}y{|~}{}~}l{|w~|h{}~}y{}y~h{|y~}d{}y~|d{}~}d{|y~}|m{}t{|}x~}|V{~}w{|x~v{~|r{|y~ty~|l{|y~t{|y~|o{}y~v{}y~m{|"
3811       "y~s{}y~m{}y~}f{}y~t{}~}l{|y~y{}~}iy~|xy~}l{}~}t{}~}qy~}vy~}vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{}y~s"
3812       "y~}p{|y~s{}y~iy~|j{|y~|ty~}o{|y~|ty~}k{|x~g{|y~|hx~q{|y~}r{}y~|x{}y~x{|x~r{|y~|ry~}o{}x~}x~}x~}px~p{}y~|t{}y~}]"
3813       "y~|^{|x~oy|yy~|y{|o{}y~|q{|x~oy|yy~|y{|M{}~}i{}y~w{~|h{|y~|f{}~}v{}~}n{|n~|S{}x~|{}~}Uy}y{}~}qy}p{|r~ky~}y{|y~}"
3814       "_{|~}Yy}x{}~}xy~|xy}8{}~| v{}~}t{}~}hy~y{}~| {{|r~iy~}y{|y~}jy~|w{|~|{|~}o{}~|s{|y~oy~v{|~|{|~}m{|y~j{|o~}o{|o~"
3815       "}o{|o~}o{|o~}o{|o~}o{|o~}s{|p~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|w{|y~|{y~}qx~p"
3816       "{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|o{|x~|y~}my~}{}~}t{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~"
3817       "}i{|q~}m{}y~u{|x~|oy~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~ry~|u{|y~h{|y~e{|y~d{|y~d{|y~d{|y~_{|y~"
3818       "d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~U{|y~y{}~|x{}y~n{}~}t{}~}n"
3819       "{}~}t{}~}n{}~}t{}~}n{}~}t{}~}l{}~}y{}y~k{}y~sy~}m{}~}y{}y~:{|y~vy~|w{}~|s{|y~o{}~|s{|y~{|y~}y{|y~}{}~|s{|y~{|y~"
3820       "}t{}~|s{|y~o{}~|ky~f{}y~yy~}9k~}n{|y~y|~Q{|u~|y}u~r{}t~y}s~o{|s~}k{|s~|m{|y~sy~|ny~sy~ly~}wy~}j{|y~y|y~|ny~|  F"
3821       "x~ uj~j{|~x{}y~ly~wy~|wy~u{|y~|u{|x~}~}R{|y~a{}y~K{}~| r{}~}h{}y~t{}y~i{|y~|g{}y~|^{}y~o{}n~}ey~}n{}y~t{|y~|jy~"
3822       "}iy~}t{|y~|ey~}8{|}w~}|mk~}n{|}v~}|hx}m{~}wy~|v{|y~x{|~}t{}n~|p{|y~|t{|y~}p{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|q"
3823       "y~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|x{|x~}wy~}s{|y~|vy~}{y~}q{}y~|qx~p{|y~|g{}y~|qx~q{|y~|u{}y~|d{"
3824       "|y~}jy~}k{|y~|s{}y~|m{|y~|{y~}n{}y~{}~}v{}~y|y~|p{}y~|x{}y~gy~}hx~|c{}~}a{|~}d{|y~| y{|y~t{}y~m{}y~sy~|o{|y~|f{"
3825       "|y~|ty~}o{|y~|`y~|i{|y~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{|x~e{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~"
3826       "|ty~}k{}y~_{|y~}j{}y~g{}~}ty~}l{}y~yy~}m{|y~{y~|y{|y~{y~}m{|y~y}y~h{|y~yy~|hx~by~}d{}~}d{}y~7{}~|y{|~}|~}x{}~q{"
3827       "|y~|v{|y~}l{|y~t{|y~|o{}~}v{}~}m{|y~|t{}y~my~|e{}y~t{}~}ky~|{y~|j{}y~w{}y~l{}~}ty~}qy~}vy~}vy~}s{|y~|y{|y~x{}y~"
3828       "my~|w{|y~|o{|y~x{|y~x{|y~|o{}y~sy~|p{|y~|t{}~}iy~|iy~}ty~|o{}y~s{}y~|lx~|g{|y~|h{|y~}rx~px~|y{}y~y{|x~|r{|y~|ry"
3829       "~}n{|r~}o{}y~|qx~r{}y~}^y~|_{|x~o{|y~|{y~|{}~}o{}y~|s{|x~o{|y~|{y~|{}~}Ny~}i{}y~w{~|h{|y~|f{|y~}|{|}y~|h{}y~L{|"
3830       "v~}T{|~xy~}|yx|x{~|U{}y~y{|y~}`{|~}Y{|~x{}~}x{|y~x{~|8{}~| v{}~}ty~}hy~y{}~| `{|y~}y{|y~|j{}~}v{~}y{|~}p{|y~ry~"
3831       "|p{}~|v{~}y{|~}mx~j{}n~|p{}n~|p{}n~|p{}n~|p{}n~|p{}n~s{}p~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y"
3832       "~|k{|y~|r{|y~}r{|y~|vy~}{y~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~o{|x~y{|y~}n{}y~}~}sx~r{|y~|s{}y~|q{|y"
3833       "~|s{}y~|q{|y~|s{}y~|q{|y~|s{}y~|jy~}i{|s~}|l{}y~sy~}p{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y"
3834       "~s{|y~t{}y~|i{|y~|f{|y~|e{|y~|e{|y~|e{|y~|`{|y~d{|y~d{|y~d{|y~i{|y~|t{}y~n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|"
3835       "t{}y~o{|y~|t{}y~o{|y~|t{}y~ix|j{|y~|}~}w{}y~n{}~}ty~}n{}~}ty~}n{}~}ty~}n{}~}ty~}l{|y~yy~|k{}y~sy~|m{|y~yy~|9{}~"
3836       "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}{~}t{|y~q{}~}q{|y~k{|y~f{|y~y|y~|9x|}y~}q|m{}~x}P{}w~}{}{v~|r{|t~y|}u~"
3837       "|n{}u~}i{|u~}l{|y~sy~|ny~|u{|y~ly~|w{}y~iy~y}y~m{|y~|  G{|y~| ry~|xy~|f{|~x{}y~m{}~|wy~|wy~ty~}t{|w~Q{|y~|b{}y~"
3838       "K{}~| ry~|h{|y~|uy~}i{|y~|h{}y~|]x~ns|x~y|e{|y~}n{|y~|u{}y~|k{|y~|iy~}t{}y~e{}y~4{|}w~}|U{|}v~}|Ty~w{}~}v{}y~xy"
3839       "~sy~}ry~}p{|y~|t{|y~}p{|x~p{|r{|y~|s{|x~o{|y~|e{|y~|g{|x~qy~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~|wx~|"
3840       "wy~}s{|y~|v{|y~|y~}q{|x~r{}y~|p{|y~|g{|y~}r{}y~|q{|y~|u{|y~}d{|y~}jy~}k{|y~}sx~ky~}|y~|n{|y~|y~|v{}~y}y~p{|y~}w"
3841       "{|y~}hy~}i{}y~|b{}~}a{|y~d{|y~| y{|y~tx~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~}`y~|hy~}u{|y~}n{}y~t{}~}j{|y~d{|y~h{}y~y{"
3842       "|x~f{|y~m{}y~ty~|u{|y~r{}y~t{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~^{}~}j{|y~g{}y~u{|y~}l{|y~y|y~|ly~|y~wy~|y~|"
3843       "mx~yy~}hy~y|y~hx~a{}y~d{}~}d{}~}6u~y{}y~}y~|py~|v{}y~}l{|y~t{|y~|o{}~}vy~|ly~}ty~}n{|y~|e{}y~t{}~}k{}~y}y~iy~}v"
3844       "y~|m{}y~ty~}qx~w{|x~w{|y~|ry~}y{|y~x{}~}my~|w{|y~|o{|y~x{|y~x{|y~n{}y~|u{|y~|oy~}ty~}iy~|i{}y~u{|y~ny~}s{|y~}m{"
3845       "}y~|f{|y~|g{}y~|t{}y~|p{|w~y}y~y}x~}q{|y~|ry~}ly}x~y}|n{|x~r{}y~|q{}y~}_y~|`{|x~mx~|y~|}y~|n{}y~|u{|x~mx~|y~|}y"
3846       "~|Ny~}i{|y~|x{~|h{|y~|fp~|i{}y~d{}~}d{|x~|S{~}x{}u~|y{}~S{}y~xy~}a{|~}X{~}y{|~|w{}~|{}~7y| u{}y~ty~}hy~y{}~| a{"
3847       "|y~}y{|y~|j{|y~v{}~x{|~}p{}~|s{}~|p{|y~v{}~x{|~}n{}y~|jy~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}ty~}ty~}j"
3848       "{|x~p{|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|v{|y~|y~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~"
3849       "|r{|x~r{}y~|r{|x~r{}y~|p{|x~w{|y~}o{|w~s{}y~|r{|y~}sx~p{|y~}sx~p{|y~}sx~p{|y~}sx~iy~}i{|y~w|h{}y~s{}~}p{|y~tx~n"
3850       "{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~s{|y~tx~}hy~}ey~}dy~}dy~}dy~}`{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~t{}~}ny~}t"
3851       "y~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}j{|x~iy~}~}vy~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}ky~y|y~j{}y~|u{|y"
3852       "~|ly~y|y~7y~}xy~|y{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|k{|y~ey~y}y~6{|y~}b{|x~|O{}y~}y{}{|}y~|p{|w~}{|}"
3853       "{}w~}l{}v~g{}w~}k{|y~sy~|n{}y~uy~}m{|y~v{|y~|j{}w~}l{}y~|  Gx~|u{}|Px|O{|y~x{|y~j{|w{|~x{}~}n{|y~v{}~|x{|y~t{}y"
3854       "~}t{}x~Py~|by~}K{}~|dx|Jx|f{|y~fx~v{}y~h{|y~|i{}y~|f{|s{}y~}f{}y~l{|t{|x~l{}y~ux~j{}y~h{}y~|v{|x~f{|y~}hx|dx|a{"
3855       "}v~}Xv~}|bx|m{}~|x{|y~}x{|x~{|y~|t{|y~|r{}y~p{|y~|t{}y~|o{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}ry~}r{|y~|ry~}"
3856       "k{|y~|e{|y~|j{|y~|v{}y~}l{|y~|i{|y~|oy~}s{|y~|uv~}p{}y~}t{}y~}o{|y~|f{}y~}t{}y~}p{|y~|t{}y~|o{}r{}y~|jy~}j{}y~|"
3857       "u{|y~}k{}y~}y~ly~}y~u{|w~}px~u{|y~|iy~}j{}y~}a{}~}`y~|e{|y~| y{|y~|v{}x~m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|ay~|h{}y"
3858       "~u{}y~}n{}y~t{}~}j{|y~d{|y~h{}y~x{|x~g{|y~m{}y~ty~|u{|y~r{}y~t{}~}n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}y~^y~}j{|y~"
3859       "g{|y~|v{}y~}ky~y}y~kw~}w{}w~m{}y~|y{|y~}i{}~}y~}i{}y~|a{}y~d{}~}d{}~}5{}y~}w{|y~}|o{}y~w{|w~l{|y~}u{}y~n{}~}w{}"
3860       "y~k{}y~|v{}y~|my~|e{}y~t{}~}k{|w~}j{}y~u{}~}m{}y~|v{}y~}q{}y~|x{}~}~|x{}y~q{}y~|{|y~y{|y~|my~|w{|y~|ny~}y{|y~x{"
3861       "}y~n{}x~ux~n{}y~|v{}y~|iy~|i{|y~}vy~}o{|y~|rx~n{|y~}e{|y~|fx~|v{}y~}n{|}q~|p{|y~|ry~}j{}y~j{}y~}t{}y~}o{}~}_y~|"
3862       "`{|y~ks~|l{}~|u{|y~ks~|My~}hx~x{~|h{|y~|gx~|}x~}|}y~|j{}y~d{}~}c{|x~S{|~}xy|}y|x{}~|R{}~|x{}~a{|}|X{|~}p{}~| /{"
3863       "}y~|v{}y~}hy~y{}~| a{|~|x{}~|i{}~|vr~|s{|~}s{}~|o{}~|vr~|q{}y~}j{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y"
3864       "~|r{}y~q{|y~|r{}y~u{|y~|ty~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|y~|uv~}p{"
3865       "}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{|x~u{|y~}o{}y~}t{}y~}p{}y~|u{|y~}o{}y~|u{|y~}o{}y~|"
3866       "u{|y~}o{}y~|u{|y~}iy~}i{|y~|e{}y~sy~}p{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~s{|y~|v{"
3867       "}w~|i{}y~|f{}y~|e{}y~|e{}y~|e{}y~|a{|y~d{|y~d{|y~d{|y~h{}y~|v{}y~|n{}y~t{}~}n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y~"
3868       "|n{}y~|v{}y~|n{}y~|v{}y~|j{|x~i{}y~}v{}y~|n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}k{}~}y~}j{}x~ux~k{}~}"
3869       "y~}7{|y~}|{y|{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|j{|y~e{|w~|6y~}`x~I{|~hx|y{|}yw|jw~|"
3870       "f{}x~j{|y~sy~|mx~w|x~l{}~}uy~}j{}w~|k{}y~}|  I{|x~}|{y|y~|Py~}O{|~}x{|~}j{}~|y{|~{|}y~|n{}~|v{|y~x{}~}sx~}|y{|}"
3871       "u~Q{}~}by~|K{}~|d{}y~Jy~}f{}~}f{|y~}|{|}y~}kw|y~w|m{}y~}s|my~}w|}w~e{}y~ly~}w|}x~}l{|x~|y{|x~|jy~}h{|x~}|{y|x~|"
3872       "m{~}w|}y~}g{}y~d{}y~_{|}y~}Xy~}|_y~}m{|~}w{|u~y}w~|sx~q{|y~|q{|y~t|}y~}m{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~|e{}"
3873       "x~}v|}x~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|u{}y~}m{|y~q|r{|y~|oy~}s{|y~|u{|w~}o{}x~}|{y|}x~n{|y~|e{}x~}|{y|}x~o{|y~|t"
3874       "{|y~}oy~}x|{y|x~}iy~}j{|x~}w|}x~j{|w~}l{}x~}tw~|q{}y~|t{}y~iy~}k{|x~o|l{}~}`{}~}e{|y~| xx~|y{|}w~m{}w~|y{|x~|lx"
3875       "~}|yy|}|mx~|y{|}x~}m{}y~}|x{|}~}jy~|h{|x~|y{|}x~}n{}y~t{}~}j{|y~d{|y~h{}y~w{|x~h{|y~m{}y~ty~|u{|y~r{}y~t{}~}mx~"
3876       "|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}y~g{}~}|x{|}y~|j{|y~}gx~|y{|}x~}k{}w~}k{}x~}w{|x~}n{|y~|w{}y~|j{|w~|j{}y~|`{}"
3877       "y~d{}~}d{}~} w{|y~}|{|y~}y~}m{|x~}|y{|}y~}n{|~}x{|y~|jx~|y{|}y~}l{}y~}|x{|y}m{}y~t{}~}jw~|jy~}u{|y~|n{}x~}|{|}w"
3878       "~y|s{|x~|{|y~|~}|{}y~}px~y|y~|}y~}ly~|vy~}n{}y~|{|y~y{}y~|n{}w~|y{|x~|mx~|y{|}y~}h{}y~|i{}y~}y{|x~nx~q|}y~|ox~r"
3879       "|m{|y~|iw|x~}x{}y~}w|o{|y}x~y}|n{|y~|ry~}j{}y~i{}x~}|{y|}x~m{|^y~|_{|iu~|j{|s{|iu~|Ly~}h{|y~}|{~|{|}mx|y~}t|o{|"
3880       "y~r{}~}j{}y~d{}~}b{|y~|S{|~}r{}~|Py|w{}:{|~}r{}~|=k|!{}x~}|{|}w~y|jy~y{}~| ay|w{}h{|~}uv|}~}|ry~s{}~|o{|~}uv|}~"
3881       "}|q{|y~}ix~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|r{}y~q{|y~|vx~sy~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~"
3882       "q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}p{|y~|u{|w~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y"
3883       "|}x~ox~s{|y~}pv~}|{y|}x~o{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~hy~}i{|y~|e{}y~{x|y{|}y~|ox~|y{|}w~mx~|y{|}"
3884       "w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~rx~|y{|}y~|}y~}|x{|}~}qx~}|yy|}|m{}y~}|x{|}~}m{}y~}|x{|}~}m{}y~}|x{|}"
3885       "~}m{}y~}|x{|}~}j{|y~d{|y~d{|y~d{|y~gx~|y{|}y~}m{}y~t{}~}mx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}"
3886       "i{|x~i{|x~|y{|}y~}lx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}k{|w~|j{}w~|y{|x~|k{|w~|6{|q~|m{|q~|m{|q~|m{|q~|m"
3887       "{|q~|i{|y~dw~5{}~_{}~}I{}~c{}~d{|y~|dy~|j{|y~sy~|m{|s~|ly~}u{}~}j{|w~i{}m~  S{|s~}Oy~}O{}~|x{}~|j{}q~}n{|~}t{}y"
3888       "~}x~q{}s~}{|y~}R{|y~c{|y~J{}~|dx~Jy~}fy~|e{}t~}k{}q~}no~|nq~}d{}y~lq~|j{|s~|j{|y~|g{|r~|ls~}f{}y~dx~\\y|X{}|]y~"
3889       "}ly~|w{|}y~}|{}~}|r{|y~}py~}q{|p~}k{|q~}q{|p~}|m{|o~|o{|y~|d{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|t{}y~}n{|o~r{|y~|o"
3890       "y~}s{|y~|t{}x~}n{}r~}m{|y~|d{}r~}n{|y~|s{}y~|pp~}hy~}i{|r~}hw~|l{}x~}tw~|r{|y~}s{|y~}jy~}k{}l~|m{}~}`{|y~e{|y~|"
3891       " x{|t~}|y~m{}y~|t~|j{}s~|m{|t~|}~}l{}r~}jy~|g{|t~|}~}n{}y~t{}~}j{|y~d{|y~h{}y~v{|x~i{|y~m{}y~ty~|u{|y~r{}y~t{}~"
3892       "}m{|s~}l{}y~|t~|l{|t~|}~}k{}y~g{}r~}h{}u~k{|t~|}~}k{|w~|k{|x~|w{|x~|ny~}u{}y~iw~io~h{}y~d{}~}d{}~} v{}t~{}x~}o{"
3893       "|q~}ly~}y|y~}i{|s~}j{}s~}m{}y~t{}~}j{}x~j{}y~sy~}n{}~}t~}x~}r{|u~|{t~o{|q~ky~|vw~}ot~}x~}m{}y~|t~|l{|s~}g{|v~j{"
3894       "}t~|o{|k~}p{|o~|n{|y~|is~y{|t~}l{}y~k{|y~|ry~}j{}y~h{}r~}Ny~|Kw~|Lw~|Ky~}g{|r~n{|o~}n{|p{|i{}y~d{}~}ay~|R{|y~}y"
3895       "|{y|}y~| a{|y~}y|{y|}y~|<k~}\"{}~}t~}x~}jy~y{}~| Gy~o{|~}r{}~|ty~|ny~o{|~}q{|y~}i{|y~}py~}s{|y~}py~}s{|y~}py~}s"
3896       "{|y~}py~}s{|y~}py~}s{|y~}py~}w{|y~|so~}q{|q~}o{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|y~|t{}x~}"
3897       "n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}n{|~q{|~p{}~y|r~}m{|r~}l{|r~}l{|r~}l{|r~}gy~}i{|y~|e{}y~{}t~}n{|t~}|y~m{|t~}|y~m{"
3898       "|t~}|y~m{|t~}|y~m{|t~}|y~m{|t~}|y~r{|s~|y{}r~|p{}s~|l{}r~}l{}r~}l{}r~}l{}r~}j{|y~d{|y~d{|y~d{|y~fs~}l{}y~t{}~}m"
3899       "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}Rq~}k{|t~|}~}m{|t~|}~}m{|t~|}~}m{|t~|}~}jw~i{}y~|t~|iw~3{|}w~}|i{|}w~}|i{|}w~}|i{|"
3900       "}w~}|i{|}w~}|g{|~}d{}y~} r{|~|Iy~|dy~|d{|}cy|i{|y}sy}|k{}w~}k{|y~|u{|y~ix~}gy}p~  Q{|}x~}|Ny~}Oy~wy~h{|y}v~}|my"
3901       "~r{|x~}o{|}w~}|x{}y~}Ry~|d{}~}J{}~|dy~}Jy~}g{|y~c{|}x~}|j{}q~}no~|m{|}w~y}|c{}y~l{|y}w~y}f{}w~}hx~dy}x~y}|k{|}w"
3902       "~}|e{}y~dy~} ry~}l{|y~c{}y~|p{}y~q{|r~}|h{|}w~}|o{|s~y}|k{|o~|o{|y~|b{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|s{}y~}"
3903       "o{|o~r{|y~|oy~}s{|y~|t{|x~}l{|}x~y}|l{|y~|b{|}v~}m{|y~|ry~}o{|y}w~y}|gy~}g{|}w~}|g{|x~k{|x~|t{}x~qx~q{}y~|ky~}k"
3904       "{}l~|m{}~}_y~|f{|y~| v{}x~}|{|y~m{}y~{|}x~}|h{|}x~y}|j{}x~}|{}~}k{|}w~y}|iy~|e{}x~}|{}~}n{}y~t{}~}j{|y~d{|y~h{}"
3905       "y~u{|x~j{|y~m{}y~ty~|u{|y~r{}y~t{}~}k{|}x~}|k{}y~{|}x~}|i{}x~}|{}~}k{}y~f{|y}w~}|fy}w~j{|}x~}y{}~}jx~}ix~ux~|o{"
3906       "}y~t{|y~}j{|y~}io~h{}y~d{}~}d{}~} u{|}x~}|y{}y~}o{|y~|}w~}|k{|v~}f{|}x~}|h{|}w~y}|m{}y~t{}~}j{|y~|jy~}s{}y~n{}~"
3907       "}{}x~}y{}~}|q{|}y~}|x{}x~}l{}v~}|jy~|v{|}y~}n{}s~|l{}y~{|}x~}|i{|}x~}|e{|}x~i{|}x~}m{}j~p{|o~|n{|y~|is~y{|t~}l{"
3908       "}y~k{|y~|ry~}j{}y~f{|}x~y}|My~|Jy~|Jy~|Jy~}f{|}u~}n{|o~}O{}y~d{}~}h{}|w{}y~O{|}v~}| ]{|}v~}|:k~}\"{}~}{}x~}y{}~"
3909       "}|jy~y{}~| H{}~|o{|~|s{|~}t{|t~|t{}~|o{|~|q{}y~h{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}"
3910       "y~w{}y~ro~}o{|}w~}|m{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|y~|t{|x~}l{|}x~y}|i{|}x~y}|i{|}x~"
3911       "y}|i{|}x~y}|i{|}x~y}|U{|~}x{|}x~y}|j{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|fy~}i{|y~|e{}y~{|}w~}|k{}x~}|{|y~k{}x~}|{|y~"
3912       "k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~p{}x~}|v{|}w~y}|n{|}x~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|i{|y~d"
3913       "{|y~d{|y~d{|y~e{|}x~}|k{}y~t{}~}k{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|R{}~|{|}x~}|i{|}x~}y{}~}l{|}x~}y{}~}l{|"
3914       "}x~}y{}~}l{|}x~}y{}~}j{|y~}i{}y~{|}x~}|h{|y~}  e{|~} V{|      .{|~ p{}~}dy~|1{|y~2{}~}   U{|y~ ^{}~}  ){|y~|  {"
3915       "}y~}  6{}~}_{}~}f{|y~|  -y~}6{|y~ A{}y~Z{}~}  j{|y~I{}y~d{}~}d{}~} \\{|y~a{|}y|*{}~}j{|y~|O{}~}F{|y~K{|}y~y|j{}"
3916       "y~     Ry~}c{|~| r{}~}h{}t~|K{| U{| '{}~}^y~y{}~|O{|~| wy|cy}|st|sy|ay~}  ^{|~|    9{|    Y{|~|    8y| Q{|y~h{}"
3917       "y~`{|y~  d{|~}        c{|~ oy~e{}~}0{}~}2y~|   U{}~} ]{}y~|r{|y}  7{}y~  y{}y~|  7{}~}_{|y~f{|y~|  .{|y~|6{|y~ "
3918       "A{}y~Z{}~}  j{}~}I{|y~d{}~}dy~} \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y~     Ry~}b{~| r{}~}h{|y}x~}|   ){}~}^y~y{"
3919       "}~|N{}~ 'y~}  ]y~         p{~}      g{}~}h{}y~`{}~}  h{|}|{}~}        c{|~ o{}~}fy~/{}~    c{}~ [{}y~}|v{|}y~} "
3920       " 7x~  x{}y~|  8{}v~M{|v~|  .{}y~5{}~} A{}y~Z{}~}  k{|y~|I{|y~}e{}~}e{|y~| \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y"
3921       "~     Ry~}b{~| r{}~}    i{}~}*{}~| (x~uy|  e{}~|         q{}~      h{|y~|h{}y~a{|y~|  h{|y~|y~|        c{|~ ny~"
3922       "g{}~}      M{|}q~|  9y|}y~|    1{}w~}M{|v~|  6y}|x{|}y~|6{|y~} A{}y~Z{}~}  l{|x~G{}w~}h{}~}h{}w~} [{|y~ g{}~}j{"
3923       "|y~|O{}~}F{|y~J{|y~h{}y~     Ry~}b{~| r{}~}    i{}~}-x}y~ '{}x~w|y~|  e{}~|         qy~      i{|x~g{}y~b{|x~  f"
3924       "{}w~                e{|y}w~}|  8{|w~}     ({|n~}  m{}s~}7{|w~ @{}y~Z{}~}  nv~|F{|y}~}h{}~}h{}~y}| Z{|y~ g{}~}j{"
3925       "|y~|O{}~}F{|y~J{|y~h{}y~     Rx| W{}~}    i{}~}-{}y~}| &{}s~|  i{|~y}y~         t{|~y}y~      kv~|g{}y~dv~|  ex"
3926       "}                   s{|y~}|     '{|n~}  m{|y}w~}|6{|y~} ?{}y~Z{}~}  nx~}|-{}~} B{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~"
3927       "h{}y~           q{}~}  -{|}x~}|  f{}y~}|         t{|x~}|      kx~}|f{}y~dx~}|                                  "
3928       " :{}~}                                     I{" };
3929 
3930     // Define a 52x64 font (large sans).
3931     static const char *const data_font_large[] = {
3932       "                                                                                                               "
3933       " -{|                                                                                                           "
3934       "                                    [{|x}|Dw}|Pw}| @{}v~} C{|w}|Ew}|Pv}| xv|Ev|Pu|  kv|Dw|P{|v}  6{|w}|E{|x}|P{"
3935       "|w}| pw}|                                                                                                      "
3936       "                           G{|w~}F{}w~P{}v~}Q{}w~}|w{|x~X{|v~vv~|U{|r~| D{}w~F{}w~P{}u~S{|v~|wv~}V{}w~|G{|w~|Q{"
3937       "|u~|Sv~}w{}w~}\"{|}x~}|v{|x~W{}w~|F{}w~Q{|u~}Q{}x~}|v{|x~X{}w~}w{|v~ G{}w~F{}w~|Q{}u~Rv~|w{}w~}O{}w~           "
3938       "                                                                                                               "
3939       "       E{|w~|H{}w~P{}t~}Ss~}|y{}x~X{|v~vv~|V{|p~ Cw~}H{|w~|Q{|t~}T{|v~|wv~}U{}w~Gw~}Q{|s~|Tv~}w{}w~}#{|s~}|{|x~"
3940       "}V{}w~|H{}w~Ps~}St~}w{}y~}X{}w~}w{|v~ Fw~}H{|w~|Q{|t~}Sv~|w{}w~}P{|w~|                                         "
3941       "                                                                                        D{|w~|J{|w~|Q{|x~}{w~|U"
3942       "{}l~}X{|v~vv~|Vw~}x{}x~} D{|w~|J{|w~|Q{|w~{}x~}U{|v~|wv~}T{}x~}Iw~}Pw~y|w~Tv~}w{}w~}#k~|U{}w~I{|w~|Q{}x~|{}x~|U"
3943       "{}r~|{|x~}X{}w~}w{|v~ Ew~|Iw~}Pw~|}x~}Tv~|w{}w~}Q{|w~|    M{|                                                  "
3944       "                                                                           q{}w~Jw~|Q{|x~}xw~Ux~}y{|}t~}W{|v~vv"
3945       "~|W{|x~|v{|x~| D{|w~|Kw~}Pw~x{}x~|V{|v~|wv~}S{}x~}K{}x~}P{}x~|y{|x~}Uv~}w{}w~}${|x~|y{|s~}S{}x~}K{|w~|Q{}x~}xw~"
3946       "Ux~}{|}r~W{}w~}w{|v~ E{|w~|K{}x~}Pw~|y{}x~|Uv~|w{}w~}Qw~|    O{}v~}                                            "
3947       "                                                                                 s{}x~}L{}x~|Pw~vw~W{|x~v{|}w~}"
3948       "V{|v~vv~|W{}y~}tx~} C{|w~L{}x~}P{}x~|w{}x~|W{|v~|wv~}R{}x~|M{}x~}P{}x~|w{|x~}Vv~}w{}w~}${|x~v{|}w~|Q{}x~}Lw~|Q{"
3949       "|x~}vw~W{|x~w{|t~|W{}w~}w{|v~ D{|w~L{|x~}P{}x~|w{}x~|Vv~|w{}w~}R{}x~}    P{|r~|                      Y{}w~|    "
3950       "                                                                                                  A{}x~|N{}x~}P"
3951       "{}x~u{|x~}\"v|vv|V{}y~}t{}y~} B{}x~}N{|x~}P{}x~|u{}x~Vv|vv|P{}x~|O{|x~}P{}x~|u{|x~}Wv|uv| D{}x~|N{}x~|Q{|x~}tx~"
3952       "}X{|x~u{|}y~}|Vu|vv| C{|x~}N{|w~P{|x~|u{}x~Vv|vu|S{|x~}    Op~|                      Zv~|                      "
3953       "                         ;v~                                                u{|v~      6{|y}|N{|y}|P{|x}s{|x} I"
3954       "{}y~}t{}y~} Aw|Nw|Ow|sw|    Qw|Nw|Pw|rx|  5{|x}N{|x}O{|y}|s{|y}| {{|y}| Dv|@v|Rv| C{}x~}x{|w~ Hu|@v|Rw| yv}@v}R"
3955       "{|w}  lv|@v|Rv|  8v}@v}|S{|w} m{}w~|        E{|y~x}|                                               ;{|w~}      "
3956       "                                          vv~|         J{}y~}t{}y~}               e{}w~}B{|w~}Rv~| Dx~|v{|x~| H"
3957       "v~A{}w~|S{|w~} {{|w~}B{}w~|S{|v~| Ay|sx|Y{}w~|B{|w~}Rv~  8{|w~}B{|w~}Rv~| o{|w~}     ?y}~}|  *x~             J{"
3958       "|y~|  b{}x~|T{|x~}                            L{|q~} y{}q~| H{|w~} xw~} `{|w~| {{|}t~)w~}Cv~Lv~Tw~}Dv~        G"
3959       "{|x}w~}Tw~|U{|v~y}|  1{|y}v~y}|   cv~y}     p{|y}x~y}|             {{v|vv|      3{}w~|         I{|x~|v{|x~|    "
3960       "      %{|   5{|y}w~y}|U{}w~|Cv~R{}v~}Q{|}y~}|ux~|Y{|v~|wv~}W{|x~t{}y~} H{|w~}C{}w~|Ru~|S{}w~}w{|v~W{}w~|D{|w~}R"
3961       "t~S{|v~vv~|X{|v~}K{}w~}ux~X{}w~C{|w~}R{}v~}Q{|}y~}|ux~|Y{|v~vv~| J{|w~}D{|w~}R{}u~Rv~|w{}w~}N{|w~}Zw~}Hv~}w{}w~"
3962       "}    N{|u~}  ,{|y~} Ix|Tx|kw|        Ru|  6{|y~|Yv|fx}|Zu| o{|w~Rw~|Hx|   Xu| vt|Ns| =t| xt|Ot|   [u|  ds|  kr|"
3963       "    Qt| ut| ts|    S{|q~} y{}q~| G{}w~| yw~} `{|w~|!{}q~)w~}Cv~Lv~Tw~}Dv~        I{|r~}Tw~|U{|r~}  5{|}o~| yr| "
3964       " ps~}     t{|p~|  kt|  is| s{|y} r{|x}| rx}|  bt|  lu|S{|v~vv~|!{|y}w~y}|  :{|l~|Qx| u{|y}w~}|Q{|x}w~y}|K{|w~| "
3965       " 9y|y}w~|O{|y}w~}|)y|x}dw~|hy|x}dw~|ly|x}y~y}|e{}x~|   6w~}x{}x~} us|      lt|Nt|Nt|Nt|Nt| ut|p{}~|   9{|}o~|V{"
3966       "}w~D{}w~R{|t~|S{|u~}vx~|Y{|v~|wv~}W{}y~}t{|x~ G{|w~}E{|w~}R{}t~S{}w~}w{|v~V{}w~E{|w~}R{}t~}T{|v~vv~|W{|v~}s{|y}"
3967       "X{}u~}w{|x~Ww~}Dv~R{|t~|S{|u~}w{|x~X{|v~vv~| I{}w~|Ew~}R{|t~}Sv~|w{}w~}Nw~}Yw~}Hv~}w{}w~}    O{|s~|cW}  i{}y~|"
3968       "\"{|}L{|u~}|Z{|}v~}|p{}u~}V{|}  /g|    ({}r~}| v~}R{}x~}vw~}R{|x}|t{|x}|V{|y~|\\{|}t~|i{}x~|]{}q~}|O{}x~}Iw~|R{|"
3969       "w~Hx~  *{|w~V{|}s~}|Sy|y}v~}T{|}q~}|V{|y}p~}|L{|u~}\\{|g~}T{}q~y}|_{}c~}[{|}q~}|U{|}r~}|   b{|}q~| w{}v~}X{}k~y"
3970       "}|R{|y}p~}|b{}m~x}y|W{}c~|`{}e~Y{|}o~}|a{}w~}hv~|Y{}w~}M{}w~}W{}w~}k{|u~}b{}w~}V{}t~|h{}t~|h{}u~}jv~^{|}p~}|Z{}"
3971       "m~y}|U{|}p~}|\\{}m~y}y|S{|}o~}y|bZ~g{|v~h{}w~}i{|v~|d{|v~|rv~|l{|v~}kv~|p{|v~|i{}v~g{}v~fv~}g\\~]{|q~}Uw~}I{}q~"
3972       "|P{|w}| w{}w~ yw~} `{|w~|\"o~)w~}Cv~Lv~Tw~}Dv~        J{|q~}Tw~|U{|q~}  7{}l~}\"y}p~y}  sr~}     v{}n~}R{}v~}V{"
3973       "}c~|_{}d~}^{|}p~}|R{|v~Y{}^~|iv~}r{|v~qv~}a{|}p~}| x{}x~} s{}w~ s{}w~|  f{|}r~}|-{}w~|i{|v~({|q~}|W{|v~vv~|Ty|u"
3974       "}y|U{|}o~|  ly|u}y|U{|l~|T{|}v~}| {|}p~|T{}p~}|N{|w~} yy|}m~} N{|r~|P{}q~|0{|y}t~|f{}x~}l{|y}t~|f{}x~}l{}p~}h{}"
3975       "x~}%{}v~}N{}v~}N{}v~}N{}v~}N{}v~}Q{|p~W{}\\~}b{|y}p~}|^{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|"
3976       "Z{}u~}jv~^{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|\"{|}q~y}t{}x~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}g{}"
3977       "v~fv~}c{}w~}J{|l~}Vw~}F{}w~|R{}x~|w~Ss~}x{|x~X{|v~|wv~}W{}y~}t{|x~ F{|w~|Fw~}R{|x~y}x~}T{}w~}w{|v~U{}x~}Fw~}R{|"
3978       "w~{w~|U{|v~vv~|V{}v~|x{|}v~Y{|s~}x{}x~W{|w~}F{}w~Qw~|w~Ss~}x{|x~X{|v~vv~| H{}w~F{}w~Qw~|}x~|Tv~|w{}w~}O{}w~Xw~}"
3979       "Hv~}w{}w~}    P{|q~c{}Y~}  ix~!y~|N{}r~}\\{}r~}s{|q~|Y{|y~}  5{|}e~}    *{}m~|\"v~}R{}x~}vw~}Rw~|tw~|V{|y~|]{}q"
3980       "~}k{|w~^{|l~|Q{}x~}J{}w~P{}x~}Ix~  *{}x~}W{}n~|Zy|}p~}W{|}k~}Z{}i~|Nt~}\\{|g~}V{}l~|`{}c~}\\{}l~|X{}n~}   e{|l~"
3981       "|Ty|y}u~y}|Rt~X{}g~}V{|}j~}d{}g~}|Z{}c~|`{}e~\\{|}i~}|d{}w~}hv~|Y{}w~}M{}w~}W{}w~}l{|u~}a{}w~}V{}t~}i{|s~|h{}t~"
3982       "|kv~`{|k~}|\\{}i~}|Z{|k~}|^{}i~}|W{|h~}dZ~g{|v~h{}w~}hv~}d{}v~q{}w~}l{}u~kv~|o{}v~j{|v~|fv~}h{}v~f\\~]{|v~u}U{}"
3983       "w~Iu}v~|Qt~| w{}x~} {{w~} `{|w~|#{}o~)w~}Cv~Lv~Tw~}Dv~    Ov|    s~x}|Tw~|U{|x}s~|  9{}j~}%{}j~|  uq~|     x{}l"
3984       "~}St~V{}c~|_{}d~}`{|}k~|T{|v~Y{}^~|iv~}r{|v~qv~}c{|k~}| {}v~} t{}w~ t{}u~|  i{|l~-v~i{}w~|Xw}|R{|l~X{|v~vv~|W{|"
3985       "}o~}|X{|m~|  p{|}o~}|X{|l~|U{}r~}!{|n~}U{}n~|Ow~} {{|}j~} N{|r~|R{|n~}1{|r~|g{|w~k{|r~|g{|w~k{}n~iw~$t~Nt~Nt~Nt"
3986       "~Nt~P{|r~V[~}d{|}j~}`{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}t~|kv~`{|k~}|Z{|k~}|Z{|k~}|Z{|k~}"
3987       "|Z{|k~}|&{|k~}w{|w~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}fv~}h{}v~b{}w~}K{}j~}W{|w~|H{|w~|R{|x~}{|x~}U{|"
3988       "x~}|w~}|{}x~X{|v~|wv~}W{|x~t{}y~} E{}w~G{}x~}Qw~|{}x~|U{}w~}w{|v~Tw~}H{}w~Q{}x~|{|x~}U{|v~vv~|U{}v~|}t~}Y{}x~|{"
3989       "}w~y|x~}V{|w~|H{|w~|R{}x~|{|x~}U{}x~}|w~}{|x~}X{|v~vv~| G{}x~}H{|w~|R{}x~|yw~Tv~|w{}w~}P{|w~|Xw~}Hv~}w{}w~}    "
3990       "P{}w~y|w~|d{|Y~|  j{|y~}\"{}x~Oo~}_{|o~}u{|o~}Zw~|  8{}b~}    ,{|j~}#v~}R{}x~}vw~}Rw~sw~U{|y~|^{}o~}lw~|_{}k~|Q"
3991       "{}x~}Jw~|P{|w~|Jx~  *w~|Xk~|[m~}X{}h~}[{}h~}P{}t~}\\{|g~}X{|j~|`{}c~}^{|i~}[{|j~   gi~|X{|}l~}|V{}t~|Y{}e~|Y{}f"
3992       "~}f{}d~}\\{}c~|`{}e~]{}e~}|f{}w~}hv~|Y{}w~}M{}w~}W{}w~}m{|u~|`{}w~}V{}s~|j{}s~|h{}t~}kv~b{|g~}]{}g~}]{|g~}_{}g~"
3993       "}Y{}f~dZ~g{|v~h{}w~}h{}v~dv~}q{}w~}lt~|m{|v~mv~}kv~}e{|v~|j{|v~|f\\~]{|w~}O{|w~|D{|w~|Rr~| ww~} w~} `{|w~|${|v~"
3994       "}|#w~}Cv~Lv~Tw~}Dv~    Ov~   !{|v~}Nw~|O{|v~}  :{|u~}|w{|}v~|'{}i~|  r{|}v~}     y{}v~}|x{|}v~}U{}t~|W{}c~|_{}d"
3995       "~}a{}g~|V{|v~Y{}^~|iv~}r{|v~qv~}e{|g~}\"{}t~} u{}w~ u{}s~| >y~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~"
3996       "}y|xy|}w~|  s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x"
3997       "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w"
3998       "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f"
3999       "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|"
4000       "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{"
4001       "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~}    Pw~}y{|x~}cY~  i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~|  ;{}`~}    -"
4002       "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~  +{|w~Xs~y}s~|\\m~}X{}"
4003       "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~   hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{"
4004       "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|"
4005       "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~   "
4006       " Ov~   !{}w~}Mw~|N{|v~  :{}v~|s{|v~V{|t}|V{|t~s}w~|  p{|v~     {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~"
4007       "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~}  t{}"
4008       "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{"
4009       "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_"
4010       "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{"
4011       "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}"
4012       "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{"
4013       "|t}|Lw~|xw~c{|[~}  iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~  ={|^~}    .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x"
4014       "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{}  4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{"
4015       "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~}   iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y"
4016       "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}"
4017       "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc"
4018       "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~    Ov~   !{}w~|Mw~|M{}w~  :v~|q{}w~|Xp~}X{}v~|p{|"
4019       "}|  o{}w~|     v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p"
4020       "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~|  w{|w~}|o{|}w~|(x~}tw~ rw~K{}x"
4021       "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|"
4022       "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~"
4023       "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`"
4024       "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|"
4025       "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~}  j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~}  b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|"
4026       "P{|w~|xx|av~|fv~|  j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~}  ?{}t~}y|     u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{"
4027       "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}|  `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~"
4028       "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~|   jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n"
4029       "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|"
4030       "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a"
4031       "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~    Ov~   !v~Lw~|M{}w~|  <{|w~"
4032       "}p{|w~}Xn~|Zv~  _{|v~    !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|"
4033       "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~|  x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}"
4034       "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w"
4035       "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|"
4036       "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v"
4037       "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v"
4038       "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy|  g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~}  d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}"
4039       "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~|  A{}u~}     q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^"
4040       "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~  a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o"
4041       "{}v~|a{|v~}p{}v~   j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|"
4042       "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|"
4043       "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~|  vw~} `{|w~|$"
4044       "w~} w~} >w~}Dv~    Ov~   !v~Lw~|M{}w~|  <{}w~|ow~}Xm~|[v~  ^v~|    \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}"
4045       "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~}  y{|"
4046       "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~"
4047       "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|"
4048       "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
4049       "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|"
4050       "x}rx}|  6w|Nw|Ox|rw| Nw~}  e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|"
4051       "Oy|Uw|jw|Vu|Wv|kw|b{}v~}     p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}"
4052       "w~}xx~x{}w~}|U{}w~  a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~|   \\{}w~}"
4053       "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}"
4054       "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}"
4055       "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~|  ww~} `{|w~|$w~} w~} >w~}Dv~    "
4056       "Ov~   !v~Lw~|M{|w~|  <{}w~|ow~}Xy~}w|}t~[v~|  _{}w~}    #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~"
4057       "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\"
4058       "{|}p~}  {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}"
4059       "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~"
4060       "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}"
4061       "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~         Py~}|u{|v~}       2w~}  f{"
4062       "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}"
4063       "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~|     ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{"
4064       "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~  aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}"
4065       "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}"
4066       "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v"
4067       "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~|  xw~} `{|w~|$w"
4068       "~} w~} >w~}Dv~    Ov~   !w~}Lw~|M{|w~|  <v~nw~}X{|s{}v~}\\{}v~|  `{|v~    #{}w~|n{|w~}Z{}w~|{|w~}Uu~|P{}w~}T{|u"
4069       "~h{}v~}f{|r~y}v~}r~}d{}w~}hv~|iv~}r{|v~qv~}j{|v~}i{|u~-{}v~}{}w~{|v~} {}w~ {}v~}{}w~{|u~ Cy~}Rv~|S{}~}g{|y~|_v~"
4070       "q{}w~|Tw~|T{}w~| {{x~}t{|y}u~}|u{}x~^{}m~}  {{x~}wq}y|s{}x~,{}y~}r{}y~}R{}w~H{|x~}Qs~} L{}m~}w{|x~} H{}x~|U{|x~"
4071       "}p{|x~}.{}x~|k{|x~}a{}x~|k{|w~cx}u~|n{|x~}#{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}Tv~xv~[v~}w{|v"
4072       "~W{|v~}e{|c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~y|v~nv~g{|v~}i{|u~g{|v~}i{|u~g{|v~}i{"
4073       "|u~g{|v~}i{|u~g{|v~}i{|u~d{}y~f{}y~|f{|v~}k{|s~f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}b{|v~|r{|v~|^{}i~}|"
4074       "\\v~q{}t~|         F{}v~|    C{~|   mw~}  gu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|V{|w~Y{}w~}i{}w~} B{"
4075       "}w~}Mx~${}n~W{|k~}d{|U~}c{|m~}W{|n~}[w~}kw~}Xt~}X{|w~}mv~cv~|     o{|v~| mv~}R{}x~}vw~}Tw~|tw~|^{}v~|x{|y~|u{|~"
4076       "|iw~|rw~s{|w~\\v~B{}x~}N{|w~}J{}w~|S{|n~|Q{}w~  b{|w~|Zv~|nv~|V{}w~}E{}w~}M{|v~X{|w~|y{}w~}\\{|w~}M{|v~={}v~^{|"
4077       "v~m{}w~}b{|v~lv~| <{|}x~}6{|x~}|7{}w~}cv~|b{|w~}b{|v~xv~[{}w~}l{}w~}bu~|P{}w~}h{}v~}c{}w~}L{}w~}Ru~M{}w~}hv~|Y{"
4078       "}w~}M{}w~}W{}w~}t{}u~X{}w~}V{}w~|{}x~}o{|w~|{v~|h{}w~|{v~}ov~gu~|h{}v~|c{}w~}mv~}fu~|h{}v~|e{}w~}mv~}a{|v~C{|v~"
4079       "|Z{|v~h{}w~}ev~}j{}v~l{}w~}p{}x~}{|w~ov~|h{|v~}u{}v~[{}v~rv~}M{}v~}Y{|w~}Lw~|F{|w~|Y{}v~|qu~| Kt|Uw~}uu|Mt|Ru|u"
4080       "{|w~|Wt|Ow~}Mu|Tw~}uu| Jw~}Dv~Tu|mv|Vu|Pt|Ku|Qu|Bv|Us|Rv~   !w~}Lw~|M{|w~|  iv|Sv~o{|w~}N{}v~\\{|t~}|Is|Mu| u{}"
4081       "w~|   Zt| Lv~|n{|v~[{|v~xv~Tu~P{}w~}T{}v~|gu~g{|t~}|y{|v~x{}t~}e{}w~}hv~|iv~}r{|v~qv~}ju~|h{}v~|/{}v~}y{}w~y{|v"
4082       "~}!{}w~!{}v~}y{}w~y{|u~ F{|}y~}x|V{|v~S{}x~}i{|w~|`{}w~|rw~}Sw~|T{|v~|!{}y~}u{|n~}v{}y~}a{|k~}  {}y~}vn~}t{}y~}"
4083       "-{}y~}r{}y~}R{}w~I{|w~Pt~}| L{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|kw~|a{}x~|kw~|ct~}lw~|${|v~xv~U{|v~xv~U{|v~xv~U"
4084       "{|v~xv~U{|v~xv~U{|w~}x{}w~|]{|v~v{|v~Wu~|L{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{|v~}f{}w~|{v~}o"
4085       "v~gu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|f{}w~h{}w~|gu~|l{|r~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~"
4086       "h{}w~}a{}v~rv~}]{}g~}]w~}s{|r~|Xt|Nt|Nt|Nt|Nt|Nt|Xt|lu|Ut|Pt|Nt|Nt|Nt|  0{}v~|Pu|Pt|Nt|Nt|Nt|Nt| ut|t{}y~}   nw"
4087       "~}uu|  t{}w~}|wv|v{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~V{|w~Xv~iv~| C{|v~M{|y~}%{}m~}Wk~}d{|U~}d"
4088       "{|k~}Y{}k~|]w~}kw~}Y{|s~X{|v~n{|w~}d{}w~}     n{}w~} lv~}R{}x~}vw~}U{|w~t{|w~]v~|w{|y~|`w~|rw~s{}x~|\\v~|C{}x~}"
4089       "N{}w~|J{|w~}Q{|r~|O{}w~  b{}w~Z{|v~m{}w~}V{}w~}E{}w~}M{|v~Xw~}x{}w~}\\{|w~}M{}w~}=v~}^{|v~m{}w~}b{|v~lv~} ?{|}u"
4090       "~}6{|u~}|:{}w~}d{}w~|`{|w~}c{}w~|x{}w~}\\{}w~}l{}w~}c{|v~}O{}w~}gu~c{}w~}L{}w~}S{|v~}M{}w~}hv~|Y{}w~}M{}w~}W{}w"
4091       "~}uu~}W{}w~}V{}w~|{|w~|p{}w~yv~|h{}w~|{|v~ov~h{|v~}fu~c{}w~}mv~}g{|v~}fu~e{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}e{}v~j"
4092       "v~|l{}w~}pw~|yw~|q{|v~f{}v~|w{|v~|Zv~}t{}v~M{}v~}X{|w~}L{}x~}F{|w~|Z{}v~|o{}v~| P{|}q~}|Xw~}w{}s~}|S{|}q~}|X{}s"
4093       "~}|x{|w~|Z{|}r~}|W{}k~}W{}s~}|x{|w~|`w~}w{|s~}|Rv~Lv~Tw~}n{|v~}Xv~_w~}w{}s~}r{|s~}cw~}w{|s~}|V{|}r~}|Yw~}w{}s~}"
4094       "|V{}s~}|x{|w~|Zw~}w{}t~|Y{}o~}|Z{}i~]{|w~|m{}w~|c{|v~iv~i{}w~|pu~ow~}hv~}m{|v~|d{|v~iv~`d~Uw~}Lw~|M{|w~|  l{|s~"
4095       "}|u{}x~}av~o{|w~}M{}w~|\\{}q~}|P{}o~}|\\w~}w{|s~}|^x~y}hv~W{}w~}X{|w~|m{}w~|d{}w~}h{}w~}]{|y}w{|}x~}|]_~|dv~t{}"
4096       "w~t{|w~}[{|q~}|U{|y}i~}f{|`~b{|v~lv~|\\{}w~|x{}w~}U{|u~Q{}w~}U{|v~}f{|v~|ht~|w{|v~v{}u~}f{}w~}hv~|iv~}r{|v~qv~}"
4097       "k{|v~}fu~/{|w~}x{}w~x{|w~}I{|T{}w~S{|i{|\\w~}x{}w~x{|w~|!v~}O{|}p~}|Y{|v~T{|v~}k{|v~}_v~s{}w~|Sw~|Su~|#{|x~u{}l"
4098       "~ux~|bv~}y|v{|x~} !{|x~ul~|ux~|.{|x~|t{|x~|R{}w~J{|w~|L{|}x~}&{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|l{"
4099       "}x~}`{}x~|l{}x~}br~|o{}x~}Qv~|S{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{|w~}]{}w~}v{|"
4100       "v~X{|v~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|{|v~ov~h{|v~}fu~i{|v~}fu~i{|v~}fu~i{|v~}"
4101       "fu~i{|v~}fu~g{|u~j{}v~}h{|v~}l{|w~}v~}g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`v~}t{}v~\\{}f~}^w~}t{}v~}y|Y"
4102       "{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|_{|}q~}|r{|}r~}[{|}q~}|W{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Qv~Lv~Lv~"
4103       "Lv~O{|y}w~}u~|\\w~}w{|s~}|V{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Q{}u~Q{|}r~}|x{}x~}b{|w~|m{}w~|a{|w~|m{}w~|a{"
4104       "|w~|m{}w~|a{|w~|m{}w~|c{|v~iv~aw~}w{}s~}|^{|v~iv~ W{}w~}u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{"
4105       "}w~}W{}w~X{}w~}k{|v~ C{|v~|M{}y~|&{|k~}X{}l~|cU~}di~|[{}i~|^w~}kw~}Y{}s~|Xv~|o{}w~|dw~}     mv~| lv~}R{}x~}vw~}"
4106       "^{}Z~f{|w~}v{|y~|`w~|rw~t{|x~}[{}w~}C{}x~}Nv~Hv~O{}v~}M{}w~  bw~}Z{}w~}m{|v~V{}w~}E{}w~}M{|v~Y{}w~w{}w~}\\{|w~}"
4107       "Mv~|>{|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{"
4108       "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v"
4109       "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|"
4110       "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|"
4111       "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|"
4112       "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~|  n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{"
4113       "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|"
4114       "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|"
4115       "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~"
4116       "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv"
4117       "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z"
4118       "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{"
4119       "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|"
4120       "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|"
4121       "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}"
4122       "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}"
4123       "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~     lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~"
4124       "}H{}w~|Q{|t~|N{}w~  c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v"
4125       "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w"
4126       "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~"
4127       "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m"
4128       "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}"
4129       "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w"
4130       "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c"
4131       "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}"
4132       "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|"
4133       "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]"
4134       "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}"
4135       "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}"
4136       "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}["
4137       "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~"
4138       "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a"
4139       "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}"
4140       "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~}     pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v"
4141       "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~  c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v"
4142       "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m"
4143       "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~"
4144       "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~"
4145       "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}"
4146       "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}"
4147       "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u"
4148       "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t"
4149       "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r"
4150       "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w"
4151       "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~"
4152       "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv"
4153       "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d"
4154       "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v"
4155       "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}"
4156       "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~"
4157       "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~"
4158       "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o"
4159       "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~     pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v"
4160       "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~  c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|"
4161       "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{"
4162       "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n"
4163       "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{"
4164       "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|"
4165       "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|"
4166       "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w"
4167       "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w"
4168       "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v"
4169       "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~"
4170       "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]"
4171       "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}"
4172       "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{"
4173       "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r"
4174       "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|"
4175       "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v"
4176       "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{"
4177       "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}"
4178       "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~}     q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw"
4179       "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~  cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|"
4180       "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{"
4181       "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{"
4182       "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w"
4183       "~|H{|w~|  s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}"
4184       "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|"
4185       "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s"
4186       "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{"
4187       "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}"
4188       "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}"
4189       "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t"
4190       "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w"
4191       "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|"
4192       "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m"
4193       "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~"
4194       "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~"
4195       "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~"
4196       "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~"
4197       "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~|     q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x"
4198       "x~x{|}w~}U{}w~  d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{"
4199       "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{"
4200       "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~"
4201       "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~|  bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U"
4202       "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w"
4203       "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_"
4204       "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}"
4205       "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|"
4206       "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v"
4207       "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} "
4208       "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~"
4209       "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v"
4210       "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~"
4211       "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}"
4212       "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|"
4213       "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}"
4214       "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~"
4215       "r{}w~}dw~|     lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~  d{}x~}Y{|v~k{}w~}W{}w"
4216       "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}"
4217       "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|"
4218       "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}"
4219       "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~|  b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T"
4220       "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~"
4221       "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o"
4222       "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a"
4223       "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{"
4224       "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~"
4225       "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v"
4226       "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}"
4227       "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|"
4228       "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|"
4229       "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}"
4230       "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`"
4231       "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u"
4232       "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~"
4233       "|yv~|X{}w~|sv~|dV~}    2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ "
4234       " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}"
4235       "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}"
4236       "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u"
4237       "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~|  aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~"
4238       "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{"
4239       "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`"
4240       "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~"
4241       "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|"
4242       "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~"
4243       "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}"
4244       "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w"
4245       "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v"
4246       "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}["
4247       "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V"
4248       "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{"
4249       "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}"
4250       "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~}    2v~| k{}w~| {{|x~"
4251       "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~  e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~"
4252       "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w"
4253       "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b"
4254       "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~|  a{}w~^v~|m{|"
4255       "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c"
4256       "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}"
4257       "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m"
4258       "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}"
4259       "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|"
4260       "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|"
4261       "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\"
4262       "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w"
4263       "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}"
4264       "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~"
4265       "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}"
4266       "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}"
4267       "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~}    2v~| k{}w~| {{}"
4268       "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~}  t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{"
4269       "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}"
4270       "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv"
4271       "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I"
4272       "{}w~I{|w~|  a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{"
4273       "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K"
4274       "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~"
4275       "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|"
4276       "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X"
4277       "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}"
4278       "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o",
4279       "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}"
4280       "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|"
4281       "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~"
4282       "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}"
4283       "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|"
4284       "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{"
4285       "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~}    2v~| k{}w~| {{w~|tw~|W{|}m~}T{"
4286       "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{"
4287       "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|"
4288       "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~"
4289       "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~|  "
4290       "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v"
4291       "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|"
4292       "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~"
4293       "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~"
4294       "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~"
4295       "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~  p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~|  jv~}"
4296       "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|"
4297       "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}"
4298       "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w"
4299       "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}"
4300       "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}"
4301       "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd"
4302       "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw"
4303       "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z|    5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox"
4304       "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}"
4305       "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{"
4306       "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w"
4307       "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~|  p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~"
4308       "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~"
4309       "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~"
4310       "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{"
4311       "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c"
4312       "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~"
4313       "}8{|y~|sw~vw~}q{|y~| Q{}w~  p{|w~|m{}w~|Ux~}w{|x~}W{|v~|  i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{"
4314       "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}"
4315       "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}"
4316       "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~"
4317       "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v"
4318       "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j"
4319       "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{"
4320       "|w~}b{}x~}     pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k"
4321       "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}"
4322       "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u"
4323       "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~"
4324       "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~|  r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~"
4325       "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|"
4326       "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}"
4327       "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv"
4328       "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]"
4329       "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{"
4330       "|w~|r{}y~ P{}w~  p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|"
4331       "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}"
4332       "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_"
4333       "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|"
4334       "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}"
4335       "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~"
4336       "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{"
4337       "}w~|b{}x~}     q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w"
4338       "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv"
4339       "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|"
4340       "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|"
4341       "v~|Ru~|M{|w~}H{|w~J{|w~|  s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}"
4342       "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|"
4343       "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{"
4344       "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v"
4345       "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}"
4346       "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~  p{|"
4347       "w~|m{}w~|Ux~}w{|x~}  w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|"
4348       "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{"
4349       "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{"
4350       "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|"
4351       "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m"
4352       "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|"
4353       "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~|   "
4354       "  r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}"
4355       "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k"
4356       "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~"
4357       "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|"
4358       "v~|S{}v~|L{|w~}Gw~|K{|w~|  t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~"
4359       "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~"
4360       "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~"
4361       "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y"
4362       "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\"
4363       "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~"
4364       "} P{}w~  p{|w~|m{}w~|Ux~}w{|x~}  w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b"
4365       "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v"
4366       "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
4367       "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u"
4368       "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{"
4369       "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}"
4370       "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x"
4371       "{|v~`w~}     m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~  g{}w~Uv~|lv~|W{}w~}P{}v~"
4372       "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{"
4373       "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv"
4374       "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{"
4375       "|w~}G{}x~}K{|w~|  tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~"
4376       "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L"
4377       "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~"
4378       "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v"
4379       "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|"
4380       "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~  "
4381       "p{|w~|m{}w~|Ux~}w{|x~}  w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{"
4382       "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k"
4383       "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V"
4384       "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~"
4385       "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}"
4386       "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~"
4387       "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|"
4388       "     mv~|  o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~  gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~"
4389       "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}"
4390       "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}"
4391       "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w"
4392       "~|  u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~"
4393       "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|"
4394       "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{"
4395       "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}"
4396       "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W"
4397       "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~  p{|w~|m{}w"
4398       "~|Ux~}w{|x~}  B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~"
4399       "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}"
4400       "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v"
4401       "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{"
4402       "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v"
4403       "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|"
4404       "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~}     mv~}  g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw"
4405       "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~  h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|"
4406       " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}"
4407       "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v"
4408       "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~|  u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv"
4409       "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~"
4410       "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{"
4411       "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~"
4412       "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}"
4413       "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|"
4414       "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~  p{|w~|m{}w~|Ux~}w{|x~}  C{}w"
4415       "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~"
4416       "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~"
4417       "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv"
4418       "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m"
4419       "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w"
4420       "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}"
4421       "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X"
4422       "{|v~{|v~^{}w~}     n{}v~  gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~  h{|w~T{|v~m{}w~}V{}w~}"
4423       "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}"
4424       "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g"
4425       "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w"
4426       "~}F{}x~}L{|w~|  u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv"
4427       "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w"
4428       "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~"
4429       "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~"
4430       "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{"
4431       "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|"
4432       "w~|y{|y~} N{}w~  p{|w~}m{}w~|Ux~}w{|x~}  Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|"
4433       "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{"
4434       "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v"
4435       "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{"
4436       "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w"
4437       "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y"
4438       "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w"
4439       "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~|     o{|v~|  hw"
4440       "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~  h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~"
4441       "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~"
4442       "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y"
4443       "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~|  u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}"
4444       "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv"
4445       "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a"
4446       "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u"
4447       "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}"
4448       "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x"
4449       "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~|   >{|w~}mv~|Ux~}w{|x~}  Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w"
4450       "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w"
4451       "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}"
4452       "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m"
4453       "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv"
4454       "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}"
4455       "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y"
4456       "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~|     o{}v~j{}  {|x~}t{|"
4457       "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~  hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|"
4458       "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~"
4459       "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}"
4460       "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~|  u{}w~|nu~|_"
4461       "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv"
4462       "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~"
4463       "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|"
4464       "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|"
4465       "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x"
4466       "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~|   ={|v~n{|v~|Ux~}w{|x~}  Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}"
4467       "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w"
4468       "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{"
4469       "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n"
4470       "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v"
4471       "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}"
4472       "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|"
4473       "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~}     p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~"
4474       "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{"
4475       "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a"
4476       "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}"
4477       "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~|  u{}w~|o{}u~|_t~|q{|v~}_"
4478       "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u"
4479       "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~"
4480       "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_"
4481       "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y"
4482       "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x"
4483       "~|g{|x~}   <{|v~|o{}v~|Ux~}w{|x~}  Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f"
4484       "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~"
4485       "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~"
4486       "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{"
4487       "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{"
4488       "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~"
4489       "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p"
4490       "{|v~X{|r~}Z{}u~}     q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{"
4491       "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|"
4492       "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~"
4493       "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u"
4494       "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~|  u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{"
4495       "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~"
4496       "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~|  r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w"
4497       "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}"
4498       "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U"
4499       "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~}   ;{|u~p{|u~|Ux~}w{|x~}  Ey~|s{|~}T"
4500       "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u"
4501       "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u"
4502       "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v"
4503       "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{"
4504       "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{"
4505       "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} "
4506       "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y|     tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|"
4507       "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}"
4508       "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}"
4509       "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~"
4510       "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|"
4511       "v~|[{|u~}b|^{|w~}E{|w~|N{|w~|  tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}"
4512       "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|"
4513       "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~|  qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~"
4514       "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}"
4515       "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#"
4516       "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{"
4517       "|t~}Ux~}w{|x~}  E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f"
4518       "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}"
4519       "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~"
4520       "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}"
4521       "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}"
4522       "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v"
4523       "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M"
4524       "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~}    -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o"
4525       "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z"
4526       "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y"
4527       "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~"
4528       "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~|  t{}u~y"
4529       "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv"
4530       "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{"
4531       "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~|  q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}"
4532       "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{"
4533       "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~"
4534       "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~|  E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~"
4535       "}   l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|"
4536       "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}"
4537       "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}"
4538       "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}"
4539       "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{"
4540       "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{"
4541       "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~"
4542       "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~}    -{|h~|$v~}#{}x~}t{}x"
4543       "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~"
4544       "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv"
4545       "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}"
4546       "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~|  sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw"
4547       "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d"
4548       "~Uw~}Lw~|M{|w~|  p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~"
4549       "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~"
4550       "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~}  A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~}   m{|x~}b{}x~hw~lk~k{|x~}b{}x~"
4551       "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}"
4552       "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}"
4553       "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~"
4554       "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|"
4555       "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~}    +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~"
4556       "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~|  "
4557       "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{"
4558       "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~"
4559       "|  s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{"
4560       "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~|  o{|n~|w{}u~b"
4561       "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|"
4562       "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${"
4563       "}m~}  ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~}   mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d"
4564       "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}["
4565       "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~"
4566       "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~"
4567       "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V"
4568       "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~|  i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~}    *{|}p~"
4569       "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}"
4570       "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~  h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w"
4571       "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}"
4572       "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~|  q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}"
4573       "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|"
4574       "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~|  n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~"
4575       "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V"
4576       "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y|  9y|}u~}y| "
4577       "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}|  q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|"
4578       "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{"
4579       "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}"
4580       "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U"
4581       "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\"
4582       "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~}  3{|~}     ;f|    '{|y}w~}y|  8{|y~|X{|x~}"
4583       "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|"
4584       "w~|  B{|v~| 1{|y}u~y}|  o{|x}u~y}y| Fv~|  7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y|   a{|w~}C{}x~}O{|w~|  oy}"
4585       "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}"
4586       "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~|  ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~"
4587       "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}|    Ry|y}v~y}|"
4588       "   Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~       5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~}  r{|y}|Kw~|L{|y}|Hv~|    E"
4589       "{|y}u~y}|      qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y"
4590       "}y|S{|y}v~y}y|  oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}"
4591       "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~"
4592       "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~|    Mw~|                K{|y~|  e{|w~Nw~"
4593       "| ?{}w~ Cw~}      .{}w~  @{|v~|d{}|     Kv~|   !u~|     J{|w~}C{|w~O{|w~|     9w~} Iv~   bw~}9{|w~|    X{|v~ rv"
4594       "~Lw~|M{}w~|  <v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|u~y}s~|       5{|w~|Ax~}w{|x~} {"
4595       "{x~|   0{|v~    ?{}y~}         R{|}        5x~|         O{|y~}  &{|v~Uw~}D{|v~    Lw~|                K{|y~|  d"
4596       "{}x~}P{}w~ >w~| D{|w~|      .w~|  ?{|v~}g{|x~|     M{|v~    {|u~|     K{|w~}Bw~|P{|w~|     :{}w~} Iw~}   bw~}9{"
4597       "|w~|    X{}w~| r{}w~|Mw~|Mv~  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|l~|       4{|w~"
4598       "|Ax~}w{|x~} {{}y~}   /v~|    ?x~|                  f{|x~         M{}  %{}w~|Uw~}D{}w~|    Lw~|                K"
4599       "{|y~|  d{|w~Pw~| ?{|w~ C{}w~      .{|w~  ={|u~}|l{|u~|     N{}v~    {{|u~|     L{|q~}H{}x~}V{}q~|     :v~| Iw~}"
4600       "   bw~}9{|w~|    Xv~ q{}w~}Mw~|N{|v~  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|}o~}|  "
4601       "     3{|w~|Ax~}w{|x~} {{|x~|   0v~}m{}    N{|x~                  e{}y~}            Rv~Tw~}Dv~    S{}x~x{|w~|   "
4602       "             K{|y~|  c{}x~}R{}x~} >{|x~| Cw~}      .{|x~|  ;{}t~}|sy|}t~|     N{|v~}    y{|u~|     M{|q~}H{|w~V"
4603       "{}q~|     ;{}v~ I{|w~}   bw~}9{|w~|    Y{}w~} q{|v~}|Ow~|P{|}v~}  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}      "
4604       "    )v~}Iy~}  gw~|Q{|y}v~y}|       1{|w~|Ax~}w{|x~} yx~|   0{}v~|p{|~}    N{|x~|                  f{|x~        "
4605       "    S{}w~}Tw~}E{}w~}    S{}x~|y{|w~                J{|y~|  bw~|Sw~| >{}y~}        K{}y~}  9{|p~x}q~}|     N{|u~"
4606       "|    x{|u~     M{|q~} y{}q~|     K{|}|p{|u~| I{}w~|   bw~}9{|w~|    Z{|v~ o{}q~}Tw~|U{|p~  :v~  S{|w~}W{|w~|#{|"
4607       "w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|        W{|w~|Aw|vx| y{|x~}   0{|u~|s{}x~}    N{|x~|                "
4608       "  f{|x~|            U{|v~Sw~}F{|v~    R{|x~}y{}w~                J{|y~|  b{|x}|T{|x}|             w{}g~}|     Q"
4609       "x|y}u~}    v{|u~     N{|p} yp}|     K{|x~}y|wy|}u~} J{|}v~   aw~}9{|w~|    \\{|}v~} nq~}Tw~|U{|q~|  :v~  S{|w~}"
4610       "W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|        W{|w~| :{|}w|}w~|   /t~y}x|y}v~}    U{|}|x{|w~|    "
4611       "              f{}x~|            W{|}v~}Sw~}H{|}v~}    Qq~|                J{|y}               *{|}l~}|     O{}q"
4612       "~    tt|            `{|i~} Lr~|   aw~}9{|w~|    `{}q~ l{}s~}Tw~|U{|s~}|  9v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~"
4613       "}          )v~}Iy~}  gw~|        W{|w~| :{|q~   .{|i~}    U{|q~                  ly}w|}w~|            [{}q~Rw~}"
4614       "L{}q~    P{}r~                                M{|y}u~y}y|     L{}r~|                R{|j~} Ks~}   `w~}9{|w~|   "
4615       " `{}r~| jy|v}|Tw~|U{|u}|  6v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy}|  gw~|        W{|w~| :{|r~| "
4616       "  -{|k~}|    U{|r~}                  l{}r~}            Z{}r~|Rw~}L{}r~|    O{}t~                               "
4617       "       k{}t~}           -{|`}|    `{|}m~}| Jt~}   _w~}9{|w~|    `{}s~| :w~|   cv~  S{|w~}W{|w~|#{|w~| j{}w~ s{}"
4618       "w~Uw~}          )v~}           d{|w~| 9y}w~y}   ){}o~}|    S{|}u~}|                  k{}r~            Y{}s~|Qw~"
4619       "}L{}s~|    M{}w~}                                      j{}w~}|           +{}`~}    ]{|x}v~y}| Gw~y}   ]w~}9{|w~"
4620       "|    `{}v~}| 8w~|   cv~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}                      g{|w~|     8{|}v~y}|    Ly|   "
4621       "               g{|y}w~}|            X{}v~}|Ow~}L{}v~}|    Iy|                                                  "
4622       "l{}`~}                Ww~|                                                                                     "
4623       "                                            L{}`~}                Ww}|                                         "
4624       "                                  r{" };
4625 
4626     // Define a 104x128 binary font (huge sans).
4627     static const char *const data_font_huge[] = {
4628       "                                                                                                               "
4629       "                                                                                                               "
4630       "                                                                                                               "
4631       "                                                                                                               "
4632       "                                                                                                               "
4633       "                                                                                                               "
4634       "                                                                                                               "
4635       "                                                                                                               "
4636       "                                                                                                        FY  AY "
4637       "'Z      ;W      @Y  @Y 'Z    Y  @Y (Z        :Y  ?Y (Z          0Y  ?Y (Z    >X                                "
4638       "                                                                                                               "
4639       "                                                                                                               "
4640       "                                                                                                               "
4641       "                                                                                                               "
4642       "                         )X  AX '\\ )XAV 7YDY -]      BY  BY '[ +YEY 2X  AY (\\ -YDY   'XAU 3Y  AY (\\ )XAV 8YD"
4643       "Y      LY  AY (\\ ,YEY #Y                                                                                      "
4644       "                                                                                                               "
4645       "                                                                                                               "
4646       "                                                                                                               "
4647       "                                                                                  (X  CX '^ +[CU 6ZEY .`      C"
4648       "X  CY '] -ZEZ 2X  CY (^ .ZEZ   )[CU 2Y  CY (] *[CU 7ZEZ      LY  CY (] -ZEZ %Y                                 "
4649       "                                                                                                               "
4650       "                                                                                                               "
4651       "                                                                                                               "
4652       "                                                                                                               "
4653       "                        'Y  EY '^ ,^FV 6ZEY /b      CX  DX '_ .ZEZ 2Y  DX '_ /ZEZ   +_FV 1X  CX (_ ,^FV 7ZEZ   "
4654       "   KX  CX (_ .ZEZ &Y                                                                                           "
4655       "                                                                                                               "
4656       "                                                                                                               "
4657       "                                                                                                               "
4658       "                                                                             %Y  GY '` .aHV 6ZEY 1e      DY  FX"
4659       " 'a /ZEZ 1Y  FX '` /ZEZ   +aHV 0X  EX '` .aHV 7ZEZ      JX  EX (a /ZEZ &X                                      "
4660       "                                                                                                               "
4661       "                                                                                                               "
4662       "                                                                                                               "
4663       "                                                                                                               "
4664       "                   #X  GX 'XNX 0dKW 6ZEY 1f      DY  HX &WMX 0ZEZ 0X  GX 'XMW 0ZEZ   ,dLX /X  GX 'WMX 0dLX 7ZEZ"
4665       "      IX  GX 'WMX 0ZEZ 'X                 :T                                                                   "
4666       "                                                                                                               "
4667       "                                                                                                               "
4668       "                                                                                                               "
4669       "                                                                                    ;X  IX 'XLX 1o 5ZEY 2ZLY   "
4670       "   CX  IX &WKW 0ZEZ /X  HX (XLX 1ZEZ   ,o .Y  HX (WKX 1o 6ZEZ      IY  IY (WKW 0ZEZ (X                 <Z      "
4671       "                                                                                                               "
4672       "                                                                                                               "
4673       "                                                                                                               "
4674       "                                                                                                               "
4675       "                                  =X  KX 'XJX 3WKd 5ZEY 3XGX      CX  JX 'WIW 1ZEZ .X  JX (XJX 2ZEZ   -WKd -X  "
4676       "IX (WIW 2WKd 6ZEZ      HX  IX (WIW 1ZEZ )X                 =^                                                  "
4677       "                                                                                                               "
4678       "                                                                                                               "
4679       "                                                                                                               "
4680       "                                                                                                     >X  MX &WH"
4681       "W 3VHa 4ZEY 3WDW      CX  LX 'WGW 2ZEZ -X  LX 'WHW 2ZEZ   -VHa +X  KX (XHW 3VHa 5ZEZ      GX  KX (WGW 2ZEZ )X  "
4682       "               ?b                                                                                              "
4683       "                                                                                                               "
4684       "                                                                                                               "
4685       "                                                                                                               "
4686       "                                                         ?W  MW &WFW 4VF^ 3ZEY 4WBV      BW  MX 'WEW 3ZEZ ,W  M"
4687       "X 'WFW 3ZEZ   -VF^ )X  MX 'WFW 4VF^ 4ZEZ      FX  MX 'WFW 3ZEZ *X                 ?d                           "
4688       "                                                                                                               "
4689       "                                                                                                               "
4690       "                                                                                                               "
4691       "                                                                                                               "
4692       "             ?W  X 'WDW 5UC[ 2ZEY 4VAV      AW  X &WDW 4ZEZ +W  NW 'WDW 4ZEZ   -UC[ 'W  MW 'WDW 5UC[ 3ZEZ      "
4693       "EW  MW 'WDW 4ZEZ +X                 ?f                                                                         "
4694       "                                                                                                               "
4695       "                                                                                                               "
4696       "                                                                                                               "
4697       "                                                                              @X \"X 'WBW 6UAW 0ZEY 4V@V      B"
4698       "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ   .VAW $W  W 'WBW 6UAW 1ZEZ      DW  W 'WBV 4ZEZ +W                 >f          "
4699       "                                                                                                               "
4700       "                                                                                                               "
4701       "                                                                                                               "
4702       "                                                                                                               "
4703       "                              ?X #W 'W@W      U?V      AX #W &W@V    NX #W &V@W        9W \"W 'W@V          .W "
4704       "\"W 'W@V   !W                 >XHX                                                                             "
4705       "         3Y                                                                                                    "
4706       "                                                                                                               "
4707       "                                                                                                               "
4708       "                                                                           6W $W &V>V      U?V      @W $W &W>V "
4709       "   NW $X 'V>V        8W $X (W>V          /X $W 'W>V   #W                 >XFX                                  "
4710       "                                                    5Z                                                         "
4711       "                                                                                                               "
4712       "                ,Z                                                                                             "
4713       "                                                                                             GZ                "
4714       "                    #U?V                                                           NY  7Z ,X      CVCW      MY "
4715       " 7Z ,X   $Z  7Z ,X        >Z  6Y ,X          4Z  7Y +W    7Y                                 @Z                "
4716       "                                                                                                               "
4717       "                                                         +Z                                                    "
4718       "                                                                                                               "
4719       "                       HY                                    \"U?V                                             "
4720       "              MY  8Y ,Y      CVBV      LY  9Z ,Y   #Z  9Z ,Z        >Z  8Y ,Y          3Y  8Z ,Y    9Y         "
4721       "                        ?Z                                                                                     "
4722       "                                                                                                   *Y          "
4723       "                                                                                                               "
4724       "                                                                 IY                                    !U?V    "
4725       "                                                       LY  :Y ,[ $R>U   ,V@V      MZ  :Y +Z   #Y  9Y +Z      ?R"
4726       ">U 8Y  9Y +Z %S?U        HY  :Z ,[    ;Y                                 ?[                                    "
4727       "                                                                                                               "
4728       "                                     )Y                                                                        "
4729       "                                    8U                                                                         "
4730       "    9Y                                     V@U                                                           JY  <Y"
4731       " +[ 'XAU   ,V@V      LY  ;Y +\\   #Y  ;Y +\\      CXAU 7Y  ;Z ,\\ )XAV        HY  ;Y +[    <Z                  "
4732       "               ?U                                                    ;T        $W /W                           "
4733       "                                                                                   8e   !f      LY    Y     LX "
4734       "   L]   :Y  <Y  NX 0X  >Y                                 @Y /X 0Y           K`            .X                  "
4735       "  ^                                                  =ZEY                          @Y                          "
4736       "           NVAV                                          <P              -X +Y  =Y +] )[CU 7YDY 4V@V      KY  ="
4737       "Y +] ,YDY 5Y  =Y *] .YDY 5[  M[CU 6Y  <Y ,] *[CV 7YDY      Y  =Y +] ,YEZ !Y =Y  FYDY                 8X        "
4738       "   EU                                                    :T        %W .X                                       "
4739       "                                                                       9e   !f      KY   !Y     LY   \"a   :Y  "
4740       "<Y  NX 0X  >Y                                 E^ /X 0_          %f            1]                   'c          "
4741       "                                        @ZEZ                          AY                                     MV"
4742       "CW                                          <R              4a .Y  >X *^ +]DU 7ZEZ 5U>U      JY  ?Y *^ -YEZ 4Y "
4743       " ?Y *^ .ZEZ 5[  ]DU 5Y  >Y +^ ,]DU 6ZEZ      Y  ?Y +_ .ZEZ \"Y <Y  FYEZ                 :[           FU        "
4744       "                                 7Y          -T 7W#W <Y    9X -W  DU             KY    HZ \"\\      4Z    M[ \""
4745       "Y             LZ        +\\        8]                 >Z    G[    G\\                 @e   !f      JX   !Y     "
4746       "LY   %d   :Y  <Y  NX 0X  >Y                                 Ha /X 0b          *j    L]        D_               "
4747       "    +g                 A[                      LY        8Z -ZEZ   \"Y          1o )V    FX  NZ  FY            "
4748       "%Y    ,X  NX*Z NW              3WEW    H\\                       #[ !Z \"[ \"[ \"[    G[7T              8g 0Y  "
4749       "@Y +_ ,_FV 7ZEZ 5U>U      IY  @Y +` .YEZ 3X  ?X *` /ZEZ 4[:P 8_FV 4X  ?Y +` ._EU 6ZEZ      NX  @Y *_ .ZEZ #Y ;Y"
4750       "  FYEZ                 ;]           GU                                         <b          1T :]'X @b    >W ,X "
4751       " FV             a   \"d -g      >d   (d +b            %b        4f        Bg                 Ie   \"e   \"h    "
4752       "             Ge   !f      IX   \"Y     LY   &e   :Y  <Y  NX 0X  >Y                                 Jc /X 0c    "
4753       "      -n   $g        I`                   .j        >a        ;e    HU        .U        +b        Ac 2ZEZ   'b "
4754       "         5o -]    Na (c  KY          .Y #_   8Y!W'Y\"X.c$X              3XGX    Mf                       -e +d "
4755       ",e ,e ,e   \"e=V              ;k 1Y  BY +XNW .aGV 7ZEZ 5V@V      HX  AY +XNW .YEZ 3Y  AY *WNW /ZEZ 4\\>T 9`GV 3"
4756       "X  AY +XNW .`GV 6ZEZ      NY  AX *XNW /ZEZ $Y :Y  FYEZ                 <_           IU   (Q  LZ 4Z2Z 1Q        "
4757       "                      &g   %Z +XCX    MT <a)W Ah $X  HX +X  GV           GX 3e )_ /j 4n  L] ?y /i C~S =i 0g    "
4758       "        +g    L\\ 8t (m Ks 2~R E} <o HZ(Z :Z \"Z 4Z,] LZ 2_'_(^-Z Ck :q 0k ?q *n J~d'Z(Z*Z LZ=Z.\\.Z7Z(Z([$Z'~^"
4759       " @e 3X  Ff )\\    MY   #Y     LY   (g   :Y  <Y  NX 0X  >Y                                 Kd /X 0e          0p "
4760       "  (m        Lb                   1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik    LW    DX    DW        /i   ?Y(Y   4h 5ZEZ"
4761       " ,\\ ,h        7\\ -o .`   $f -h  NY    No     %_ %c   @_\"X-_\"W0h&W   .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S "
4762       "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k   (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ  Im 1X  CY *WMX /bHV 7ZEZ 5V@V      G"
4763       "X  CY *WLW /YEZ 2Y  CY *WLW 0ZEZ 3[AW :bHV 3Y  BX *WLW 0bHV 6ZEZ      MY  CX *XMX 0ZEZ $X 9Y  FYEZ             "
4764       "    =a M~i        7U   (Q  N_ 9_8_ 3R                              )k   'Z +XCX +X@X 4T >e,X Cl &X  IX *X  GV  "
4765       "         GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j            /l    N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z"
4766       " Ep =t 5o Au 1u N~d'Z(Z)Z MZ<Z/\\/Z5Z*['[&Z&~^ @e 3X  Ff )]    MY   $Y     LY   )h   :Y  <Y  NX 0X  >Y         "
4767       "                        Le /X 0e          1r   +r        c                   3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko "
4768       "   Y    EX    EY        2m   @Y)Y   6l 7ZEZ 0e 2k        >e 1o 0c   'j /i  X   !r     (b 'g   Eb\"W0c#X0i(W   -"
4769       "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p   ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ  Kp"
4770       " 1X  DX *WKW /WMYJV 6ZEZ 5V@V      GY  EY *WKX 0YEZ 1Y  EY *XKW 1ZEZ 2[EZ :WMZKV 1Y  DX *WKX 1WLYKW 6ZEZ      L"
4771       "Y  EY *WKW 0ZEZ %X 8Y  FYEZ                 >c M~h        7T   (S !a <b:b 6S          %|                  $o   "
4772       ")Z +XCX +W?W 3T ?g.X Dp (X  IX )X  HV           HY 6l 7i 5t <v #_ ?y 3p F~S Aq 8n            3p (Y $^ 9z 2v!{ :"
4773       "~R E} Az NZ(Z :Z \"Z 4Z.] JZ 2`)`(_.Z Gt ?w :s Cx 5x!~d'Z(Z)Z N[<Z/\\/Z5[,[%Z'[&~^ @e 2X  Gf *_    MX   $Y     "
4774       "LY   )h   :Y  <Y  NX 0X  >Y                 >X               8f /X 0f          3t   -s        c                "
4775       "   4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms   #[    FX    F[        4n   @Y*Y   6m 7ZEZ 3k 5l        Bk 4o 1f   )k 0k #"
4776       "X   #u     (b (i   Fb#X0c#W/k+X   .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t "
4777       "  0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ  Ls 2X  FX *WIW 1WJc 6ZEZ 4VBV      EY  FX *XJW 0YEZ 0X  EX )WJW 1ZEZ 1[I^ <WJc 0"
4778       "X  EX )WJW 2WJZNW 5ZEZ      KX  FY *WIW 1ZEZ &X 7Y  FYEZ                 ?d M~h        8U   )T #e ?d=e 8U      "
4779       "    *~Q                  &r   *Z +XCX +W?W 3T @i/W Dq (X  JX (X  HV           HX 7o <m 7x >x %_ ?y 5r F~S Ct :p"
4780       "            6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ "
4781       "@e 2X  Gf +a    MX   %Y     LY   *i   :Y  <Y  NX 0X  >Y                 >Y               9f /X 0g          5v  "
4782       " 0u        d                   6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w   &]    GX    G]      6U &o   ?Y+Y 7X )n 7ZEZ "
4783       "6p 7m        Eo 6o 2h   *l 1l %X   #v     (b )k   Gb$X/c$X/l,W   -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z "
4784       "!Z \"Z :~ D_-Y Hw =v >w >w >w   4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ  Mt 1X  HX )WHW 2VHb 6ZEZ 4WDW      DX  GX )WHW 1YE"
4785       "Z /X  GX )WHW 2ZEZ 0[M` ;VHb /X  GY *WHW 3VHb 5ZEZ      JX  GX )WHW 2ZEZ 'Y 7Y  FYEZ                 ?e M~f    "
4786       "    7U   )U %g Bh@g :W          .~T                  't   +Z +XCX ,X@X 3T Ak1X Er (X  JX 'X  IV           HX 8q"
4787       " =m 7y ?y '` ?y 6s F~S Dv <r            8u 4m /_ 9~ :~%~Q ?~R E} D~Q\"Z(Z :Z \"Z 4Z0\\ GZ 2`*a(`/Z Jz Bz Az F{ "
4788       ";{!~d'Z(Z(Z Z;Z0^0Z3Z.[#[*Z$~^ @X %X  :Y ,c    MX   &Y     LY   +^   .Y  <Y  NX 0X  >Y                 >Y      "
4789       "         :] %X &]          5]C\\   1v        Nc                   7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y   (_    H"
4790       "X    H_      7U 'p   ?Y,Y 6X *o 7ZEZ 8t 9YH]        Ht 9o 3i   *XG[ 1VE[ &Y   %x     (b *[I[   Hb$W.c%X.VE[-X  "
4791       " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az   7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ  Na"
4792       "J_ 2X  IX )WGW 2VG` 5ZEZ 4XFX      CX  IX )WFW 2YEZ .X  IX )WFW 3ZEZ /j 8VG` -X  HX *WFW 4VG` 4ZEZ      IX  IX "
4793       ")WGW 2ZEZ 'X 6Y  FYEZ                 ?XKX M~f        7T   )W 'i DiAi ;X          1~V                  (w   -Z "
4794       "+XCX ,X@X 3T AZI[2W Es (X  KX &X  IV           HX 9s >m 7z @z )a ?y 7t F~R Dx >t            9v 8s 2` :~P <~Q&~S"
4795       " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X  ;Y -e    MX   'Y     "
4796       "LY   +[   +Y  <Y  NX 0X  >Y                 >Y               :[ #X #Z          6\\?[   2v        F\\           "
4797       "        8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#}   +`    HX    Ia      8U (q   >Y-Y 6X +p 7ZEZ 9bMb ;U@Y        JbMb :"
4798       "n 3ZIZ   +T@Y 2R>Y 'X   %y     (XLV +ZEZ   IXMW%X.YMW%W-R>Y.W   -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z "
4799       "!Z \"Z :~S Ha/Y K| B| C| D} D|   9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ  N]B\\ 2X  JX *WEW 3UE_ 5ZEZ 3YJY      AX  JW )WE"
4800       "W 2YEZ -X  KX (WFW 3ZEZ .f 5UE_ ,X  JX )WFW 4VF_ 4ZEZ      HX  KX )WEW 3ZEZ (X 5Y  FYEZ                 @YJW M~"
4801       "e        7U   *X (j EkCk =Y          3~X                  )x   -Z +XCX ,W?X 3T BYEY3X Ft (X  KX %X  JV         "
4802       "  IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v            :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D"
4803       "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X  ;Y .g    MW   'Y     LY   +Y   )Y  <Y  NX 0X  >Y               "
4804       "  >Y               :Z \"X \"Z          7[=Z   3aE[        E[                   9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$"
4805       "~P   -b    IX    Jc      9U )r   >Y.Y 5X ,]DX 7ZEZ ;\\>\\ <R;X        M]>\\   0XDX   ,R=Y  MX (X   %hEW     (SG"
4806       "V ,YAY   JSHW%W-SGW&X GX/W   ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P"
4807       "   <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ  \\>Z 1X  LX )VCW 4UD] 4ZEZ 2f      ?X  LX )WDW 3YEZ ,W  KX )WDW 4ZEZ -b 2UD] *W"
4808       "  KX )WDW 5UD] 3ZEZ      GW  LX (VCW 4ZEZ )X 4Y  FYEZ                 @XIX M~d        7U   *Y *l GmDl ?[       "
4809       "   6~Z                  *`C\\   -Z +XCX ,W?W 2T CYCY5X E]CZ (X  LX $X  JV           IX 9]E^ @m 7aGb B^Ec ,b ?y "
4810       "9aF[ F~R E_C_ B_E^            ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa"
4811       "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X  ;Y /i    MW   (Y     LY   ,Y   (Y  <Y  NX 0X  >Y                 >Y    "
4812       "           :Y !X !Y          8[;Z 1\\ 0\\:U        D[                   ;Z<Z 4b 8~S E~R M~R 4Z :~]+[;Z;Z%bCb   "
4813       "/d    JX    Ke      :U )]BW   =Y/Y 5X ,[?U   3Z8[ &W        NZ7Z   2XBW    EX  LW )X   %iEW      KV -Y?Y   @W&X"
4814       "!W&W EW0X   -b )a (b )a 'a )a 5~d)dCb N~S I~S H~R H~R 6Z !Z !Z \"Z :~V Kb0Y MbCb HbCb HbCb HbCb HbCb   >bCh%Z(Z"
4815       "#Z(Z$Z(Z$Y'Y![.Z HZ  Z;Z 1X  NX )WBV 5VBZ   $e      >W  MX )WBW   !X  MX )WBW   #` /UBZ (W  MX )WBW 6UBZ       "
4816       " 9X  MW (WCW    MX 3Y                    GXHW M~d        8U   *[ +m HnFn A]          9~\\                  +^=Y"
4817       "   -Z +XCX -X@X 2U DXAX5W E\\=V (X  LX #X .R@V?Q          ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@]     "
4818       "       <Z@^ @~P 9b ;Z=d Aa;^'Z>j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`"
4819       "2Z0[4[ LZ/[\"~^ @X #X  <Y 0\\N]    NX   )Y     LY   ,Y   (Y  ;X  NX 0X  >Y                 >Y               ;Z "
4820       "!X !Y          8Z9Y 6d 4[5R        CZ                   ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=`   1f    KX    Lg "
4821       "     ;U *\\=T   =Y0Y 4X ,Z;R   5Z3Y &W       !Y3Y   3W@W    EW  LX *W   %jEW      KV -X=X   @W'X W'X EX1W   ,b "
4822       "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=`   @`=e%Z(Z#Z(Z$Z(Z$Y'Y"
4823       " Z/[ HZ !Z9Y 0W  X )WAW 6VAW   \"d      <W  X (VAW    X  X (V@V   &a .VAW &X  NW (V@V 6UAW        6X  X )WAW   "
4824       " NW 2Y         N\\ #[ \"\\ #\\ #[  MXHW L~b        7U   +\\ ,n IoGp C_          ;~]                  ,]:X   -Z "
4825       "+XCX -X@X 8c LX@X7X E[:T (X  MX \"X /TAVAT          .X :\\?\\ Am 7Y9] CT4] .c ?Y  J]8S  Z E\\;\\ E]=[          "
4826       "  <W;\\ B~T ;b ;Z7_ C_5['Z7e GZ  MZ '`3[$Z(Z :Z \"Z 4Z6] BZ 2b-b(b1Z!_8^ GZ;` K_9_ LZ:` D]5W 3Y 9Z(Z&Z$Z7Z3`3Z."
4827       "Z4Z JZ0Z  \\ ?X #X  <Y 1\\L]    NX   *Y     LY   ,Y   (Y      8X  >Y                 >Y               ;Y  X !Y "
4828       "         8Y8Y 6f 6Z2P        BY                   <Z9Z 7c 7\\  Z (`;` >j BZ(Z+[;Z;Z'_9_   3h    LX    Mi      <"
4829       "U *[:R   <Y2Z 4X -Z8P   6Y/X 'W       #Y/Y   6W>V    EW  KW +W   %kEW      KV .X;W   @W'W NW(X CW2X   -c *c )b "
4830       "*c )c +c 7ZHZ 2_5[ NZ !Z  Z !Z  >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_   B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z"
4831       "8Y 0W !W (V?W      I`      :X !W (V?W    X \"X (W@W   *d    EX !W (W@W          0X \"X (V?W   !W 1Y        #d ,"
4832       "e +d +d ,e #XHW LZ#Z        7U   +] -o KqHp C_          <c                   2]7V   -Z +XCX -W?X <l#X?X7W E[7R "
4833       "(X  MX \"Y 0VCVCV          .X :[<[ B\\IZ 7V5] DQ0] 0XNZ ?Y  K\\4Q !Z E\\9\\ F\\;[            =U8[ DdAc =d <Z5^ "
4834       "E^1Y'Z3b HZ  MZ (_/Y$Z(Z :Z \"Z 4Z7] AZ 2c/c(c2Z!]4] HZ9^ L^5^ MZ8^ E\\0T 3Y 9Z(Z&Z%Z6Z3`3Z-Z6[ J[2Z  \\ >X #X "
4835       " <Y 2\\J]    NW   *Y     LY   ,X   'Y      8X  >Y                 >Y               ;Y  X  X          9Z7X 6g 7Y"
4836       "        #Z                   =Y8Z 7d 7[  Z )_7_ Bp EZ(Z+[;Z;Z(^5^   5j    MX    Nk      =U +[7P   <Z3Y 3X -Y   "
4837       " MX+W 'V       $X+X   7V=W    FW  KW ,W   $kEW      KV .X;X   AW(X NW(W BW2W   ,d +c *d +c *d +c 7ZHZ 3^0X NZ !"
4838       "Z  Z !Z  >Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5]   C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V      H^"
4839       "      8X #W (W>V    NW \"W (W>W   .h    EW \"X )W>W          0W #X (V=V   \"W 0Y        &j 1i 0j 1j 1i &X <Z#Y "
4840       "       7U   +_ /p KrJr Ea          >`                   .\\5U   -Z +XCX -W?W =r'X>W8X EZ  ;X  NY !X 1XDVDX 2X  "
4841       "      &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y  L\\  2Z E[7[ G\\9[            >S5[ F`7` ?YNY <Z3\\ F]-W'Z0` IZ  MZ )^+W$"
4842       "Z(Z :Z \"Z 4Z8] @Z 2YNX/XNY(c2Z\"]2] IZ7] N]2] MZ6] G\\-R 3Y 9Z(Z&[&Z6Z4XNW3Z-[8[ HZ3[ !\\ =X #X  <Y 3\\H]    N"
4843       "W   +Y     LY   ,X   'Y      8X  >Y                 >Y               ;Y  X  Y          :Y6Y 7i 9Y        \"Y   "
4844       "                >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2]   6l    NX    m      >U +Z   !Y4Z 3X -Y    NW(W (W      "
4845       " &X)X   8V<V +X  DW  LW ,W   $lEW      KV .W9W   AW(W MW)X CW2W   +YNY ,YNZ +YNY ,ZNY +YNY +YNY 9ZGZ 4^.W NZ !Z"
4846       "  Z !Z  >Z !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2]   E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'V<V      GZ   "
4847       "   5W $W 'V<V    NW $W 'V<V   2m    EW #W (V<V          /W $W (W=W   #W 0Y        (n 6o 5n 5n 6n (X ;Z%Z       "
4848       " 7U   ,a 0q LrJr Fc          A_                   ,\\2S   -Z +XCX .X@X ?u(W=X:X DY  :X  NX  Y 2ZFVFZ 2X        "
4849       "'X :Z9[ CR?Z 7R/\\ \"[ 1XMZ ?Y  L[  2[ F[5Z G[7Z            >R4[ G^1^ AZNY <Z2[ G]*U'Z.^ IZ  MZ )](U$Z(Z :Z \"Z"
4850       " 4Z9] ?Z 2YNX0YNY(d3Z#]0] JZ6\\ N\\/\\ NZ5\\ G[  <Y 9Z(Z%Z&Z6Z4XNX4Z,Z8Z FZ4Z  [ <X \"X  =Y 4\\F]       #Y     "
4851       "LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          :Y6Y 7j :Y        \"Y              "
4852       "     >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\   8n    X   !o      ?U ,[    Y5Y 2X -Y    W&W )W       'W%W   9V"
4853       "<V +X  DW  LW     )mEW      KV /X9X   BW)X MW)W BW3X   ,YMY ,YMY ,ZNZ -YMY +YNZ -YMY 9ZGZ 5]*U NZ !Z  Z !Z  >Z "
4854       "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0]   F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X            +P                "
4855       "       %_K[                              CY        *r 9q 8r 9r 9q *X ;Z%Z      >Q  JT   ,b 0q MsKs Ge          "
4856       "C^                   *[0R   -Z +XCX .X@X @v)X=X:W CY  :X  Y  NX 1[HVH[ 1X        'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y  M["
4857       "  1Z EZ4[ I[5Z            ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ  MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5"
4858       "\\!\\-\\ Z4[ GZ  ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z  [ ;X \"X  =Y 5\\C[       #Y     LY   -Y   'Y      8X  >Y        "
4859       "         >Y               ;Y  X  Y          :Y6Y 7k ;Y        \"Z                   @Z5Y 9YLY 5[ #Z +\\.] J| KZ"
4860       "(Z+[;Z;Z*\\-\\   :p   !X   \"q      @U ,Z    NY6Y 1X -X    W#V *W       (W#W   :U;V +X  DW  LW     )mEW      KV"
4861       " /X9X   BW*X LW*X BW3W   +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z  Z !Z  >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\"
4862       "#\\-\\#\\-\\#\\-\\   H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y                                    /[G[               "
4863       "               DY        +u =u <u ;u =u ,X :Y&Z      >S  LU   ,c 1q MtLt Hf          E]                   )[.Q "
4864       "  -Z +XCX .W?X Bx)X=X;X DZ  :X  X  MY 0ZIVIZ /X        'X ;Z7[ 1Z  AZ ![ 4XKZ ?Y  MZ  0Z EZ3Z I[5Z             "
4865       "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ  MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[  ;Y 9Z(Z$Z"
4866       "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X  =Y 6\\A[       $Y     LY   -Y   'Y      8X  >Y                 >Y              "
4867       " ;Y  X  Y          :Y6Y 7l <Y        !Y                   @Y4Z :YLY 4[ $Z ,\\,] M~Q MZ(Z+[;Z;Z+\\+\\   <r   \"X"
4868       "   #s      AU ,Z    MY7Y 1X -Y   \"W!V :f       (V!W   ;U;V +X  EX  MW     (mEW      KV /W7W   BW*W KW+X BW3X  "
4869       " +YLY .YKY -YLY .YKY -YLY .ZLY ;ZFZ 6\\%R NZ !Z  Z !Z  >Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP"
4870       " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X                                    /XC[                              EY "
4871       "       -x @x >x ?x @x -X :Z'Z      ?U  MU   -e 2q MtLt Ig          E[                   'Z,P   -Z +XCX .W?W By)"
4872       "X<W;W CZ  :X  X  MY .ZKVKZ -X        (Y <Z5Z 1Z  A[ !Z 4XKZ ?Y  N[  1Z DZ3Z IZ3Y             NY K\\%[ EYKZ >Z0Z"
4873       " J\\#Q'Z*\\ KZ  MZ +[ Q$Z(Z :Z \"Z 4Z<] <Z 2YMY3XLY(YMZ5Z%\\*\\ LZ4[\"[*\\!Z3[ IZ  :Y 9Z(Z$Z)[4Z6XLW5Z)Z<Z BZ8Z"
4874       " !\\ :X !X  >Y 7[>[       %Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y"
4875       "5Y 7UH_ <Z        \"Z                   AY3Y ;YKZ 4[ %Z ,[*\\ N~S NZ(Z+[;Z;Z+[*\\   =\\NXM[   #X   $\\MXN\\    "
4876       "  BU ,Z  *P DY8Y 0X -Y   #W NV @k       )V NV   <V;V +X  EW  NY     )nEW      KV /W7W   BW+X KW+W CY4X   +YKZ /"
4877       "YKY .ZLZ /YKY .ZKY /YKY <ZEZ 7\\#Q NZ !Z  Z !Z  >Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z"
4878       "(Z$Z(Z$Y'Y K[9[ Ct =Y3X                                    /U@[                 \"Q            EY        .z B{ "
4879       "B{ Az B{ /X :Z'Y      >V  U   -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[                   '[    8Z +XCX /X@X C`MTL_)W;"
4880       "W<X CY  9X !Y  LX ,ZMVMZ +X        (X ;Z5Z 1Z  A[ !Z 5XJZ ?Y  NZ  0Z DY2Z J[3Z      )Q   Q   JZ M[!Z FYJY >Z0Z "
4881       "J[ 'Z)\\ LZ  MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ  :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !"
4882       "\\ 9X !X  >Y 8[<[       &Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y5Y "
4883       "7RB] =\\        $Z                   BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\   ?\\MXL[   $X   %\\LXM\\      CU"
4884       " ,Y *Q\"R DY9Y 0X -Y   #V=_?V Cm       *V LV   <U;V +X  FX \"[     (nEW      KV /W7W   BW+W JW,X F[3W   *YJY 0Z"
4885       "KZ /YJY /YKZ /YJY /YJY =ZEZ 7[!P NZ !Z  Z !Z  >Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z("
4886       "Z$Z(Z$Y'Y J[:Z Bw @Y6[                                    .Q<[                 #S            GY        /`Da E`C"
4887       "` DaD` C`Da E`C` 0X 9Y(Z      ?X !U   .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[                   &Z    7Z +XCX /X@X C\\"
4888       "ITFY)W;W=X BY  9X !X  KY +YNVNZ *X        (X ;Z4Z 2Z  @Z !Z 6YJZ ?Y  Z  /Z DY2Z JZ1Y      ,T   T   MZ N[ NZ HZJ"
4889       "Y >Z0Z K[ &Z(\\ MZ  MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ  :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;"
4890       "[ ![ 8X !X  >Y 9[:[       'Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y"
4891       "5Y %\\ =]        %Y                   BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[   @\\LXK[   %X   &\\KXL\\     "
4892       " DU -Z +S$T EY:Y /X -Z   %V?fBU Eo       +VEg=V   =V<V +X  GX *b     &nEW      KV /W7W   BW,X JW,W Nb2X   +ZJY "
4893       "0YIY /YJY 0YIY /YJZ 1YIY =ZEZ 8\\  NZ !Z  Z !Z  >Z !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$"
4894       "Z(Z$Y'Y IZ;Z Ay BY9^                                     G[                 %U            HY        0]<^ G^=^ F"
4895       "^<] E]<^ G^=^ 1X 9Z)Z      @Z \"U   .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[                   &[    7Z +XCX /W?X D[GTC"
4896       "V)W;W=W AZ  :X \"Y  KY *j (X        (X <Z3Z 2Z  @Z !Z 6XIZ ?Y  Z  0Z DZ2Z JZ1Z      0W   V   Y NZ KZ IYIZ ?Z0Z "
4897       "K[ &Z(\\ MZ  MZ -[  Z(Z :Z \"Z 4Z?\\ 8Z 2YKX5XKY(YLZ6Z&[&[ MZ3[%[&\\#Z2[ JZ  :Y 9Z(Z#Z+Z1Z7WJW7Z&Z@Z >Z<Z ![ 7X"
4898       "  X  ?Y :[8[     \"\\ 3YBZ  \\ ,ZAY 4\\ &Y \"Z 0YAZ     \"X  >Y .Y3Y 3Z '\\  MZ )Z  ;Z 2^ +Y               ;Y  "
4899       "X  Y        6Y /Y5Y $[ =`  G^ !Z    IZ             M\\     #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[   B\\KXJ["
4900       "   &X   '\\JXK\\      H\\ 1Z ,U&V EY;Y /X ,Z   'V@jDV Gp       +UDj?V   >V<V +X  GW )`     $nEW      KV /W7W   "
4901       "BW-X IW-X N`0W   *YIZ 1YIY 0YHY 1YIY 0ZIY 1YIZ ?ZDZ 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)"
4902       "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[          \"[ &Z &[ !["
4903       " #\\ #[ ![    G[@W            IYBZ        J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z      @Z \"U   .k 5q N~o Mm 4l =X"
4904       ".X 7^ 7Z3Z NZ                   %Z    6Z +XCX /W?W D[FT@S)W;W>X AZ  :X \"Y  JX (f &X        )X ;Z3Z 2Z  @Z !Z 7"
4905       "XHZ ?Y !Z  /Z CY1Y JZ1Z      2Y   Y  $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ  MZ -[  Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'["
4906       "$[ NZ2Z%[%[#Z2[ JZ  :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X  X  ?Y ;[6[     (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X  NX "
4907       "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y  X  Y        9_>W KY5Y #[ =c  h >XD` "
4908       "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[   C\\JXI[   'X   (\\IXJ\\  "
4909       " (Y  d 5Z -W(X FY<Y .X ,[   (UAmDV Iq       ,VDl@U   >V=W +X  HX )^   ,Y1Y HnEW      KV 0X7W   BW-W HW.X M^/X )"
4910       "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#"
4911       "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y  NX  NX  X  E[ >XD` -c )c *b *c )c '\\ &bDX L"
4912       "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y     8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z      AZ !U   /m 6q N~o No 6o ?X.X 8_ "
4913       "6Y3Z Z                   $Z    6Z +XCX 0X@X DZET>Q)W;W>W ?Y  :X \"X  IY 'b $X        )X ;Z2Y 2Z  @Z !Z 8YHZ ?Y "
4914       "!Z  0[ CY1Y JZ1Z      5\\   \\  'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ  MZ .[  NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ"
4915       "2Z&[#Z#Z2[ JZ  :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ <Z?[ \"\\ 6X  X  ?Y <[4[     -l :YGd ,k 9eGY :h 5r 8eGY GYGe +Y  NX 0X3"
4916       "\\ 8Y FYGd=c!YGe 2h ;YGd 3eGX ;YG` 9m :t BY1Y LZ+Z+Y7[7Y*[1Z MY+Z J~ 2Y  X  Y        <eAW KY5Y \"Z <f 'o CYFd D"
4917       "Y(Y 5Y 6Y1Y MY'Z CUE\\ B~Y!Y@X@Y =h 0z\"~W KY0Y >ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z   C[IXH[   (X   ([HXI[   ("
4918       "Z $k 9Z .Y*Z FY=Y .X ,\\   *UAnCU J^CW       -VCmAV   ?W>V *X  IX (a   /Y1Y HnEW      KV 0X7W   BW.X HW.W La3X "
4919       "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z("
4920       "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y  Y  NX  Y  E[ ?XFd 1g .h /h /h /h )\\ )hHX "
4921       "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y     9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y      A[ !T   /n 6q N~o q 8q @X.X 8` 7"
4922       "Y3Y Z                   $Z    5Z +XCX 0X@X DYDT EW;W?X ?Y  :X #Y  IY %^ \"X        )X <Z1Z 3Z  @Z !Z 8XGZ ?Y !Z"
4923       "  0Z BY2Z JY0Z      8_   _  *Z!Y DX LYFY @Z/Y M[ $Z&[ NZ  MZ .[  NZ(Z :Z \"Z 4ZB\\ 5Z 2YJX7XJY(YJZ8Z([#[ NZ2Z&["
4924       "#[$Z2[ JZ  :Y 9Z(Z\"Z-Z/Z9XJX9Z#ZDZ :Z@Z \"\\ 5X  NX  @Y =[1Z     1q <YIh 0o =hHY <l 7r 9hIY GYHg ,Y  NX 0X4\\ "
4925       "7Y FYIg@g#YHh 6l =YIh 7hHX ;YHa ;q <t BY1Y KY+Y*Y8\\8Y([3[ MY+Y I~ 2Y  X  Y        =gCX KY6Z !Z <i -q CYHh F[*Y"
4926       " 5Z 7Y1Y NZ&Y EWG` D~Y!Y@X@Y >k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[   CYHXGY   'X   'YGXHY   'Z &o"
4927       " ;Z /[,[ FZ?Y -X +\\   +UBoBU LZ>W       -UBnAU   >W@W *X  JX 'c   1Y1Y HnEW      KV /W7W   BW.W GW/X Lc5W 'Y ,"
4928       "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#"
4929       "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y  Y  NX  Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0"
4930       "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z     :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z      AZ !U   /o 7p M~n s :s AX.X 8` 7Z4Y Y"
4931       "                   #Z    5Z +XCX 0W?X EYCT EW;W@X >Z  ;X #Y  HX #Z  X        *X ;Z1Z 3Z  @Z !Z 9XFZ ?Y \"Z  /Z "
4932       "BY2Z KZ0[      <b   a  -[\"Y BX MYFY @Z0Z M[ $Z%[ Z  MZ .Z  MZ(Z :Z \"Z 4ZD] 4Z 2YJX7XJY(YJZ8Z([\"[ Z2Z&Z\"[$Z2"
4933       "[ JZ  :Y 9Z(Z!Z.Z/Z9WHW9Z\"ZF[ :[BZ \"\\ 4X  NX  @Y >[/Z     4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y  NX 0X5\\ 6Y FY"
4934       "JiBi$YJk 8o ?YJj 9kJX ;YJc <r <t BY1Y KZ-Z)X8\\8Y'Z4[ LZ,Y I~ 2Y  X  Y        ?jDX KY6Y  Z ;k 1r CYIj G]-Z 5Z 7"
4935       "Y1Y NZ&Z HYHb E~Y!Y@X@Y @n 8~P\"~W KY0Z ?YFY 0[ +Z 0[!Z)]BZB]&Z(Z+[;Z;Z.Z\"[ LQ  GWGXFW  HQ /X /Q*Q @WFXGW   &Z"
4936       " (q ;Z .[BVB[ DY@Z -X *]   .UC^EXBU LX<W       .VBWC[AU   ?WAW )X  KX %c   2Y1Y HnEW      KV /W7W   BW/X GW/W J"
4937       "c7X 'Y ,YFY 4YFZ 3YFY 4YEY 3YFY 4ZFY AYBZ :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\"
4938       "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t <u ;t ;t ;t tDn Gr <o 6o 6o 6o ,Y  Y  NX  Y &l @XIj 8o 5o 6n 6o 5o"
4939       " -\\ ,nLW JY0X HY0X GX0X GX0Y KY,Y JYJj CY,Y     :ZBXBZ!Z+Z Z,Z Z,Z!Z+Z 6X 7Z-Z      BZ  U   0q 7o M~n s ;u BX."
4940       "X 9a 6Y5Z!Y                   \"Z    5Z +XCX C~d&YCT EW;W@W =[  <X #Y  HY $Z  X        *X ;Z1Z 3Z  @Z !Z :YFZ ?"
4941       "Y \"Z  0Z AZ3Z KZ0[ 5Z \"[  ?e   d  0Z\"Y AY YEZ AZ0Z MZ #Z%[ Z  MZ /[  MZ(Z :Z \"Z 4ZE] 3Z 2YJY9XIY(YIZ9Z(Z![ "
4942       "Z2Z'[!Z$Z2[ JZ  :Y 9Z(Z!Z/[/Z:XHW9Z\"[H[ 8ZC[ \"[ 3X  NX  @Y ?[-Z     5v ?YKm 6r ?mKY ?q 9r <mKY GYKm /Y  NX 0X"
4943       "6[ 4Y FYKkEl%YKm ;r @YKl ;mKX ;YKd >t <t BY1Y JY-Y(Y9]9Y&Z5Z JY-Y H~ 2Y  X  Y        @lFX JY6Y  NY 9k 4s CYJl H"
4944       "^.Y 4[ 8Y1Y NY$Y J[Ie G~Y!Y@X@Y Ap ;~R\"~W KY0Z @YEZ 0[ ,Z 0Z [*\\AZA\\&Z(Z+[;Z;Z/[![ NS  GUFXEU  HS 0X 0S,S @U"
4945       "EXFU   %Z )r ;Z -[G^G[ CZAY ,X )]   /UC[>TAU NX;W )P9P     =UAWAYAU   >XDX )X  LX  HY   3Y1Y HnEW      KV /W7W "
4946       "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-"
4947       "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u <u =v <v =u <u!uGr Js =r 9r 9r 9r .Y  Y  NX  Y (o AXJl :q "
4948       "7q 9r 9q 7q .\\ -y IY0X HY0X GX0X GX0Y KZ-Y JYKl DY-Z     ;ZAXAZ\"Y)Y!Z*Z\"Z*Z\"Y)Y 6X 7Z-Y      BZ  NT   0s 8o"
4949       " L~m!u =w CX.X 9b 7Y5Y Y                   \"Z    5Z +XCX C~d&YCT EX<WAX <Z  <X #X  GY &^ \"X        *X ;Z0Y 3Z"
4950       "  @Z !Y 9XEZ ?Y \"Z  0Z AZ3Y JZ/Z 5Z \"[  Ag   g  4[\"X ?X YDY AZ0Z MZ #Z%[ Z  MZ /[  MZ(Z :Z \"Z 4ZF] 2Z 2YIX9"
4951       "XIY(YIZ9Z(Z Z Z2Z'[![%Z2[ JZ  :Y 9Z(Z!Z/Z.Z:XHX:Z!ZHZ 6ZDZ \"\\ 3X  NY  AY @Z*Z     6w @YLo 9t @oLY At :r =oLY "
4952       "GYLo 0Y  NX 0X7[ 3Y FYLmGn&YLo =t AYLo >oLX ;YLe ?u <t BY1Y JY-Y(Y9]9X%[7Z IZ.Y H~ 2Y  X  Y        AnGX JY7Z  N"
4953       "Z 9k 6t CYKn I^/Z 5\\ 8Y1Y Z$Z L\\Jg H~Y!Y@X@Y Br =~S\"~W LZ/Y @YDY /[ -Z 0Z NZ+\\@Z@\\'Z(Z*Z;Z;Z/[![ U  GSEXDS"
4954       "  HU 1X 1U.U @SDXES   $Z +t ;Z ,[JbJ[ AYBY +X (^   2UCZ9QAU NW:W *Q:Q     >VAW?XAU   ?ZHY (X  MX  EX   4Y1Y HnE"
4955       "W      KV /W7W AQ:Q :W0W EW1X <X:X &Y -YDY 6ZEZ 5YDY 6ZEZ 5YDY 5YEZ CZBZ :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['YHZ"
4956       "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y  Y  N"
4957       "X  Y *r BXKn <s :t ;t ;s :t /\\ /{ IY0X HY0X GX0X GX0Y JY.Z JYLo FZ.Y     :Y@X?Y$Y'Y#YIP5PIY\"Y.PIY$Y'Y 7X 6Z/Z"
4958       "      CZ  NU   1u 8m K~m\"w ?^C] CX.X 9b 7Z6Y X                   \"Z    4Z +XCX C~d&XBT EX=XAW ;[  =X $Y  GY ("
4959       "b $X        +X :Y/Z 4Z  @Z \"Z :XDZ ?Y \"Y  0[ @Y4Z JZ/Z 5Z \"[  Dj   j  8[\"X =X\"ZDY AZ0Z N[ #Z$[!Z  MZ /Z  L"
4960       "Z(Z :Z \"Z 4ZG] 1Z 2YIX:YIY(YHZ:Z)[ [!Z2Z'Z [%Z2[ J[  ;Y 9Z(Z Z0Z-Z;XHX;Z NZJ[ 6[FZ \"\\ 2X  MX  AY AZ(Z     7x"
4961       " AYMq ;u AqMY Bv ;r >qMY GYMp 0Y  NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u <t BY1Y JZ/Z(Y:^:Y$[9[ HY/Z H~ 2Y "
4962       " X  Y        BpHX JY7Z  MY ;o 9u CYLp J_0Y 4\\ 8Y1Y Y#Z M]Jh I~Y!Y@X@Y Ct ?~T\"~W LZ/Y AZDY .[ .Z 1[ NZ+[?Z?['Z"
4963       "(Z*Z;Z;Z/Z NZ!W  GQDXCQ  HW 2X 2W0W @QCXDQ   #Z ,u ;Z +[MfM[ ?YCY +X '_   4UDZ'U W:W +R;R     >U@W?XAU   >j (X "
4964       " NX  CX   5Y1Y HnEW      KV /W7W AR;R ;W1X EW1W :X<X %Y .ZDY 6YCY 5YDZ 7YCY 5YDZ 7ZDY DZAZ ;[  JZ !Z  Z !Z  >Z "
4965       "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ <Z7] IYBY 5w >w ?x >x ?w >w#wKv Nu ?v"
4966       " =v =v =v 0Y  Y  NX  Y +s BXLp >u <v =v =u <v 0\\ 0{ HY0X HY0X GX0X GX0Y JZ/Y IYMp EY.Y     ;Y?X?Y%Y%Y$YJR7RIY$"
4967       "Y.RJY%Y%Y 8X 6Z/Y      CZ  MU   2v 8m K~m#y @[>\\ DX.X :c 7Z7Z!Y                   \"Z    4Z +XCX C~d&XBT DW=XB"
4968       "X :[  >X $Y  FY +f &X        +X ;Z/Z 4Z  AZ !Z ;YDZ ?YFP -Z?Q  BZ ?Z5Z JZ/Z 5Z \"[  Gj   Ii  ;[\"X1Q,W\"YCZ BZ1"
4969       "Z MZ \"Z$[!Z  MZ /Z  LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[  ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\"
4970       " 1X  MX  AY BZ&Z     8^Ga AYN[H_ <cI\\ B`I[MY CaH_ <r ?`H[NY GYNr 1Y  NX 0X9[ 1Y FYNqJp'YMq @aJa CYN[H_ A`I[MX "
4971       ";YNg @`E[ <t BY1Y IY/Y&X:^:Y#Z:[ GY/Y G~ 2Y  X  Y      JW5V B`M_JX IY8Z  LY =r ;cL_ CYM^Na J`1Y 5^ 9Y1Y!Z\"Z ^K"
4972       "j J~Y!Y@X@Y D_I` A~U\"~W LY.Y AYCZ .[ /Z 1Z MZ,\\?Z?\\(Z(Z*Z;Z<[/Z NZ\"Y  ;X  ;Y 3X 3Y2Y 3X    EZ -hM[ ;Z *~Q >"
4973       "YDY *X )b   6UDY%U V9W ,S<S     >U@W>W@T   =h 'X  X  AW   5Y1Y HnEW      KV /X9X AS<S <W1W DW2X 9W<W $Y .YCZ 7Y"
4974       "CY 6YBY 7YCY 6ZCY 7YCZ EZAZ ;[  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z("
4975       "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y  Y  NX  Y ,u CXM^Nb"
4976       " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z     <Y>X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0"
4977       "Z      CZ ;P4U   1w 9l J~m#z B[;[ EX.X :d 7Y7Y X                   )~Q   #Z +XCX C~d&XBT DW=XCX 9\\  ?X $Y  FY "
4978       "-j (X        +X ;Z/Z 4Z  AZ \"Z :XCZ ?YM_ 5ZE^  IZ >Y6Z IZ0[ 5Z \"[  Jj   Ci  ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z  "
4979       "MZ 0[  LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[  <Y 9Z(Z NZ2Z,Z<XFW;Z MZLZ 2ZHZ #\\ 0X  MX  AY C"
4980       "Z$Z     9Y>^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y  NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\"
4981       ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y  X  Y      M];\\ F]E[JX IY9[  LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^"
4982       " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[  <X  <[ 4X 4[4[ 4X    EZ ._KUHV ;Z )~ <Y"
4983       "EY *X *e   8UDY$T!W:X .U=T     ?U?W>W@U   =f &X !X  @W   5Y1Y HnEW      KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC"
4984       "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z"
4985       "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y  Y  NX  Y -w DXNY"
4986       "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y     ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1"
4987       "Z      DZ =S4U   2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y                   *~R   #Z +XCX C~d'YBT DX?XBW 7\\  @X $Y  FY "
4988       "/ZNVNZ *X        ,X :Z/Z 4Z  AZ #Z :XBZ ?o 9ZGc  MZ =Z8[ HY0\\ 6Z \"[  Li   >j  C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!"
4989       "Z  MZ 0[  LZ(Z :Z \"Z 4ZJ] .Z 2YHX<YHY(YFY;Z)Z MZ!Z3[([ N[&Z3[ H]  >Y 9Z(Z NZ2Z,Z<XFX<Z LZN[ 2[JZ \"[ /X  LX  B"
4990       "Y DZ\"Z     :U7\\ Ca>\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y  NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6"
4991       "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y  X  Y      `>` I\\B[KX IY:\\  LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L"
4992       "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\  <X  <\\ 5X 5\\4\\ 5X    EZ /^IUFT ;Z ("
4993       "| ;YFY )X +h   :TDY#U\"W:X /V?V     ?U?W>XAU   <c $X \"X  ?X   6Y1Y HnEW      KV .W9W @U>V ?W3X CW3X 8X>W #Y /Z"
4994       "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*"
4995       "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y  Y  "
4996       "NX  Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z     <X<X<X(X!X'XJX=XJY(X.X"
4997       "JX(X!X 9W 4Z1Y     >~d W5T   2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X                   )~R   #Z   0~d&XBT DX?XCX 6\\   "
4998       " =Y  EY 0ZMVMZ +X        ,X :Z/Z 4Z  B[ %\\ :XBZ ?q ;YHg  Z <Z:[ GZ1\\ 6Z \"[  i M~c Nj  G\\!W9eIVBX%Y@Y CZ3[ M"
4999       "[ \"Z#Z!Z  MZ 0Z  KZ(Z :Z \"Z 4ZK] -Z 2YGX=XGY(YFZ<Z*[ MZ!Z3[(Z M[&Z3[ H^  ?Y 9Z(Z NZ3Z*Z=XFX=Z Kf 0[L[ #\\ /X "
5000       " LX  BY        JS4[ C`<\\ A\\5Q D[;` E[9Z 5Y 1\\<` G`<Z 2Y  NX 0X=\\ .Y F_=[MV=[)`<[ D\\<\\ E`<[ E[;_ ;` 0Z3Q 5"
5001       "Y .Y1Y HY1Y%Y<`<Y [?[ DZ2Y $[ 1Y  X  Y     !cBc J[?YLX HY<]  JX @Y?_ @[ '`<[ EZ4Z 5` :Y1Y\"Z Z#\\GYI\\ EZ:Z IY@"
5002       "X@Y FZ;[ E]>\\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;Z<Z/Z LZ&\\  ;X  ;\\ 6X 6\\2\\ 6X    EZ /\\GUCQ ;Z 'z 9YGY"
5003       " )X -ZN_   ;TDX\"U\"W;Y 0W@W     ?T>W>X@T   ;a #X #X  =W   6Y1Y GmEW      KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :"
5004       "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z"
5005       "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y  Y  NX  Y /_B] E_<"
5006       "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y     ;X<X<X)X NX)YKZ?ZJX(X/ZKX)X"
5007       " NX ;X 3Y2Z     >~d#Z6U   3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X                   *~R   \"Z   0~d&YCT CXAXBW 5]   "
5008       " >Y  EY 2ZKVKZ -X        ,X :Z/Z 4Z  BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki  J\\!X:hKVAW%Y@Y CZ5\\ L"
5009       "[ \"Z#Z!Z  MZ 0Z  KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F`  BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X"
5010       "  LX  BY        JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y  NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y  EY .Y1Y "
5011       "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y  X  Y     \"eCd L[>YLX HY>^  IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G"
5012       "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.Z<Z=[)Z(Z*Z;Z<Z/Z LZ'\\  :X  :\\ 7X 7\\0\\ 7X    EZ 0\\FU -Z &x 8YHY (X -YK"
5013       "_   >UDX!T\"X<Y 1XAX     ?T>W>X@U   :] !X $X  <W   6Y1Y GmEW      KV .Y=X ?XAX AW4X BW4W 5W@X \"Y 0Z@Y :Y@Z 9Y@"
5014       "Y :Z@Y 9Y@Z ;Z@Y HZ?Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ M[/[ M[0Z LZ/Z LZ/Z M[ N[?\\ Z3XBZ*Z(Z#Z(Z$Z(Z"
5015       "$Y'Y @ZM[ 9Z3[ KYDY 3P0Z AP0Z BQ0Z AQ0Z BP0Z AP0Z&P0b7Z'\\2P CZ7Z DZ7Z DZ7Z DZ7Z 3Y  Y  NX  Y 0]<Z E^:Z D[9[ C["
5016       ":\\ E\\:[ D[9[ C[:\\ 4\\ 3[9[ GY0X HY0X GX0X GX0Y HZ3Y G_:[ GY2Y     <X;X;X*X NX)XJ[A\\JX*X/[JX*X NX ;X 3Z3Z   "
5017       "  >~d&^7U   4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X                   )~R   \"Z    NW?W BYCT CYBXCX 6_    ?Y  EZ 5ZI"
5018       "VIZ /X        ,X :Z.Y 4Z  C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi  N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z "
5019       " MZ 0Z  KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc  EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X  KX  CY  "
5020       "      )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y  NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y  EY .Y1Y GY3Y#Y=WNX=Y M"
5021       "ZAZ AY3Y %[ /Y  X  Y     #gEf N[<YMX HYBb  IY BY;] BZ %^8Z DY5Y 5b ;Y1Y#Z NZ$[FYF[ EY9Y IY@X@Y HZ8[ H\\9[ 1Y 5Y"
5022       ".Z DZ@Z ,\\ 4Z 2[ LZ.Z<Z<Z)Z(Z*[<Z<Z/Z LZ(\\  9X  9\\ 8X 8\\.\\ 8X    EZ 1\\EU -Z %^E] EhIg 6X .YI_   ?UEX T!W="
5023       "Z 2YBY     @U>W>W?U   7W <~d BX  ;W   6Y1Y GmEW      KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y"
5024       "?Y HZ?Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ LZ/[ M[0Z LZ/Z LZ0[ LZ M[A\\ NZ4XAZ*Z(Z#Z(Z$Z(Z$Y'Y @[NZ 8Z3"
5025       "[ KYDY  AZ !Y  Y  Z !Z !Z 5`5Z([ %Z5Z FZ5Z FZ5Z FZ5Z 4Y  Y  NX  Y 1\\:[ F]8Z F[7[ E[8[ E[8[ E[8[ E[8[ 4\\ 4[9\\"
5026       " GY0X HY0X GX0X GX0Y GY4Z G^8Z GZ4Z     <X;X:W+X LX*WH[C\\IX*X0[HW+X LX <X 2Y4Z     =~d(`7T   4~Q 9e E~i%~R GY3"
5027       "Y GX.X ;YMZ 7Z;Z!X                   *~R   !Z    X@X BZDT BXCYDX 6`    ?Y  DY 7[HVH[ 1X        -X 9Z.Y 4Z  D[ 7"
5028       "m 9X@Z ?v AZLp &Z 8^H_ DZ1\\ 6Z \"[ (i F~d Ei #\\ NW;lMV@W'Y>Y D~P JZ !Z#[\"~Q Dy Z  K~] :Z \"Z 4ZN] *Z 2YFX?XF"
5029       "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg  JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X  KX  CY        (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] "
5030       "G]7Z 4Y  NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X  DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X  NX  Y     $iGh Z:XNX"
5031       " GYHg  HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z<Z/"
5032       "ZIuIZ)\\  8X  8\\ 9X 9\\,\\ 9X    EZ 1[DU -Z $Z@[ EhJh 6X /YF_   ATDX U\"X?[ 3ZCZ     @U>W>W?U     K~d CX  ;X  "
5033       " 6Y1Y FlEW      KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y <Z?Z ;Y>Y <Z?Z ;Y>Y ;Y?Z JZ>~Q3[  I~Q G~Q F~Q G~Q 5Z !Z !Z "
5034       "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY  @Y  Y !Z  Y  Y  Y 4_4Y)[ %Z3"
5035       "Y GZ3Y FZ4Y FZ4Y 4Y  Y  NX  Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[   <Z9^ HY0X HY0X GX0X GX0Y GY4Y F]6Z GY4Y    "
5036       " ;W:X:X,X LX+XG[E\\GW*W0[GX,X LX <X 2Z5Z     =~d(`8U   4~R 9c D~h%~T HX2Y GX.X <ZLY 6Y;Z!X                   *~"
5037       "R   !Z    X@X BZDT BZGZCW 6b    @Y  DY 8ZFVFZ 2X        -X 9Z.Y 4Z  DZ 7l 8X?Z ?w BZMr ([ 7s C[3] 6Z \"[ +i C~d"
5038       " Cj '\\ NW;nNV@W(Z>Y D~ IZ !Z#[\"~Q Dy![  K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck  Y 9Z(Z LZ6Z("
5039       "Z?XDX?Z G` *d #[ +X  KX  CY        'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y  NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I"
5040       "Z5\\ ;] -X  DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y  NX  Y     $XIZHZIY!Z:XNX GYHf  GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#"
5041       "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z=[0[IuIZ*\\  7X  7\\ :X :\\*\\ :X      L["
5042       "CU -Z %Z>Z EiKh 6X /XC^   BTDX U\"YA\\ 4ZCZ N~d  &U>W?X>T     K~d EY  :W   5Y1Y EkEW      KV ,YAY =ZCZ DW6X @W6"
5043       "X 5W@W   'Z>Y <Y=Y <Z>Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L"
5044       "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY  @Y  Y  Y  NY  Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y  Y  NX  Y 2[6Z G"
5045       "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z   =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z     <X:X:X,W JW+XF[G\\FX,X1[FX,W JW <X "
5046       "2Z5Y     <~d'UNY9U   5~T H[LaM[!~g&~V JY1X GX.X <ZLZ 7Y;Y X                    Z    3Z    W?X AZET A\\M\\CX 7d "
5047       "   BZ  DY 8XDVDX 2X        -X 9Z.Y 4Z  E[ 7j 7Y?Z ?x CZNt )Z 5p @Z3] 6Z \"[ .i @~d @i *\\ MW<^Ib@W(Y=Z E| GZ !Z"
5048       "\"Z\"~Q Dy![  K~] :Z \"Z 4f 'Z 2YEXAXEY(YCZ?Z*Z KZ\"Z6\\'[ LZ&Z8] An $Y 9Z(Z LZ7Z'Z?XDX?Z F_ *c #\\ +X  JX  DY "
5049       "       'Y E\\4Z FZ %Z4\\ HZ1Y 8Y 3Z4\\ G[4Y 4Y  NX 0XC\\ (Y F[6]6Y*[4Y GZ4[ H\\4Z JY4\\ ;\\ ,X  DY .Y1Y FY5Y!Y?"
5050       "XMX?Y JZF[ >Z6Y &[ .Y  NX  Y     %WEYJYEX#Z8a GYHe  FY DX4[ DY $\\5Y CY8Z 5d <Y1Y$Z LZ'[DYD[ GY9Y IY@X@Y IY4Z J"
5051       "[5[ 3Y 6~W EY=Z *[ 6Z 2Z KZ/Z;Z<[*Z(Z)Z<Z=Z/[IuI[,\\  6X  6\\ ;X ;\\(\\ ;X      LZBU -Z %Y<Z FjMi 6X 0X@]   CTD"
5052       "W NU!ZE^ 5ZCZ M~d  &T=W@X=T     K~d FY  :X   5Y1Y EkEW 3Z    CV +ZEZ ;ZCZ EW6W ?W7XA]\"XAX   'Y=Z =Y=Y <Y<Y =Y="
5053       "Y <Z=Y =Y=Z KY=~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$"
5054       "Y'Y >c 6Z2Z KYDY  ?Y  X  NX  NY  Y  Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y  Y  NX  Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ"
5055       "4[   >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y     ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z     <~d'RKY:U   5~U J"
5056       "~T$~g'~X KY1X GX.X <YKZ 7Z<Y W                    NZ    3Y    NW?W @\\GT @jCW 7f    CZ  DY 7VCVCV 1X        .X "
5057       "8Z.Y 4Z  F[ 6h 5X>Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)Y<Y Ez EZ !Z\"Z\"~Q Dy![  K~] :Z \"Z 4e &Z"
5058       " 2YEXAXEY(YCZ?Z*Z KZ\"Z8^'[ L['Z:_ @p 'Y 9Z(Z KZ8Z'Z@XBW?Z F^ (b $\\ *X  JX  DY        &X E[2Y FZ &Z3\\ HY0Y 8Y"
5059       " 3Y2[ G[4Y 4Y  NX 0XD\\ 'Y F[5[5Y*[4Y HZ2Z H[3Z KZ3[ ;[ ,Y  DY .Y1Y FY5Y!Y?WLX?Y J[GZ <Y7Z '[ -Y  NX  Z     'WC"
5060       "YKXBV#Z8` FYHc +YCY EY4[ DY $[4Z CX8Y 5e <Y1Y$Z KZ([DYCZ GY9Y IY@X@Y IY3Z KZ3Z 3Y 6~W EY<Y )[ 7Z 2Z KZ/Z;Z;Z*Z("
5061       "Z)[=Z=Z/[IuI[-\\  5X  5\\ <X <\\&\\ <X      LZBU -Z &Y:Y FjNj 6X 0X?]   EUEX NU!s 6ZCZ L~d  &T=WAY=T     K~d GX"
5062       "  9Y   5Y1Y DjEW 3Z    CV *]M] 9ZCZ FW7X5X3W7WCc%XBX5Y   JY<Y >Z=Z =Y<Y >Z=Z =Y<Y >Z=Z LZ=~Q3Z  H~Q G~Q F~Q G~Q"
5063       " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY  ?Y  Y  X  MX  Y  Y"
5064       " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y  Y  NX  Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z   >Z:a IY0X HY0X GX0X GX0Y FZ7Y E["
5065       "3Z GY6Y     ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z     <~d NX:U   5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W         "
5066       "           NZ    3Y    X@X ?]IT ?hCW 7h2X   ;Y  CY 7TAVAT 1X        .X 8Z.Y 4Z  G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5"
5067       "^     4i ;~d :i 1[ LW<Z?]?W*Z<Z Fx CZ !Z\"Z\"~Q Dy![  K~] :Z \"Z 4e &Z 2YEXBYEY(YBZ@Z*Z KZ\"Z9^&[ L['[Ad >r *Y "
5068       "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X  JX  DY        &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y  NX 0XE\\ &Y FZ4[5Y*[4Z IZ"
5069       "2Z H[2Y KY2[ ;[ +X  DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y  NX  NY  *Q   NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y"
5070       " 5YMY <Y1Y$Z KZ(ZCYCZ GY9Y IY@X@Y JY2Z L[3Z 3Y 6~W FZ<Z )[ 8Z 2Z KZ/Z;Z;Z*Z(Z)[=Z>[/[IuI[.\\  4X  4\\ =X =\\$\\"
5071       " =X      MZAU -Z &X8Y G~W 6X 0W<\\   FUEX MT iNW 8[D[ K~d  &T=WE\\<T     K~d HX  NQ<Y   4Y1Y CiEW 3Z    CV )k 7"
5072       "ZC[ HW7W5Y3W8XFh>Q<YAW5Z   KZ<Z ?Y;Y >Z<Z ?Y;Y >Z<Z ?Z<Y LZ=~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YBZ?Y*Z KZ/"
5073       "Z KZ0Z KZ1[ L[1Z KZ H[K\\ J[8X=[+Z(Z#Z(Z$Z(Z$Y'Y <` 5Z2Z KYDZ  ?X  Y  Y  NX  NX  NX 4[/Y,Z $Y/Y JY/Y JY/Y JY/Y "
5074       "6Y  Y  NX  Y 3Z3Z HZ3Y IZ1Z IZ2Z IZ2Z JZ1Z IZ2Z   ?Z:b IY0X HY0X GX0X GX0Y EY8Z E[2Y GZ8Z     ;W9X9X.W HW-XB[M"
5075       "\\BW,W3[BX.W HW =X 0Y8Z     ;~d NY;U   6~X!~[%~c&~Z LX0Y HX.X =ZJZ 7Y=Y N~l                  4Z    3Y    X@X ?`L"
5076       "T >eBX<U\"[M\\4Y   ;Y  CZ 7Q?V?Q 0X        .X 8Y-Z 5Z  H\\ 5j 9Y=Z ?T9_ Ec>] ,Z 1j <[7_     7i 8~d 7i 5[ KW=Z="
5077       "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z<a&Z K['} <s ,Y 9Z(Z KZ9Z%ZAXBXAZ E] &_ $"
5078       "\\ (X  JY  EY        &Y F[2Z HZ %Y1[ IY.Y 9Y 4Z1Z GZ3Z 5Y  NX 0XF\\ %Y FZ4Z3Y+Z2Y IZ1Z I[2Z LY1Z ;[ +X  DY .Y1Y "
5079       "EY7Y NX@XKW@Y G[K[ :Y8Y ([ ,Z  NX  NY /[(R   NU?XNW=U%Z7_ EYHg 3bHY FY1Z DX $Z2Y CY:Y 5ZMZ =Y1Y$Z KZ)[CYBY GY9Y"
5080       " IY@X@Y JY1Y LZ1Z 4Y 6~W FY;Z *[ 7Z 2Z KZ/Z;Z;Z*Z(Z(Z=Z>[/[IuI[/\\  3X  3\\ >X >\\\"\\ >X      MZAU -Z 'X6X 5c "
5081       "%X 1X;\\   GUEX MT NgMW 9[D[ J~d  &T=m;T     K~d In 4TA[   4Y1Y BhEW 3Z    DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z   KY"
5082       ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8"
5083       "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[  @X  NX  Y  NY  X  NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y  Y  NX  Y 4Z2Z HZ3Y IZ1Z I"
5084       "Z1Z JY1Z JZ1Z IZ1Z   @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y     ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z      I"
5085       "[ 7Y<U   6~Y\"~^'~c'~\\ MX/X HX.X =YIZ 7Z>Y ~m                  4Z    3Y    W?X >g =cAW?]'[K\\5Y   ;Y  CZ %V  M"
5086       "X        /X 7Y-Z 5Z  H[ 4l ;X<Z ?Q4^ Fb<] .[ 3o ?[7_     :i    5j 9[ JW=Y;[?W+Z:Y F~ IZ !Z\"Z\"~Q Dy![2l'~] :Z "
5087       "\"Z 4f 'Z 2YDXCXDY(YAZAZ*Z KZ\"~%Z K['| 9s .Y 9Z(Z JZ:Z%ZAXBXAZ E] %] #[ 'X  IX  EY        &Y FZ0Y HY %Z1[ IY.Y"
5088       " 9Y 4Y0Z GZ2Y 5Y  NX 0XG[ #Y FZ4Z3Y+Z2Y JZ0Z IZ0Y MZ1Z ;Z *Y  EY .Y1Y EY8Z NYAXKXAY FZL[ 9Y9Y ([ +Y  MX  NZ 4b,"
5089       "S   U=`=U%Z6^ EYHi 6dIY FY1Z DY %Z2Y BX:Y 5ZLY =Y1Y%[ KZ)ZBYBZ HY9Y IY@X@Y JY1Z MZ1Z 4Y 6~W GZ:Y +\\ 7Z 2Z KZ/Z"
5090       ";Z;Z*Z(Z([>Z>Z.[IuI[0\\  2X  2\\ ?X ?\\ \\ ?X      MY@U 8y ;X6X 4a $X 1X9[   HUEX MT MeLW :[D[ I~d  &T=l:T     "
5091       "K~d Io 5m   3Y1Y AgEW 3Z    Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[   LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z  H~Q G~Q F~Q G"
5092       "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[  AX  NX  Y  NY  Y  X"
5093       " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y  Y  NX  Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z   @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D"
5094       "Z0Y GY9Z     ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y      HZ 5X<U   6~Z$~`'~a&~\\ NY/X HX.X =YHY 7Z?Z ~m         "
5095       "         4Z    3Y    W?W <i >_@XAa*[I\\6Y   ;Y  CZ %V  MX        /X 7Y-Z 5Z  I[ 3n >X;Z  ] G`9\\ .Z 4s @[9`    "
5096       " =i    /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z"
5097       "$ZAW@WAZ F_ %\\ $[ &X  IX  EY        &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y  NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0"
5098       "Z ;Z *Z  FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z  MX  N[ 7g1U   U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BY<Z 6ZKZ >Y1Y%Z"
5099       " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\  1X  1\\ @X @\\ M\\ @X      NZ"
5100       "@U 8y ;W4X 5` #X 1X8Z   HUEX MT LbJW ;ZC[ H~d  &T=j8U     L~d Io 5l   2Y1Y @fEW 3Z    Nl 0c 0[CZ&lDW5[>mEXE\\N^"
5101       "AlAX6\\   LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K"
5102       "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\  BY  X  NX  NY  Y  X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y  Y  NX  Y 5Z0Y HY"
5103       "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y   AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y     :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /"
5104       "Y:Z      IZ 4Y=T   6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m                  4Z    3Y   !X@X ;l @[>WBe,ZG\\7Y   ;Y"
5105       "  CZ %V ;~c        LX 7Y-Z 5Z  J\\ 2n @Y;Z  N\\ G`8\\ /Z 5u A\\<b     ?i    *i ?Z IW=X8Z>V+Y8Y G~R LZ !Z\"Z\"~Q"
5106       " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZ<Z$ZBX@XBY F` %[ $\\ &X  IX  EY        &Y FZ"
5107       "0Z JZ %Y/Z JY,X 9Y 5Z0Z GY1Y 5Y  NX 0XI[ !Y FY3Z3Y+Y1Y JZ/Y IZ0Y MY/Y ;Z *[  GY .Y1Y DY9Y MYBXIWBY Dg 7Y;Z *[ +"
5108       "[  MX  M[ :l6W   T:\\:U&Y5] DYHk :hKY GY/Z DZ 'Z2Y BY<Y 5ZKZ >Y1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z "
5109       "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX      NZ@U 8y <X4X 4_ #X 1X7Z   IUEX MT J^HW <ZCZ F~d  &T="
5110       "g5T     -X ,o 5k   1Y1Y >dEW 3Z    Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6]   LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z  H~Q "
5111       "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\  CY  X  NX"
5112       "  NY  Y  Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y  Y  NX  Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$Z<WJY JY0X HY0X GX0X G"
5113       "X0Y DZ;Y CZ0Y FY:Y     :WK~KW/WJ}JW.W=b=W.W6[=W/W9[9W >X /Z;Z      JZ 2X>U   6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y "
5114       "N~m                  4Z    3Y   !X@X :n 'WBg.ZE\\8X   :Y  CZ %V <~e        NX 6Y-Y 4Z  K\\ #a AX:Z  M\\ H_6[ 0Z"
5115       " 6aI` A]?c     ?f    $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z "
5116       "K['v +o 2Y 9Z(Z IZ<Z#YBX@XCZ Fa %Z %\\ %X  HX  FY        6i FZ0Z JZ %Y/Z JY,X 9Y 5Z/Y GY1Y 5Y  NX 0XK\\  Y FY3Z"
5117       "3Y+Y1Y JY.Y IY/Z NY/Y ;Z *\\  HY .Y1Y DZ;Z LXBXIWBY Ce 6Y;Y )[ -\\  LX  L\\ >q:X  !U:[9U&Y5] DY?d =jLX FY/Z C[ "
5118       ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX"
5119       " B~o BX      NZ@U 8y <X4X 4^ \"X 1X6Y   IUEX MT GW *ZCZ E~d  &T=g5T     -X ,o 5i   /Y1Y <bEW 3Z    Nl *W 'ZCZ(l",
5120       "EW6]>mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ"
5121       "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y  Y  NX  Y 5Y/"
5122       "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CY<Z CY/Z GZ<Z     :WK~KW/WJ}JW.W<`<W.W7[<W/W9[9W "
5123       ">X .Y;Y      JZ 1Y?U   6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m                  4Z    3Y   !W?X 9p +XCi0ZC\\9X  "
5124       " :Y  CZ %V <~e        NX 6Z.Y 4Z  L\\  M^ CY:Z  L[ H^4Z 0Z 7^A^ C_Ce     ?c     Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\""
5125       "Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X  HX  FY   "
5126       "     >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y  NX 0XL\\  NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^  KY .Y1Y CY;Y KYCXIXCY "
5127       "Bc 4Y<Y *[ 2a  KX  La Du?Z  !U9Z8T'Z5] DY9^ >\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ"
5128       "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX      NY?U 8y <W2W 3] \"X 1Y7Y   IUEX MT  "
5129       " JZCZ  8X  &T=WIZ6T     -X ,o 3e   -Y1Y :`EW 3Z    Nl   (ZCZ)lFW5UNV>mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7"
5130       "Y CY7Z#Z:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ "
5131       "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y  Y  NX  Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X"
5132       " HY0X GX0X GX0Y CY<Y BY/Z FY<Y     9WK~KW/WJ}JW.W;^;W.W8[;W/W9[9W >X .Y<Z      K[ 1Y@U   6~](~f'~[ ~V KX.Y IX.X"
5133       " ?ZFY 6YAZ N~m                  4Z    3Y   !W?W 6p -WCk1ZB\\;Y   :Y  CZ %V <~e        NX 6Z.Y 4Z  M\\  J] EY9Z "
5134       " L[ H^4[ 2[ 8\\<\\ BbKi     ?`     Ha @Z HV=X5X>W-Y6Y HZ2\\ Z !Z\"Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z"
5135       "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X  HX  FY        At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y  NX"
5136       " 0XM\\  MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b  Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e  JX  Ke KzF^  !U9Y7T'Z4[ CY7] @[E"
5137       "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L["
5138       "4~p BX B~o BX C~q CX      NY?U 8y <W2W 3\\   )Y6Y   JUEX NU   KZCZ  7X  &T=WGY7T     -X    J^   *Y1Y 7]EW 3Z   "
5139       "     8ZCZ 4X6UMV GX-X=^;W6UMW CY 4Y6Y DZ7Z CY6Y DZ7Z CY6Y DZ7Z#Z:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y>ZCY*Z K"
5140       "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u <u <t :t <u <u!t,Y/Y #Y,Y MY,Y MY,Y MY,Y 7Y  Y "
5141       " NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Y LZ.Y KY.Z$~d$Y>XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z     9WK~KW/WJ}JW.W:\\:W.W"
5142       "9[:W/W9[9W >X .Z=Y      JZ /X@U   6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l                  4Z    3Y   \"X@X 3n /X"
5143       "CZIZ2Z@\\<Y   :Y  BY %V <~e        Y 6Z.Y 4Z  N\\  G\\ FX8Z  K[ I]2Z 2Z 8\\9[ BsNZ     ?]     B^ @Y GV=W4X>W.Z6"
5144       "Z IZ1[ Z !Z#[\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ "
5145       "\"X  GX  GY        Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y  NX 0XN\\  LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z"
5146       " KYDXGWDY @a 3Z>Y +[ 5d  IX  Ic L~d  !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y "
5147       "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX      NY?U 8y <W2W 2[   (Y7Y   ITDW "
5148       "NU   M[CZ  6X  &T=WFY8T     -X        EY1Y 1WEW 3Z        7ZC[ 6W6ULV HX+W JX7ULW CY 5Z6Z EY5Y DZ6Z EY5Y DZ6Z E"
5149       "Z6Y$Z9Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?"
5150       "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0"
5151       "X GX0Y BY>Z BY.Y EY>Y     8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z      KZ .YAU   6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z"
5152       "CZ L~k                  :y    KY   \"X@X 0m 1WCYEY3Y>\\=X   9Y  BY %V <~e   =l    X 5Z.Y 4Z  \\  E[ GY8Z  JZ I]"
5153       "2Z 2Z 8[7[ BqMZ     ?^     C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s"
5154       " I[ LZ&[Cc  Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X  GX  GY        Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y  NX 0e  KY"
5155       " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b  GX  Ga L~c   T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f "
5156       "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[<Z<[*Z(Z%\\BZC\\+[ LZ3~p BX B~o "
5157       "BX C~q CX    DX 4Z?U -Z (W2W 2Z   'Z7X   ITDX U   MZCZ  5X  &U>WEY9T     -X        EY1Y 1WEW 3Z        6ZCZ 7X7"
5158       "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z "
5159       "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` <y @y Ay ?y @y @y%~v/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY"
5160       "-Y KY-Y MZ.Z MY-Y KY-Y$~d$Y?WEY KY0X HY0X GX0X GX0Y BZ?Y AY.Y EY>Y     8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z  "
5161       "    LZ -YBU   5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW                   'y    JY   \"W?X ,j 3WCYCY4Y=\\>X   9Y  CZ"
5162       " %V <~e   =l    X 5Z.Y 4Z !\\  C[ IY7Z  JZ I]2Z 3[ 9[5[ BoLZ     ?a     Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z  MZ 0"
5163       "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^  M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X  GX  GY        "
5164       "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y  NX 0c  IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ "
5165       "3`  EX  E_ L\\Cx   NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ"
5166       "4Y 4\\ 1Z 1[ NZ.[<Z<Z)Z(Z$\\CZD]*Z LZ3~p BX B~o BX C~q CX    DX 4Z?U -Z (W2W 2Z   'Z7X   ITDX U   MYBY  4X  &U>"
5167       "WDX:U     -X        EY1Y 1WEW 3Z        5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[  IZ !Z "
5168       " Z !Z  >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ "
5169       "B{'~x/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y "
5170       "DY@Z     8WK~KW/WJ}JW.W=a<W.W<[7W/W9[9W >X ,Y?Y      LZ +XBU   6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX            "
5171       "       (y    JY   \"W?W (h 5XCXAX5Z<\\@Y   9Y  CZ $T ;~e   =l    X 5Z/Z 4Z \"\\  AZ IX6Z  JZ I\\1[ 4Z 8Z3Z AmKZ"
5172       "     ?d     d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z  MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(Y<ZFZ*[ M[\"Z /Z LZ&Z:\\  K"
5173       "^ 6Y 9Z(Z GZAZ NZEW<WEZ IZL[ )Z *\\  X  FX  HY        H{ FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0c  IY FY3Y2Y+Y1Y"
5174       " KZ-Y JY.Y Y-X ;Y $l .Y .Y1Y AY?Y HYEWFXEX =\\ .Y@Y -[ 2b  GX  Ga LY=s   LT6W7T'Z4Z BY2Z DY=^ GY-Z =d 9Y1Y ?XAY"
5175       " 5YDZ AY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ4Z 5[ 0Z 0Z NZ-Z<Z<Z)Z(Z#\\DZD\\)Z LZ3~p BX B~o BX B~p CX"
5176       "    DX 4Z?U -Z (W2W 2Z   &[9X   IUEX T s AXAY  4X  &U>WCX;U     -X        EY1Y 1WEW 3Z      Is 0YAX 8W6UJV IW)W"
5177       " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(Y<ZEY*[ M[/[ M[0Z LZ/Z LZ/Z LZ "
5178       "Ca CZBY4Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY<` A| C| C{ A{ C| C|(~y/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y"
5179       " MZ.Z MY-Y KY-Y$~d$YAWCY KY0X HY0X GX0X GX0Y AY@Y @Y.Y DY@Y     7WK~KW/XK}KX.W>c=W.W=[6W/X:[:X >X ,Y@Z      M[ "
5180       "+YCT   5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX                   )y    HX   #X@X (TNc 6WCX@X5Y:\\AX   8Y  CZ   :~e"
5181       "   =l   !X 4Z/Z 4Z #\\  @[ KY6Z  IZ I[0Z 4Z 9Z2[ @jJZ     ?f    %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z  MZ 0Z Z'Z(Z"
5182       " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(Y<ZFZ*[ MZ!Z /Z LZ&Z8[  K] 6Y 9Z(Z FZBZ NZFX<XFY I[KZ )Z +\\  NX  FX  HY        I| FY."
5183       "Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0d  JY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y #m 0Y .Y1Y AY?Y HYFXEWFY =\\ .YAY ,[ 2d  I"
5184       "X  Ic LW8n   JU7W7T'Y2Y BY1Z EY<\\ FY-Z @g 9Y1Y ?YBY 6ZDZ AY1Y&Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Y.Z JY3Z 6["
5185       " /Z 0Z [-[=Z=[)Z(Z#]EZE\\(Z LZ2~o BX B~n AX A~n BX    DX 4Z?U -Z (X4X H~W   <\\:W   HUDX!T s AZCZ  5X  %T>WBX<U"
5186       "     -X        EY1Y 1WEW       \"s 1ZCZ 9X7UIV JX)W LW7UIW CY 6Y2Y HZ3Z GY2Y HZ3Z GY2Y HZ3Z'Z8Z <[  IZ !Z  Z !Z"
5187       "  >Z !Z !Z \"Z :Z#Z(Y<ZEY)Z M[/[ M[0[ MZ/Z LZ/Z M[ Dc DZCY3Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z6\\ IY:` D} D} D| B| D} D})~z"
5188       "/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y$~d%ZBXCY KY0X HY0X GX0X GX0Y @YAY @Y.Y DYAZ "
5189       "    7W8X8W.W HW-W?e>W.W>[5W.W:[:W =W +ZAY      LZ *YDU   5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX                   "
5190       ")y    HX   6~e 9TJ_ 7XCX?X6Y9\\BX   8Y  CZ    KX    Nl   !X 4Z/Z 4Z $\\  >Z LY5Z  IZ I[0Z 5Z 8Z1Z >fHY     =h  "
5191       "  +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z  MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[  K\\ 6Y 9Z(Z FZ"
5192       "BZ MYFX<XGZ J[IZ *Z +[  MX  FX  HY        Jb>Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0e  KY FY3Y2Y+Y1Y KZ-Y JY.Y"
5193       " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e  JX  Ke LU4k   IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&"
5194       "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1]  1X  1\\ @X @\\ L\\ AX    DX 4"
5195       "Z?U -Z (X4X H~W   ;\\;W   GTDX\"U s A[D[  6X  %T>WBX<T     ,X        EY1Y 1WEW       \"s 2[D[ 9W7UHV KX(W MX7UI"
5196       "W CY 7Z2Z IZ3Z HZ2Z IZ3Z HZ2Z IZ3Z(Z7Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC"
5197       "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY"
5198       "-Y LZ-Y MY-Z MY-Y LZ-Y   CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY     6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ   "
5199       "   MZ (XDU   5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX                   +y    GX   6~e 9TG] 8WBW>X6Y8\\DY   8Y  CZ  "
5200       "  KX    Nl   !X 4Z/Z 4Z %\\  =Z LX4Z  IZ I[0Z 5Z 9Z0Z <bFY     ;i    1i =Z HW>X3W?W/~S KZ-Z\"Z \"Z#Z!Z  MZ 0[!Z"
5201       "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[  K\\ 7Y 9Z(Z FZCZ LZGX<XGZ JZH[ +Z ,\\  MX  FY  IY        K"
5202       "]8Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0f  LY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y  Mk 3Y .Y1Y @YAY FYGWDXGY >^ .YCZ ."
5203       "[ )_  KX  L_ ES/e   FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J"
5204       "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\  1X  2\\ ?X ?[ M\\ @X    DX 4Z?U -Z 'W4W G~W   :]>X   GTDY#U s @[D[  7"
5205       "X  %U?WAX>U     ,X        EY1Y 1WEW       \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z  IZ !Z  Z"
5206       " !Z  >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8"
5207       "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y   BYDXAY KY0X HY0X GX0X GX0Y"
5208       " @ZCY ?Y.Y CYBY     5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ     6~d IYET   4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX    "
5209       "                Z    3X   6~e 9TF\\ 9WBX=W7Z7\\EX   7Y  CZ    KX    Nl   \"X 3Z/Z 4Z &\\  ;Z M~Z %Z I[0Z 6[ 9Z/"
5210       "Y 8ZCZ     8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z  MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6["
5211       "  J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\  LX  EX  IY        L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y  NX 0XM\\  MY "
5212       "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y  Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\  LX  M\\ AR+`   CT9[:U'Z3X AY0Y FY9Z FY-Z "
5213       "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\  2X  3"
5214       "\\ >X >[ \\ ?X    DX 4Z?U -Z 'X6X G~W   9^@X   GUDY$T Ns ?[CZ  8X  %U?WAY?U     ,X        EY1Y 1WEW       \"s 4"
5215       "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N"
5216       "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2Z<a HY5^ I[5Y F[5Y G\\5X E\\6Y F\\6Y F[5Y+[5~V/Y #~V L~V L~W M~W 7Y  Y  NX"
5217       "  Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y   BYDW@Y KY0X HY0X GX0X GX0Y ?YDZ ?Y.Y BYDY     4W9X9X.W HW-XC\\L[BW,WB["
5218       "3X.W HW >X )YCY     5~d IYFU   4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX                    Z    3X   6~e 9TD[ ;XBX=X8"
5219       "Z6\\GY   7Y  CY    JX    Nl   \"X 2Y/Z 4Z '\\  :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z     5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ "
5220       "#Z$[!Z  MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[  K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\  KX  EX"
5221       "  IY        LZ4Y FY.Y KZ %Z.Y KZ  <Y 5Y.Y GY1Y 5Y  NX 0XL\\  NY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y  Ff 5Y .Y1Y @ZCZ FY"
5222       "HXCWHY ?b /YDY /[ ![  MX  M[ @Q%W   ?T9\\;U'Z3X AY0Z GX8Z FY-Z E\\ )Y1Y =XEY 6Z@Y BY1Y&Z9Y9[,ZAYAZ IY9Y IY@X@Y "
5223       "LY.Z Y-Y 5Y 4Y/Y KZ0Z ;[ ,Z /[#Z*\\?Z?\\(Z(Z N`LZL`$Z NZ-\\  3X  4\\ JPCXCP J[\"\\ >X    DX 4Z?U -Z 'X6X G~W   "
5224       "8^BX   FUDY%U Ns =ZCZ  9X  $U@W@X?T     +X        EY1Y 1WEW       \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~"
5225       "T J~U K~T*~ ;[  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ "
5226       "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y  Y  Y  Y  9Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y   BYEW?Y"
5227       " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY     4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ     5~d HXFU   4~_+~i z @w DX.Y"
5228       " IX.X BZ@Y 6YGZ IY                    Y        @~e 9TCZ ;WAX=X8Y4\\HX   6Y  CY    JX    Mj   !X 2Y/Y 3Z (\\  9Z"
5229       " M~Z %Z I[0Z 6Z 8Z/Z \"Z     2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z  MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ "
5230       "Z Z .Z [%Z4Z  JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\  JX  EX  IY        MZ3Y FY.Y JY %Z/Z JY  <Y 5Y.Y GY1Y 5Y  NX"
5231       " 0XK\\  Y FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y  Bc 6Y .Y1Y ?YCY DYIXCXIY @c /YEY /[  NZ  MX  N[     *U;^=U&Z4Y AY/Y HY7X"
5232       " EY-Y E[ 'Y1Y =YFY 6Z@Z CY1Y&Z9Y9Z+ZAYAZ IY9Y IY@X@Y LZ/Z Y-Y 5Y 4Y0Z KZ0Z <[ +Z .Z$[)\\@Z@\\'Z(Z M~Q#Z [,\\  4"
5233       "X  5\\ JRDXDR J[$\\ KQCXDQ   #Y 4Z?U -Z &X8X F~W   7_EY   EUDY&U Ns <ZCZ  :X  $U@W?XAU     +X        EY1Y 1WEW "
5234       "      \"s 6ZCZ 7X8UEV MX)X MW7UFW DZ 8~U L~V K~U L~V K~U K~U+~ :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z"
5235       "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y  Y  Y  Y  9Y  Y  "
5236       "NX  Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y   BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ     4X:X9W,W JW+WE\\H[EX,X"
5237       "E[1W,W JW =X )ZEY     4~d HYHU   2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX                    Z        A~e 9TCZ <XAW<"
5238       "X8Z4\\JY   6Z  DY    JX        4X 1Z0Y 3Z )\\  8Z M~Z %Z I[0Z 7Z 7Z/Z \"Y     /i >~d >i 2Z GV>X3W@W0~V LZ-[\"Z "
5239       "#Z%[ Z  MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[  KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[  IX  DX  J"
5240       "Y        MY2Y FY.Y JY %Z/Z JY  <Y 5Y.Y GY1Y 5Y  NX 0XJ\\ !Y FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y  ?a 7Y .Y1Y ?YCY DYIWBX"
5241       "IY @d /YFY 0[  LY  MX  NZ     )U<VNW=U&Z4Y AY/Y HY8Y EZ.Y F[ &Y1Y =YGZ 7Z>Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N"
5242       "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\  5X  6\\ JTEXET J[&\\ KSDXES   $Y 3Y?U -Z &Y:Y F~W   5_GX   DU"
5243       "CZ9QAU   DZCZ  ;X  $VAW?YBU     +X        EY1Y 1WEW          DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V"
5244       ",~P :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y "
5245       "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y  Y  Y  Y  9Y  Y  NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z   BYGX?Z KY1Y HY0X"
5246       " GX0X GX0Y >YFZ >Y.Y AYFY     2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY     4~d GXHU   2kNRMk*tNq Mv <s BX.Y IY/X"
5247       " BY>Y 7ZIZ GY                   !Z        A~e 9TBY <W@W;W8Z3\\KX   5Z  DY    JX        4X 1Z1Z 3Z *\\  7Z M~Z %"
5248       "Z HZ0Z 7Z 7Y.Z #Z     ,i A~d Aj 0Z GV=W4X@W0~W MZ-[\"[ $Z%[ Z  MZ /[\"Z'Z(Z :Z \"Z 4Z@] 8Z 2Y>`=Y(Y8ZJZ([\"[ Z "
5249       ".[!Z$Z3Z  KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\  IX  DX  JY        MY2Y FY.Y JY $Y/Z JY  <Y 5Z/Y GY1Y 5Y  NX 0XI"
5250       "\\ \"Y FY3Y2Y+Y1Y JY.Z JY.Y NY/Y ;Y  ;] 7Y .Y1Y >YEY CYIWBXIX @f 0YGZ 0[  LZ  NX  NY     'U>WMW?V&Z4Y AY/Y HY8Y"
5251       " EZ.Y FZ %Y1Y <XGY 6Z>Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\  6"
5252       "X  7\\ JVFXFV J[(\\ KUEXFU   %Y 3Y?U -Z %Y<Y /Z    M`KY   BUC[=SAU   CZCZ  <X  #UAW>XCU     *X        EY1Y 1WEW"
5253       "          F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+["
5254       "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y  Y  Y  Z  :Y  Y"
5255       "  NX  Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z   BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY     2X;X:W+X LX*WH\\D[HX"
5256       "*WG[0W+X LX =X (YFZ     4~d GYIU   2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX                   !Z        A~e 9T"
5257       "BZ >W?W;W8Z2\\MY   4Y  DY    JX        4X 1Z1Z 3Z +\\  6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z     )i D~d Ci -Z GV=W4XAW/~W M"
5258       "Z-[\"[ $Z&[ NZ  MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[  L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\"
5259       "  HX  DX  JY        NY1Y FZ0Z JY $Y/Z JY  <Y 5Z0Z GY1Y 5Y  NX 0XH\\ #Y FY3Y2Y+Y1Y JY.Y IY/Z NY/Y ;Y  9\\ 8Y .Y1"
5260       "Y >YEY BXJXAWJY A[N[ 1YGY 0[  JY  NX  NY     'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y <YIZ 6Y=Z DY1Y&[:Z:Z*YAYAY HY9"
5261       "Y IY@X@Y LZ/Y NZ/Z 5Y 3Y1Y KY-Z ?[ )Z -[([%]CZC]%Z(Z Jy M[#[(\\  7X  8\\ JXGXGX J[*\\ KWFXGW   &Y 3Y?U -Z %Z>Z "
5262       "/Z    K_MZ   BUC]BVBU   A[D[  >X  #VBW=XDU     *X        EY1Y 1WEW          G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M"
5263       "~W N~X M~W N~X.~Q :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z"
5264       " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z  :Y  Y  NX  Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z  "
5265       " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY     1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW <X (ZGY     3~d GYJU   1iKQKi*pN"
5266       "RMo Jr 9q AX.Y HX0Y CZ>Z 7ZJY EY                   !Z        1X@X &TAY ?X?W;W8Z1\\NX   3Y  DY    JX        5Y 0"
5267       "Y1Z 3Z ,\\  5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z     &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ  MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2"
5268       "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[  M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\  GX  CX  KY        NY1Y FZ0Z JZ %Y/Z JZ  =Y 4"
5269       "Y0Z GY1Y 5Y  NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y  8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[  IY  NX  Z     &VB"
5270       "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y <YIY 6Z<Y DY1Y%Z:Z:Z*YAYAY HY9Y IY@X@Y LZ/Y NZ/Z 5Y 3Y2Z LZ,Z A[ (Z ,[)[%^DZD^"
5271       "%Z(Z Iw L[#['\\  8X  9\\ JZHXHZ J[,\\ KYGXHY   'Y 3Z@U -Z $[B[ .Z  NW $j   @UCpBU   @[D[  ?X  \"UBW=XEU     )X "
5272       "       EY1Y 1WEW          H[D[ 5W8UBV W*X LX8UCW F[ 9~Y ~X N~Y ~X N~Y ~X.~Q 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&"
5273       "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z"
5274       " !Z  :Y  Y  NX  Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y   AYIW<Y IX1Y GX1Y GX1Y GY2Z =YHY <Z0Y ?YHY     0X<X;X*X N"
5275       "X)XJ[@[KX(XK[/X*X NX <X 'YHZ     3~d FXJU   0hKQKh(nMRMo Jq 7o @X.Y HX0X BY=Z 7ZKZ DY                   \"Z    "
5276       "    1W?X &TAY ?W>W;W8Z0e   3Y  EZ    JX        5X /Z2Y 2Z -\\  4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z     #j J~d Ii   CW>X6Y"
5277       "BX0~Y NZ-[![ %Z'\\ NZ  MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<]<Y(Y7ZKZ'[$[ NZ -[$[#Z1Z  M[ 8Y 8Z*Z BZIZ GZKX8XKZ N[>[ 0"
5278       "Z 3\\  FX  CX  KY        NY2Z FZ0Y IZ %Y/Z JZ  =Y 4Y0Z GY1Y 5Y  NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y  7Z 8Y"
5279       " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[  HY  NX  Y     %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ "
5280       "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\  9X  :\\ J\\IXI[ I\\/\\ K[HXI[   (Y 3Z@U -Z "
5281       "%^F^ /Z  X \"f   >VBnCU   >[D[  @X  \"VCW<XGV     )X        EY1Y 1WEW          I[D[ 5X8UBV!W)X LW8UBW FZ 8~Y!~Z"
5282       " ~Y!~Z ~Y ~Y0~R 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v "
5283       "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y  Y  Y  Y  :Y  Y  NX  Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW"
5284       ";Y IX1Y GX1Y GY2Y GY2Z <YIY <Z0Y ?YIZ     0X<X<X)X Y(XJY>YJX(XJY/X)X Y <X 'ZIZ     3~d FYKT   /gJQJg(nMRLm Hp 6"
5285       "m ?X.Y HY1X CZ<Y 6YKZ DY                   \"Z        1W?W %TAY @X>W;W7Y/c   2Y  EY    IX        5X /Z3Z 2Z .\\"
5286       "  3Z M~Z &Z FY1Z 8[ 6Z/Z $Z      i L~d Li   @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ  MZ -[$Z'Z(Z :Z \"Z 4Z<] <Z 2Y<]<Y(Y6ZL"
5287       "Z'[%[ MZ ,[%[#Z1[  N[ 8Y 8Z+[ AZJZ GZKW6WKZ NZ<[ 1Z 3[  EX  CX  KY        NY2Z FZ0Y IZ %Z1[ IY  =Y 4Z1Z GY1Y 5Y"
5288       "  NX 0XE\\ &Y FY3Y2Y+Y1Y JZ0Z IZ0Y MZ1Z ;Y  6Y 8Y .Y2Z =YGY AYKW?WKX B[J[ 1YJY 2[  GY  NX  Y     $ZL[H[JY#Y6\\ "
5289       "BY0Y GX8X BZ0Z GY #Y1Y ;YKZ 7Z:Y EY2Z%Z:Z:Z*ZBYBZ HY9Y IY@X@Y L[1Z MY/Y 3Y 4Z3Y LZ+Z C\\ 'Z +[,[!_GZG_#Z(Z Fq H"
5290       "[%[$\\  :X  ;\\ H\\JXJ\\ H\\1\\ J\\IXJ\\   (Y 3Z@U -Z &x 0Z  X  c   <UAmDV   =[CZ  AX  !VDW<YHU     (X        E"
5291       "Y1Y 1WEW          JZCZ 3W8UAV\"X*X LX9UAW G[ 9Z*Y!Z+Z Y*Y!Z+Z Y*Z\"Z+Z0Z3Z 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%"
5292       "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s <Y-Y NX1Z IY1Z IY2Z GY2Z HY2Z HX1Z.Y1Z 1Z $Y  Y  "
5293       "Z !Z  ;Y  Y  NX  Y 5Y/Z IX0X IY/Y JZ0Z KZ0Z KY/Y IY0Z 7\\ 6YLX<Z IX1Y GY2Y GY2Y GY2Z <YJZ <Z0Y >YJY     .X=X=Y("
5294       "X!X'YJW<WJX&XJW/Y(X!X ;X &YIY     #[  LYLU   .fJQJf&lLRLm Gn 4k >X.Y HY2Y CZ<Z 7YKY BY                   #[    "
5295       "    3X@X %TAY @W=W;W7Z0b   1Y  EY    IX        5X /Z3Z 2Z /\\  2Z )Z  JZ FZ2Z 8Z 5Z/Z %Z      Ki   :j   >W=X8ZC"
5296       "W/Z*Z Z-Z N[ &Z(\\ MZ  MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]<Y(Y6ZLZ&[&[ MZ ,\\'[\"Z0Z  NZ 7Y 8Z+Z @ZJY FZLX6XLY N[;"
5297       "Z 1Z 4\\  EX  BX  LY        NY2Z F[2Z HZ %Y1[ IZ  >Y 4Z2[ GY1Y 5Y  NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y  6Y"
5298       " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[  EX  NX  Y     $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB"
5299       "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\  ;X  <\\ F\\KXK\\ F\\3\\ H\\JXK\\   'Y "
5300       "2ZAU -Z 'z 1Z  X  Na   ;V@jDV   :ZCZ  BX   UDW;XIU     'X        EY2Z 1WEW          KZCZ 3X9U@V\"W*X LX9VAW H[ "
5301       "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!"
5302       "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z  ;Y  Y  NX  Y 5Z0Y HX0X IZ1Z IY0Z KZ0"
5303       "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY     .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ     #Z  JXLU"
5304       "   -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ                   #Z        3X@X %TAX @W<W;W7Z/a   0Y  FY    IX   "
5305       "     6X -Z4Z 2Z 0\\  2[ )Z  JZ FZ2Z 8Z 5Z/Z %Z      Hi   @j   :V=Y9ZDX/Z*Z Z-Z N\\ 'Z)\\ LZ  MZ ,[%Z'Z(Z :Z \"Z"
5306       " 4Z:] >Z 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[  Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\  DX  BX  LY        NY3[ F[2Z HZ %Y1"
5307       "[ IZ  >Y 3Y2[ GY1Y 5Y  NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y  6Z 9Y .Y2Z <YIY ?YMX?XMY CZFZ 2YKY 3[  DY  X  "
5308       "Y     #gEf!Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y :XLZ 7Z9Z FY2Z%Z;\\<[)ZCYCZ GY9Y IZAXAZ L[1Y LZ1Z 3Y 3Y4Y KZ*Z E[ %Z )["
5309       "/[ MdNZNd!Z(Z Ag B['[!\\  <X  =\\ D\\LXL\\ D[4\\ F\\KXL\\   &Z 3ZAU -Z (| 2Z  X  L^   9V?fBU   8ZCZ  CX   V JV "
5310       "             CY2Z 1WEW          LZCZ 2W9V@V#X+X KW8U@W I[ 7Z*Z#Z)Z\"Z)Y#Z)Z\"Z)Y#Z)Z2Z2Z 7[  NZ !Z  Z !Z  >Z !Z"
5311       " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2"
5312       "[.Y2\\ 2Z $Z !Z !Z  Y  ;Y  Y  NX  Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z ="
5313       "YLY     ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY     #Z  IYNU   ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ          "
5314       "         $[,P       )W?X %TBY AX<W;W7[/_   /Y  FY    IX        6X -Z5Z 1Z 1\\  1Z (Z  K[ EY2Z 9Z 4Z0[ &[      F"
5315       "j   Ei   7W=Y;[EX/Z(Z!Z.[ M[!P'Z*] LZ  MZ ,\\&Z'Z(Z :Z \"Z 4Z9] ?Z 2Y;[;Y(Y4YMZ%[)\\ LZ +\\)[!Z/Z  Z 7Y 7Z-[ ?Z"
5316       "LZ EZMX6XMZ Z8[ 3Z 6\\  CX  BX  LY        NY3[ F[2Y GZ %Z3\\ HZ  ?Y 3Z4\\ GY1Y 5Y  NX 0XB\\ )Y FY3Y2Y+Y1Y IZ2Z "
5317       "H[2Y KZ3[ ;Y  6Z 9Y .Z4[ <YIY ?YMW>XMY DZDZ 2YLY 3[  DY  X  Y     \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y "
5318       "FY2Z%[<\\<Z(ZCYCZ GY9Y HYAXAY K\\3Z KZ2Z 3Y 3Z5Y LZ)Z F[ $Z ([1[ K~U Z(Z ;[ <\\)[ N[  <X  <Z B\\MXM\\ BZ3Z D\\L"
5319       "XM\\   %Z 3ZAU -Z )~ 3Z  X  J]   9V>a@V   7YBY  CX   NV LV              BZ3Z 1WEW          LYBY 2W8U?V#W+X KX9U"
5320       "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z  Z !Z  >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8"
5321       "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z  <Y  Y  NX  Y 4Z2Z HX0X HZ2"
5322       "Z IZ2Z IZ2Z IZ2Z IZ2Z 6\\ 5a:Z HY3Z GY3Z GY3Z GY3[ ;YLY :[2Y <YLY     ,Y?X?Y$Y'Y#YIQ6QIY$YIQ.Y$Y'Y 9X %YLZ     "
5323       "$Z  HYNU   +aHSH`!hJRIg Bi /g <X.Y GY4Y CZ:Y 6YMY @[                   $Z-Q       )W?W $TBY AW;W<X6Z.]   .Y  GY"
5324       "    HX        6X -Z5Z 1Z 2\\  0Z (Z  L[ DZ4Z 8Z 4[1Z %Z      Bj   Ki   4W=Z=\\GY.Z(Z!Z.[ M\\#Q'Z+] KZ  MZ +\\'Z"
5325       "'Z(Z :Z \"Z 4Z8] @Z 2Y:Y:Y(Y4ZNZ%\\*[ KZ *\\+\\!Z/[ \"[ 7Y 7Z-Z >ZMZ DZMW4WMZ![7Z 3Z 7\\  BX  AX  MY        NY3"
5326       "[ F\\4Z FZ &Z3\\ HZ  ?Y 3Z4\\ GY1Y 5Y  NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y  5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ "
5327       "3YMY 3[  CY  X  Y     !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\<Z(ZCYCY FY9Y HYAXBZ K\\3Z KZ3Z 2Y 2"
5328       "Y6Z LZ(Z H\\ $Z (\\3[ I~R MZ(Z :Z ;\\+\\ MY  ;X  ;X @\\NXN\\ @X1X B\\MXN\\   $Z 2ZBU -Z *~Q 4Z  X  I]   :W9U;V "
5329       "  5XAX  CX   MV NV              AZ3Z 1WEW          LXAX 2X8s+W,Y JW8t#\\ 7Z(Z%Z'Y#Z(Z$Y'Z$Z(Z$Z(Z4Z1Z 6[#Q NZ !"
5330       "Z  Z !Z  >Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3"
5331       "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z  <Y  Y  NX  Y 4Z3Z GX0X HZ3Z GZ3Z IZ3[ IZ3Z GZ3Z 6\\ 5`9Z HY4[ GY4["
5332       " GZ5[ GZ5\\ :YMY :\\4Z ;XMZ     +Y@X@Y#Z)Z\"Y(Y\"Y(Y#Z)Z 9X %ZMZ     %Z  F_   )^GSG^ NfIRHe @g -e ;X.Y GZ6Z CY9"
5333       "Z 7ZNY ?[                   %[/R       *X@X $TBY BX;X=X6[.]   /Y  GY    HX        7X +Z7Z 0Z 3\\  0[ (Z  L[ DZ4"
5334       "Z 9[ 3Z2[ &Z      >i   i   2W<Z?]HZ.Y'Z!Z/\\ L\\&S'Z,] JZ  MZ *\\(Z'Z(Z :Z \"Z 4Z7] AZ 2Y JY(Y3e$\\,\\ KZ )\\-"
5335       "\\ Z.Z \"[ 7Y 7[/[ =ZNZ DZNX4XNY![6[ 4Z 7[  AX  AX  MY        NY4\\ F\\4Z F[ &Z5] H[  @Y 2Z6] GY1Y 5Y  NX 0X?[ +"
5336       "Y FY3Y2Y+Y1Y HZ4Z G\\4Z JZ5\\ ;Y  6Y 8Y -Y5\\ ;YKY =XNX=WNY E[B[ 3YNY 4[  BY  X  Y      N_=_ LZ:_ CZ2Y FX;Y >Z4"
5337       "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW  :X  :"
5338       "V >r >V/V @s   #Z 2[CU -Z +[MeL[ 5Z  X  G\\   :W!V   3W@W     7V!W              AZ4[ 1WEW          LW@W 1W7s,X-"
5339       "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z  Z !Z  >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\,"
5340       "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z  =Y  Y  NX  Y "
5341       "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY     )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8"
5342       "X $YMY     %[  F^   '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[                   %[1S   -Y   'X@X ;Q:TCZ CX:X=X"
5343       "5[.]   /Y  HY    HX  NZ    GZ 'X +[8Z 0Z 4\\  0[ 'Z  M\\ CZ6[ 9Z 2[3[ '[ 0Y  Y  ?f   f  BX DW=\\C_J[.Z&Z\"Z0\\ "
5344       "J\\(T'Z._ JZ  MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[  @X"
5345       "  AX  MY        NY5] F]6Z DZ &Z5] G[  AY 2[8^ GY1Y 5Y  NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y  6Y 8Y -Z6\\ ;Z"
5346       "MZ =b=b EZ@Z 3d 5[  AY  X  Y      L[:\\ IZ;` D[4Z FX<Z >Z5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z "
5347       "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU  9X  9T <p <T-T >q   \"Z 1ZCU -Z ,[JaI[ 6Z  X  F\\   :W#V   1"
5348       "V?V     7W#W              @[5[ 1WEW          LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z  Z "
5349       "!Z  >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY"
5350       "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[  >Y  Y  NX  Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ "
5351       "FY6] 9c 9]6Z :d     )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ     %Z  D]   $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\     "
5352       "              '\\3T   -Z   (W?X ;S<TDZ BW8W=W4\\1`   0Y  HY    HX  NZ    GZ 'X *Z9Z /Z 5\\  0\\ 'Z  N\\ B[8[ 8Z"
5353       " 2\\5[ '[ /Z \"[  >d   c  @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ  MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-"
5354       "Z NS*\\ 6Y 6[1[ <e Bc4c\"[3Z 5Z 9\\  @X  AX  MY        NZ6] F^8[ D[ &Z7^ G[  AY 1[:_ GY1Y 5Y  NX 0X=[ -Y FY3Y2Y"
5355       "+Y1Y G[7Z F]7[ HZ7] ;Y  6Y 7Y .Z7] :YMY <a<a EZ>Z 4c 5[  @Y  X  Y      HS3V FZ<a D\\5Z FX<Y =[7[ DZ $Y1Y 9c 7Y4"
5356       "Z H[6\\#Z?WNV>Z%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS  8X  8R :n :R+R <o   !Z "
5357       "1[DU -Z -[F\\F[ 7Z  X  E\\   :W&W   /U>U     6W%W              ?[6\\ 1WEW          LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z"
5358       "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z  Z !Z  >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\"
5359       "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[  ?Y  Y  NX  Y 3[7[ FX0X G[7[ E[7[ FZ7[ F"
5360       "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b     '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d     %Z  C\\    ?S 2\\ETD"
5361       "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\                   '\\5U   -Z   (W?W :U>TE[ CX8X?X3\\3b   1Y  IY    GX  NZ    GZ ("
5362       "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[  ;a   `  =Z EX<nNd,Z$Y\"Z2] H^.W'Z2a HZ  MZ (^,Z'Z(Z :Z \""
5363       "Z 4Z4] DZ 2Y JY(Y2d\"]3^ IZ ']3] MZ-[ U-] 6Y 5\\4\\ ;d Bb2b#[2[ 6Z :\\  ?X  @X  NY        MZ8^ F^8Z B[ '[9_ F[,"
5364       "P 7Y 1\\<` GY1Y 5Y  NX 0X<[ .Y FY3Y2Y+Y1Y G[8[ F^9[ G[9^ ;Y *Q/Z 7Y -Z9^ :YMY <a;` F[>[ 4b 6[  ?Y  X  Y        "
5365       "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ"
5366       "(Z :Z 8]3] FQ  7X  7P 8l 8P)P :m    Z 0[EU -Z .[?P?[ 8Z  X  D[   9W(W   -T<S     5X)X              >\\8] 1WEW  "
5367       "        LS<T 0W5s-W.X HX6t'\\ 5Z$Y'Z%Z'[%Z(Z%Z&Z%Z(Z%Z6Z0Z 4^.W NZ !Z  Z !Z  >Z !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3"
5368       "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q "
5369       "G\\-Q G\\-Q 5Y  Y  NX  Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b     &[2["
5370       " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c     &Z  B\\    ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :]                   (\\7V   "
5371       "-Z   )X@X :W@TF[ BW7X?X3]6e   1X  IY    GX  NZ    GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[  8^ "
5372       "  ^  ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ  MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a"
5373       "\"Z0[ 7Z ;\\  >X  @X  NY        MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y  NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ "
5374       "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[  >Y  X  Y        F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#["
5375       "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\  Z #^A] :n DZ(Z :Z 7]5]   +X    Mj   (k    NZ 0\\FUBP ;Z /[,[ "
5376       "9Z  X  CZ   8X+W   *R;R     4X+X              =]:^ 1WEW          LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z"
5377       "8Z/Z 3_2Y NZ !Z  Z !Z  >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #"
5378       "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y  Y  NX  Y 1[:[ EX0X F\\;\\ C\\;[ C[:"
5379       "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a     %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c     'Z  "
5380       "@[    @T  JT  _ %] 7X.Y D^D^ BZ6Y 6b 9_                   *];X   -Z   )X@X :ZCTH] CX7YAX1^:h   2Y  JY    GX  NZ"
5381       "    GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[  5[   [  8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ  MZ &`4^"
5382       "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[  =X  @X  NY        M[<a Fa>\\ @]7R"
5383       " D\\=a E]4U 7Y /]Bc GY1Y 5Y  NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[  >Y  "
5384       "X  Y        E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J"
5385       "Z$Z N[  NZ \"^C^ 7g @Z(Z :Z 7_9_   +X    Lh   &i    MZ /]HUDR ;Z .Y*Y 8Z  X  BZ   8Y/X   (Q:Q     2X/Y         "
5386       "     <^<` 2WEW          LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z  Z !Z  >Z !Z !Z \"Z :"
5387       "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[<a G[<a G[=a F[<a G[<a G[<a,[=ZL\\"
5388       "4V'\\7S B\\4V E]5V E]5V E]5V 5Y  Y  NX  Y 1\\=\\ DX0X E\\=\\ A\\=\\ C]>] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[="
5389       "` E[=a 8a 8`=\\ 6`     #]:] H]9] G]:] G]:] H]9] 4W !a     'Z  ?Z    ?U  KT  N] $] 7X.Y Cv AZ6Z 7a 7a           "
5390       "        -_?Z   -Z   )W?X :^GTK_ CX5XAX0_>k   3Y  JX    FX  NZ    GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX"
5391       "@_ ,Z \"[  3Y   X  5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^<a 4Y "
5392       "3_>_ 8b @a2a$[.[ 8Z <~` AX  ?X  Y        L\\@c Fb@] ?^<U C]Ac D^9X 7Y /aI[NY GY1Y 5Y  NX 0X9\\ 2Y FY3Y2Y+Y1Y E]"
5393       "@] Db@\\ C]Ab ;Y *X9\\ 5] 1\\Ac 9c :_:_ GZ9[ 5` 7[  =Y  X  Y        E]DZM[ Hb@] BXB[ 8]A^ @]8T EY1Y 7_ 7Z1Y I`@"
5394       "b#]EXLXE\\!]JYK^ CY9Y E_JXJ_ HcA] C]A] ,] 4[B\\ K~c!~T FZ 3oDo A[ :Z(Z :Z 6a?a   *X    Kf   $g    LZ .^JUGU H~U"
5395       " JW(W 7Z  X  AY   7Z3Y   &P9P     1Y3Y     <~d       3`@b 2WEW          LP9P .X MV(W/X GX MW#\\ 3Z\"Z*Z#Z)Z\"Z*"
5396       "Z#Z)[#Z*Z#Z9Z.~T+b=_ N~T J~T I~S I~S 7Z !Z !Z \"Z :~V KY0b N`>` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>"
5397       "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^<V B_:Y E_:Y E_:Y D^:Y 5Y  Y  NX  Y 0]@] DX0X D]A]"
5398       " @]@] A]@] A]A^ A]@] 2\\ 2]@] B]Ab E]Ab D\\Ab D\\Ac 7_ 7b@\\ 5`     \"_@_ F_?_ E_@_ E_@_ F_?_ 3W !a     'Z  ?Z "
5399       "   ?U  KT  M\\ #[ 6X.Y Bu AY5Z 7a 6f                   2aE]   -Z   )W?W 9~ BW4YCY/bFp   3X  KY    FX  NZ    GZ "
5400       ")X %^G^ >} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[  0V   U  2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H"
5401       "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX  ?X  Y        L^DZNY FYNZF_ =`CY B^EZNY CaB] 7"
5402       "Y .qMY GY1Y 5Y  NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y  X  Y       "
5403       " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :"
5404       "Z 5dGd   )X    Jd   \"e    KZ -`MUKY H~U IU&U 6Z  X  AY   5Z7Z          LZ7Z     ;~d       3cFk 8WEW           "
5405       " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd"
5406       " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]"
5407       " DaA] DaA] DaA] 5Y  Y  NX  Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_   7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^"
5408       "      dLd CdMd BdLd CdLd DeMd 2X !`     %X  =Y    ?U  LV  MZ !Y 5X.Y As AZ4Y 6` 5~]                  )x   -Z   "
5409       "*X@X 9} BX3YFZ-{L]   4Y  LY    FX  NZ    GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[  -S   S  0Z BW8aG[%[\"Z$~R"
5410       " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX  ?X  Y        KsN"
5411       "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y  NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y  X "
5412       " Y        CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~   'X  "
5413       "  Ib    c    JZ ,u H~U HS$S 5Z  X  AY   4\\>\\          I]>\\     :~d       3~Y 8WEW            CW KV)W0X FX LW"
5414       "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} "
5415       "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y  Y  NX  Y .v BX0X Cw =w >v >w =w   8{ ?qMX CqMX C"
5416       "qMX CqMY 6] 6YNr 3^      My Ay @y @z Ay 1X  _     $V  <X    ?V  LV  LX  NW 4X.Y @p ?Z4Z 7_ 2~[                 "
5417       " (v   ,Z   *X@X 9| AW1[K[+yJ]   5Y  LX    EX  NZ    GZ )X #r =} I~S I| \"Z Bz 8t 6Z *y Bt )Z \"[  *P   P  -Z BX"
5418       "6[DX\"Z Z%~Q <~P&~R @~S FZ \"~T$Z(Z :Z \"Z 4Z.] J~Q)Y JY(Y/a K| CZ !{ GZ*[#~Q 1Y 1{ 4_ =_0_%[*[ :Z =~a AX  >X !"
5419       "Y        JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y  NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6"
5420       "] 5~P 2Y  X  Y        CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z"
5421       " :Z 2{   &X    H`    Ma    IZ +t H~U GQ\"Q 4Z  X  AY   2aLb          FaKa     8~d       3YNlN_ 8WEW            "
5422       "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM"
5423       "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y  Y  NX  Y -t AX0X Bu ;u <t <u ;u   "
5424       "8{ >pLX CpLX CpLX BoLY 6] 6YMp 1]      Lv >w =v =v >w 0X  _     #T  ;X    ?W  MV  LW  LV 4X.Y ?n >Y3Z 7_ 1~Z   "
5425       "               't   +Z   *W?X 8y @X1j)vG]   5X  MY    EX  NZ    GZ *X !p <} I~S Iz  Z By 6r 5Z )w As (Z \"[    "
5426       "    5Z AX  HZ Z%~ 9|$~P >~S FZ  ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ  x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX  "
5427       ">X !Y        JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y  NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i <mKY 7_ 7]8] IZ"
5428       "3[ 6\\ 5~P 2Y  X  Y        BmH_ N{ <k 0r 9v EY1Y 6] 8Z.Z KYNkM_&kHk Jw ?Y8a Jy CYKn =s &b 9n H~e\"~T FZ 3oDo @Z"
5429       " :Z(Z :Z 1y   %X    G^    K_    HZ *s H~U   *Z  X  AY   1t          Bs     6~d       3YNkM_ 8WEW            DW "
5430       "JV+X0o.X KW%Z 0Z Z-Z NZ,Z Z-[ Z,Z Z-[ Z<Z-~T&| K~T J~T I~S I~S 7Z !Z !Z \"Z :~P EY.` Iy @y @y @y @y DQ Q$YKy @x"
5431       " >x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y  Y  NX  Y ,r @X0X As 9s :r :s 9s   7z <"
5432       "nKX BnKX BnKX BnKY 6] 6YLn 0\\      Jt ;s :t ;t ;s .X  N]     !R  9V    >W  NX  LU  KU 3X.Y >l =Y2Y 7_ /~X     "
5433       "             %p   )Z   *W?W 4u @X/i(tE]   6Y  NX    DX  NZ    GZ *X  m :} I~S Iy  NZ Bw 2o 5Z 'u @r 'Z \"Z     "
5434       "   4Z AY  J[ Z%} 6x\"} <~S FZ  N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z  Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX  >X"
5435       " !Y        InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y  NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2"
5436       "[ 7\\ 5~P 2Y  X  Y        AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z "
5437       ":Z(Z :Z /u   #X    F\\    I]    GZ )r H~U   *Z  X  AY   /p          >o     4~d       3YMiK^ 8WEW            EX "
5438       "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv <v =v =u =v   BXHu =v"
5439       " <v =u <u .Z 2Z #YIo CmJY CmJY CmJX BnKY CmJY CmJY(oAx!r <x ?x ?x ?x 5Y  Y  NX  Y +p ?X0X ?p 7p 7p 7p 7p   6WNp"
5440       " 9lJX AlJX AlJX AlJY 5[ 5YKl /\\      Hp 8q 7p 7p 8q -X  N]      NP  9V    ?Y  X  KS  IS 2X.Y <h <Z2Y 6^ -~V   "
5441       "               $n   (Z   +X@X 1o =W-f$pB]   6X  NX    DX  Z    FZ *X  Nk 9} I~S Iw  LZ Bv 0m 4Z %q >p %Z \"Z   "
5442       "     4Z @X  JZ MZ&{ 3u z 9~S FZ  Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z  Lr BZ(Z!y -Y -s /] <^.]&[&[ <Z =~a AX "
5443       " =X \"Y        GjIY FYJj 2p =iIY =u 6Y 'dGY GY1Y 5Y  NX 0X3\\ 8Y FY3Y2Y+Y1Y >m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7"
5444       "\\ K[0Z 6Z 4~P 2Y  X  Y        ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o"
5445       "Do @Z :Z(Z :Z .s   \"X    EZ    G[    FZ 'p H~U   *Z  X  AY   ,k          :k     2~d       3YLgJ^ 8WEW         "
5446       "   EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r "
5447       "  AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y  Y  NX  Y *m =X0X >m 3m 5n 5m"
5448       " 3m   6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z      El 3k 2l 3l 4l *X  N\\        5U    ?Y  Y  KR  HQ 1X.Y 9b 9Y1Z 7"
5449       "] )~S                  \"j   &Z   +X@X -h ;X+c!l?\\   6X  Y    DX  Z    FZ +X  Kh 8} I~S Fr  JZ As ,i 3[ $n ;m "
5450       "#Z \"Y        3Z ?X  KZ MZ&x -p Mu 4~S FZ  Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn <Z  Jn @Z([ Nt +Y +o ,\\ ;].]&Z$"
5451       "[ =Z =~a AX  =X \"Y        FhHY FYHf .m ;gHY ;p 3Y %`EY GY1Y 5Y  NX 0X2\\ 9Y FY3Y2Y+Y1Y =j <YHf 5gHX ;Y (q &d 9"
5452       "fGY 6] 5[6\\ KZ.Z 7Z 4~P 2Y  X  Y        >gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i  L^ 4e "
5453       "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n    NX    DX    EY    EZ %m G~U   *Z  X  BZ   )e          4e     /~d       3YKeH] 8"
5454       "WEW            FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4"
5455       "n 5n 6o 6n   @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y  Y  NX  Y (i ;X0X "
5456       "<i 0j 1j 1j 1j   5XIi 3fGX >fGX >fGX >fGY 4Y 4YHf +Z      Bg /g .g -g /g (X  M[        5T    ?Z !Z  JP   'X.Y 5"
5457       "[ 6Y0Y 7] &~P                   Ne   $Z   +W?X '] 6W)a Mh<\\   7Y !X    CX  Y    EZ +X  Id 6} I~S Cm  HZ =l 'e "
5458       "1Z  i 6h !Z #Z        3Z ?Y  M[ M['s &k Jo .~S FZ  Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z  Gl AZ'Z Jm (Y (i )\\ "
5459       ";].]'[#Z =Z =~a AX  =X \"Y        DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y  NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y"
5460       " $k  ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y  X  Y        ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e  "
5461       "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i    LX    CV    CW    DZ #h D~U   *Z  X -R9Z   #[          *[     *~d       3"
5462       "YIaE\\ 8WEW            GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o "
5463       "5Y,^ Ai /h 0i 0i 0i   >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y  Y  NX  Y"
5464       " &e 9X0X :e ,f -f -e ,f   4XFe 0cEX <bEX <bEX <bEY 4Y 4YFb )Z      ?` (a '` '` (a %X          'T               "
5465       "       L{                   K_          0T 4X&[ Ga    AX \"Y      :Y      EX  G_      Ie   #e !_    c /a    EY "
5466       "         EY       Hc        ?e      FZ          +b    Ni   )d    Nc            (X  =Y #Y        A^  J^ %a /]  N"
5467       "c    ;Y      NX          *` 7YD^ ,]CX   1c    ^        /Y    DY  X  Y        8] 1YF] *\\  N` %c  DY 4Y   *YG\\A"
5468       "X J\\;] 9e  A^ =` 7YC] *_    G[                 >a             NU    CZ  N`        9X -T<[                     "
5469       "         LYG]BX 5WEW   %U        HW  NX  MX  GZ                 (d                       +b (b )b )a )b   9V;a "
5470       ")c *c *c *c      =a 4_ &^ %^ $^ &_ &_ :_/c <b +c *c *c *c          3_    K_ &` '` '_ &`   1WB_ *] $] $^ %^  NZ "
5471       "4YD^ 'Y      6Q  HQ  GQ  FQ  HQ  LX          &S                                          DQ          )T 4W Q :Q"
5472       "    9Y #X      :Y      EX  ?Q      8R    ?R  @Q    @R  MQ    =Y          DY       @R        -Q      <Z         "
5473       " #R    >RM]   !R    <R             X  <X #Y        ;Q  <Q  GR !Q  @Q    2Y      MX          #R 0Y=Q  Q=X   'Q  "
5474       "  @Q        *Z    DY  X  Y          ;Y  <P  AQ  CQ  ;Y 4Y   *YAQ8P @Q0Q -Y  8X 7Y 3Y=Q  LQ                     "
5475       " JQ                 4Z  IU        3X -W@[                              KYAQ8P 1WEW   $U        IV  MW  LW  FZ  "
5476       "                V                        KQ  GR  HQ  GQ  GQ   0T2Q  HR  GR  HR  HQ      ,Q %Q  GP  GQ  FQ  GQ  "
5477       "GP *P NQ ,V  MQ  GR  HR  HR          #Q    =Q  FQ  HR  HQ  FQ   *V:Q  LQ  GQ  GQ  GQ  GY 3Y=Q !Y               "
5478       "  9X                                                                MT        +X #X      :Y      EX            "
5479       "              5X          BZ                      7Y               7]                     8X  <X #Y            "
5480       "          HY      MX            0Y  'X                 MY    CY  X  Y          ;Y         8Y 4Y   *Y    1Y    E"
5481       "X 3Y                                          CZ  IU        3X -\\I_                              KY  8WEW   $V"
5482       "              %Z                  NU                                    0R                                 #V  "
5483       "                                  )T           <Z 3Y  =Y                 8X                                    "
5484       "                            MT        +X $X      9X      DX                          6Y          AZ NR         "
5485       "            =Z               6\\                     8X  <X #Y                      HY      MX            0Y  '"
5486       "X                 NZ    CY  X  X          :Y         8Y 4Y   *Y    1Y    EX 3Y                                 "
5487       "         CZ  IU        3X -q                              JY  8WEW   #V              &Z                  NV    "
5488       "                                                                  0V                                    (R     "
5489       "      <Y 2Y  =Y                 8X                                                                MT        *X "
5490       "%X      9X      EY                          6X          @[!T                     >Z               5\\          "
5491       "           9X  ;X $Y                      HY      NY            0Y  'X                 NY    BY  X !Y          "
5492       ":Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X -p              "
5493       "                IY  8WEW   #V              &Z                  MV                                              "
5494       "                        0U                                    'P           ;Y 2Y  >Z                 8X        "
5495       "                                                        MT        *X &X      9X      DX                        "
5496       "  5X          ?\\%W                     ?Z               4\\                     :X  ;X $Y                     "
5497       " IZ      NY            0Y  'X                 NY    BZ !X !Y          :Y         8Y 4Y   *Y    1Y    EX 3Y     "
5498       "                                     CZ  IU        3X -o                              HY  8WEW   \"V           "
5499       "   'Z                  LU                                                                      0V              "
5500       "                                  CZ 2Y  >Y                 7X                                                 "
5501       "               MT        )X 'X      9X      DX                          5W          <\\(X                     ?"
5502       "Z               3\\                     ;Y  <X $Y                      IY      MY            0Y  'X            "
5503       "     Z    AY !X !Y          :Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  I"
5504       "U        3X -n                              GY  8WEW   \"V              '[3Q                 <V                "
5505       "                                                      0V                                                DY 1Y  "
5506       "?Z                 7X                                                                MT        )X (X      8W   "
5507       "   CX                          6X          ;],[                     AZ               1\\                     <e"
5508       "  GX 2f                      JZ      MY            0Y  'X                 Y    @Z \"X \"Z          :Y         8"
5509       "Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X ,k                          "
5510       "    EY  8WEW   !V              'Z4R                 <V                                                         "
5511       "             0V                                                EZ 1Y  ?Y                 ARGX                  "
5512       "                                              MT        (X )X      8W      DX                          5W      "
5513       "    9^1^                     AZ               0\\                     =e  GX 2f                      KZ      LY"
5514       "            0Y  'X                !Z    @[ #X #Z          9Y         8Y 4Y   *Y    1Y    EX 3Y                 "
5515       "                         CZ  IU        3X )f                              CY  8WEW   !V              '[7T      "
5516       "           ;V                                                                      1V                          "
5517       "                      EY 0Y  ?Y                 FWGW                                                           "
5518       "     LT        'W *X      8W      CX                          5W          8`7`                     A[          "
5519       "     /\\                     >e  GX 2f                      KZ      LY            0Y  'X                !Y    >"
5520       "\\ %X &]          9Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3"
5521       "X $^                              @Y  8WEW   !V              '\\:V                 ;V                          "
5522       "                                            1W                                                GZ 0Y  @Z        "
5523       "         FWHX                                                                LT        'X +W      7W           "
5524       "                      V          5b?c                     A[               -\\                     ?e   !f     "
5525       "                <P2\\      MY            /Y  'X                \"Z    >f /X 0g          9Y         8Y 4Y   *Y  "
5526       "  1Y    EX 3Y                                          CZ  IU        3X                                5Y      "
5527       " NV              &\\=X                 ;V                                                                      "
5528       "1W                                                GY /Y  AZ                 EWHX                               "
5529       "                                 LT        &W ,X      7V                                 V          3~T        "
5530       "             A]               ,\\                     @e   !f                     <R5\\      LY            /Y  "
5531       "'X                #Z    =f /X 0f          8Y         8Y 4Y   *Y    1Y    EX 3Y                                 "
5532       "         CZ  IU        3X                                5Y       NW              '^B[                 <W      "
5533       "                                                                1W                                             "
5534       "   HZ /Y  AZ                 DWIX                                                                LT        &X -"
5535       "W      6U                                 NV          1~P                     B_               *\\             "
5536       "        Ae   !f                     <U:]      LZ            /Y  'X                #Z    <e /X 0e          7Y   "
5537       "      8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X                      "
5538       "          5Y       X              &aJ_                 <W                                                      "
5539       "                2X                                                IZ .Y  BZ                 CWJY               "
5540       "                                                 LT        %X /X                                               "
5541       "   7|                     Hf               )\\                     Be   !f                     <X?_      N[    "
5542       "        .Y  'X                %[    :d /X 0e          7Y         8Y 4Y   *Y    1Y    EX 3Y                     "
5543       "                     CZ  IU        3X                                5Y      -PDX              %v              "
5544       "   JQDX                                                                      ?QEY                              "
5545       "                  J[ .Y  D\\                 CXLY                                                              "
5546       "  KT                                                             7x                     Fe                     "
5547       "                                          -_Me     %b            .Y  'X                /e    9c /X 0c          "
5548       "5Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X                 "
5549       "               5Y      -d              $u                 Je                                                   "
5550       "                   ?d                                               $d -Y  Ne                 Ad               "
5551       "                                                 KT                                                            "
5552       " 5s                     Cd                                                               ,v     %b            -"
5553       "Y  'X                0e    6a /X 0b          4Y         8Y 4Y   *Y    1Y    EX 3Y                              "
5554       "            CZ  IU        3X                                5Y      -d              #t                 Jd      "
5555       "                                                                >d                                             "
5556       "  %e -Y  Nd                 @c                                                                                 "
5557       "                                            (m                     @c                                          "
5558       "                     +u     $b            -Y  'X                0d    2^ /X 0_          1Y         8Y 4Y   *Y  "
5559       "  1Y    EX 3Y                                          CZ  IT        2X                                5Y      "
5560       "-c              !q                 Hd                                                                      >c  "
5561       "                                             $d ,Y  Nd                 ?b                                      "
5562       "                                                                                       %g                     ="
5563       "b                                                               *t     #a            ,Y  'X                0d  "
5564       "  ,X /X 0Y          +Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ          '"
5565       "X                                5Y      -c               Nm                 Fc                                "
5566       "                                      =c                                               $c +Y  Nc               "
5567       "  >a                                                                                                           "
5568       "                   M\\                     8a                                             \"~Y                1"
5569       "r     !`            +Y  'X                0c      1X            1Y         8Y 4Y   *Y    1Y    EX 3Y           "
5570       "                               CZ          &W                                5Y      -b               Lj       "
5571       "          Db                                                                      <b                           "
5572       "                    #b *Y  Nb                 <_                                                               "
5573       "                                                                                    (_                         "
5574       "                     ~Y                1q      _            *Y  'X                0b      0X            1Y     "
5575       "    8Y 4Y   *Y    1Y    EX 3Y                                          CZ                                      "
5576       "     3Y      -`               He                 A`                                                            "
5577       "          :`                                               !a )Y  Na                 :]                        "
5578       "                                                                                                               "
5579       "            ']                                              M~Y                .l      M]            (Y  'X    "
5580       "            0`      .X            1Y         8Y 4Y   *Y    1Y    EX 3Y                                         "
5581       "                                             KY      *Z               B^                 9Z                    "
5582       "                                                  5Z                                                M` (Y  N`  "
5583       "               8Z                                                                                              "
5584       "                                                     %X                                              H~Y       "
5585       "         *d      I[            &Y  'X                0^      ,X            1Y         8Y 4Y   *Y    1Y    EX 3Y"
5586       "                                                                                      KY                       "
5587       "                                                                                                               "
5588       "                         H^ &Y  N]                 3V                                                          "
5589       "                                                                                                               "
5590       "                         B~Y                #X      CU            !X  &X                /Y      (X            1"
5591       "Y         7X 4X   )X    0Y    EX 2X                                                                            "
5592       "          KY                                                                                                   "
5593       "                                                            HZ \"X  MY                                         "
5594       "                                                                                                               "
5595       "                                                            J~Y                                                "
5596       "               9X                                                                                              "
5597       "                                                                                                               "
5598       "                                                                                                               "
5599       "                                                                                                               "
5600       "                                                                          3~Y                                  "
5601       "                             9X                                                                                "
5602       "                                                                                                               "
5603       "                                                                                                               "
5604       "                                                                                                               "
5605       "                                                                                        3~Y                    "
5606       "                                           9X                                                                  "
5607       "                                                                                                               "
5608       "                                                                                                               "
5609       "  '" };
5610 
5611     // Define a 40x38 'danger' color logo (used by cimg::dialog()).
5612     static const unsigned char logo40x38[4576] = {
5613       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,
5614       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,
5615       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,
5616       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,
5617       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,
5618       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,
5619       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,
5620       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,
5621       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,
5622       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,
5623       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,
5624       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,
5625       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,
5626       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,
5627       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,
5628       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,
5629       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,
5630       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,
5631       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,
5632       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,
5633       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,
5634       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,
5635       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,
5636       123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
5637 
5638     //! Get/set default output stream for the \CImg library messages.
5639     /**
5640        \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
5641        \return Currently used output stream.
5642     **/
5643     inline std::FILE* output(std::FILE *file) {
5644       cimg::mutex(1);
5645       static std::FILE *res = cimg::_stderr();
5646       if (file) res = file;
5647       cimg::mutex(1,0);
5648       return res;
5649     }
5650 
5651     // Return number of available CPU cores.
5652     inline unsigned int nb_cpus() {
5653       unsigned int res = 1;
5654 #if cimg_OS==2
5655       SYSTEM_INFO sysinfo;
5656       GetSystemInfo(&sysinfo);
5657       res = (unsigned int)sysinfo.dwNumberOfProcessors;
5658 #elif cimg_OS == 1
5659       res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
5660 #endif
5661       return res?res:1U;
5662     }
5663 
5664     // Lock/unlock mutex for CImg multi-thread programming.
5665     inline int mutex(const unsigned int n, const int lock_mode) {
5666       switch (lock_mode) {
5667       case 0 : cimg::Mutex_attr().unlock(n); return 0;
5668       case 1 : cimg::Mutex_attr().lock(n); return 0;
5669       default : return cimg::Mutex_attr().trylock(n);
5670       }
5671     }
5672 
5673     //! Display a warning message on the default output stream.
5674     /**
5675        \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
5676        \note If configuration macro \c cimg_strict_warnings is set, this function throws a
5677        \c CImgWarningException instead.
5678        \warning As the first argument is a format string, it is highly recommended to write
5679        \code
5680        cimg::warn("%s",warning_message);
5681        \endcode
5682        instead of
5683        \code
5684        cimg::warn(warning_message);
5685        \endcode
5686        if \c warning_message can be arbitrary, to prevent nasty memory access.
5687     **/
5688     inline void warn(const char *const format, ...) {
5689       if (cimg::exception_mode()>=1) {
5690         char *const message = new char[16384];
5691         std::va_list ap;
5692         va_start(ap,format);
5693         cimg_vsnprintf(message,16384,format,ap);
5694         va_end(ap);
5695 #ifdef cimg_strict_warnings
5696         throw CImgWarningException(message);
5697 #else
5698         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
5699 #endif
5700         delete[] message;
5701       }
5702     }
5703 
5704     // Execute an external system command.
5705     /**
5706        \param command C-string containing the command line to execute.
5707        \param module_name Module name.
5708        \return Status value of the executed command, whose meaning is OS-dependent.
5709        \note This function is similar to <tt>std::system()</tt>
5710        but it does not open an extra console windows
5711        on Windows-based systems.
5712     **/
5713     inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
5714       cimg::unused(module_name);
5715 #ifdef cimg_no_system_calls
5716       return -1;
5717 #else
5718       if (is_verbose) return std::system(command);
5719 #if cimg_OS==1
5720       const unsigned int l = (unsigned int)std::strlen(command);
5721       if (l) {
5722         char *const ncommand = new char[l + 24];
5723         std::memcpy(ncommand,command,l);
5724         std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent
5725         const int out_val = std::system(ncommand);
5726         delete[] ncommand;
5727         return out_val;
5728       } else return -1;
5729 #elif cimg_OS==2
5730       PROCESS_INFORMATION pi;
5731       STARTUPINFOA si;
5732       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
5733       std::memset(&si,0,sizeof(STARTUPINFO));
5734       GetStartupInfoA(&si);
5735       si.cb = sizeof(si);
5736       si.wShowWindow = SW_HIDE;
5737       si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
5738       const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi);
5739       if (res) {
5740         WaitForSingleObject(pi.hProcess,INFINITE);
5741         CloseHandle(pi.hThread);
5742         CloseHandle(pi.hProcess);
5743         return 0;
5744       } else {
5745         char* lpMsgBuf;
5746 
5747         // Get the error message.
5748         DWORD errorCode = GetLastError();
5749         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
5750                        0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0);
5751         cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s",
5752                    module_name==0?"(null)":module_name,
5753                    command==0?"(null)":command,
5754                    errorCode,lpMsgBuf);
5755         return -1;
5756       }
5757 #else
5758       return std::system(command);
5759 #endif
5760 #endif
5761     }
5762 
5763     //! Return a reference to a temporary variable of type T.
5764     template<typename T>
5765     inline T& temporary(const T&) {
5766       static T temp;
5767       return temp;
5768     }
5769 
5770     //! Exchange values of variables \c a and \c b.
5771     template<typename T>
5772     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
5773 
5774     //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
5775     template<typename T1, typename T2>
5776     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
5777       cimg::swap(a1,b1); cimg::swap(a2,b2);
5778     }
5779 
5780     //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
5781     template<typename T1, typename T2, typename T3>
5782     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
5783       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
5784     }
5785 
5786     //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
5787     template<typename T1, typename T2, typename T3, typename T4>
5788     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
5789       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
5790     }
5791 
5792     //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
5793     template<typename T1, typename T2, typename T3, typename T4, typename T5>
5794     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
5795       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
5796     }
5797 
5798     //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
5799     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
5800     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) {
5801       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
5802     }
5803 
5804     //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
5805     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
5806     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,
5807                      T7& a7, T7& b7) {
5808       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
5809     }
5810 
5811     //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
5812     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
5813     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,
5814                      T7& a7, T7& b7, T8& a8, T8& b8) {
5815       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
5816     }
5817 
5818     //! Return the endianness of the current architecture.
5819     /**
5820        \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
5821     **/
5822     inline bool endianness() {
5823       const int x = 1;
5824       return ((unsigned char*)&x)[0]?false:true;
5825     }
5826 
5827     //! Reverse endianness of all elements in a memory buffer.
5828     /**
5829        \param[in,out] buffer Memory buffer whose endianness must be reversed.
5830        \param size Number of buffer elements to reverse.
5831     **/
5832     template<typename T>
5833     inline void invert_endianness(T* const buffer, const cimg_ulong size) {
5834       if (size) switch (sizeof(T)) {
5835         case 1 : break;
5836         case 2 : {
5837           for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
5838             const unsigned short val = *(--ptr);
5839             *ptr = (unsigned short)((val>>8) | ((val<<8)));
5840           }
5841         } break;
5842         case 4 : {
5843           for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
5844             const unsigned int val = *(--ptr);
5845             *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
5846           }
5847         } break;
5848         case 8 : {
5849           const cimg_uint64
5850             m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
5851             m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
5852           for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
5853             const cimg_uint64 val = *(--ptr);
5854             *ptr =  (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
5855                      ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
5856           }
5857         } break;
5858         default : {
5859           for (T* ptr = buffer + size; ptr>buffer; ) {
5860             unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
5861             for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
5862           }
5863         }
5864         }
5865     }
5866     inline void invert_endianness(bool* const, const cimg_ulong) {}
5867     inline void invert_endianness(unsigned char* const, const cimg_ulong) {}
5868     inline void invert_endianness(char* const, const cimg_ulong) {}
5869 
5870     //! Reverse endianness of a single variable.
5871     /**
5872        \param[in,out] a Variable to reverse.
5873        \return Reference to reversed variable.
5874     **/
5875     template<typename T>
5876     inline T& invert_endianness(T& a) {
5877       invert_endianness(&a,1);
5878       return a;
5879     }
5880 
5881     // Conversion functions to get more precision when trying to store unsigned ints values as floats.
5882     inline unsigned int float2uint(const float f) {
5883       int tmp = 0;
5884       std::memcpy(&tmp,&f,sizeof(float));
5885       if (tmp>=0) return (unsigned int)f;
5886       unsigned int u;
5887       // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
5888       std::memcpy(&u,&f,sizeof(float));
5889       return ((u)<<2)>>2; // set sign & exponent bit to 0
5890     }
5891 
5892     inline float uint2float(const unsigned int u) {
5893       if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287)
5894       float f;
5895       const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
5896       // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
5897       std::memcpy(&f,&v,sizeof(float));
5898       return f;
5899     }
5900 
5901     //! Return the value of a system timer, with a millisecond precision.
5902     /**
5903        \note The timer does not necessarily starts from \c 0.
5904     **/
5905     inline cimg_uint64 time() {
5906 #if cimg_OS==1
5907       struct timeval st_time;
5908       gettimeofday(&st_time,0);
5909       return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000;
5910 #elif cimg_OS==2
5911       ULARGE_INTEGER ul;
5912       FILETIME ft;
5913       GetSystemTimeAsFileTime(&ft);
5914       ul.LowPart = ft.dwLowDateTime;
5915       ul.HighPart = ft.dwHighDateTime;
5916       return (cimg_uint64)ul.QuadPart/10000;
5917 #else
5918       return 0;
5919 #endif
5920     }
5921 
5922     // Implement a tic/toc mechanism to display elapsed time of algorithms.
5923     inline cimg_uint64 tictoc(const bool is_tic);
5924 
5925     //! Start tic/toc timer for time measurement between code instructions.
5926     /**
5927        \return Current value of the timer (same value as time()).
5928     **/
5929     inline cimg_uint64 tic() {
5930       return cimg::tictoc(true);
5931     }
5932 
5933     //! End tic/toc timer and displays elapsed time from last call to tic().
5934     /**
5935        \return Time elapsed (in ms) since last call to tic().
5936     **/
5937     inline cimg_uint64 toc() {
5938       return cimg::tictoc(false);
5939     }
5940 
5941     //! Sleep for a given numbers of milliseconds.
5942     /**
5943        \param milliseconds Number of milliseconds to wait for.
5944        \note This function frees the CPU resources during the sleeping time.
5945        It can be used to temporize your program properly, without wasting CPU time.
5946     **/
5947     inline void sleep(const unsigned int milliseconds) {
5948 #if cimg_OS==1
5949       struct timespec tv;
5950       tv.tv_sec = milliseconds/1000;
5951       tv.tv_nsec = (milliseconds%1000)*1000000;
5952       nanosleep(&tv,0);
5953 #elif cimg_OS==2
5954       Sleep(milliseconds);
5955 #else
5956       cimg::unused(milliseconds);
5957 #endif
5958     }
5959 
5960     inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) {
5961       if (!*p_timer) *p_timer = cimg::time();
5962       const cimg_uint64 current_time = cimg::time();
5963       if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; }
5964       const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time);
5965       *p_timer = current_time + time_diff;
5966       cimg::sleep(time_diff);
5967       return time_diff;
5968     }
5969 
5970     //! Wait for a given number of milliseconds since the last call to wait().
5971     /**
5972        \param milliseconds Number of milliseconds to wait for.
5973        \return Number of milliseconds elapsed since the last call to wait().
5974        \note Same as sleep() with a waiting time computed with regard to the last call
5975        of wait(). It may be used to temporize your program properly, without wasting CPU time.
5976     **/
5977     inline unsigned int wait(const unsigned int milliseconds) {
5978       cimg::mutex(3);
5979       static cimg_uint64 timer = cimg::time();
5980       cimg::mutex(3,0);
5981       return cimg::wait(milliseconds,&timer);
5982     }
5983 
5984     // Custom random number generator (allow re-entrance).
5985     inline cimg_uint64& rng() { // Used as a shared global number for rng
5986       static cimg_uint64 rng = 0xB16B00B5U;
5987       return rng;
5988     }
5989 
5990     inline unsigned int _rand(cimg_uint64 *const p_rng) {
5991       *p_rng = *p_rng*1103515245 + 12345U;
5992       return (unsigned int)*p_rng;
5993     }
5994 
5995     inline unsigned int _rand() {
5996       cimg::mutex(4);
5997       const unsigned int res = cimg::_rand(&cimg::rng());
5998       cimg::mutex(4,0);
5999       return res;
6000     }
6001 
6002     inline void srand(cimg_uint64 *const p_rng) {
6003 #if cimg_OS==1
6004       *p_rng = cimg::time() + (cimg_uint64)getpid();
6005 #elif cimg_OS==2
6006       *p_rng = cimg::time() + (cimg_uint64)_getpid();
6007 #endif
6008     }
6009 
6010     inline void srand() {
6011       cimg::mutex(4);
6012       cimg::srand(&cimg::rng());
6013       cimg::mutex(4,0);
6014     }
6015 
6016     inline void srand(const cimg_uint64 seed) {
6017       cimg::mutex(4);
6018       cimg::rng() = seed;
6019       cimg::mutex(4,0);
6020     }
6021 
6022     inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) {
6023       const double val = cimg::_rand(p_rng)/(double)~0U;
6024       return val_min + (val_max - val_min)*val;
6025     }
6026 
6027     inline double rand(const double val_min, const double val_max) {
6028       cimg::mutex(4);
6029       const double res = cimg::rand(val_min,val_max,&cimg::rng());
6030       cimg::mutex(4,0);
6031       return res;
6032     }
6033 
6034     inline double rand(const double val_max, cimg_uint64 *const p_rng) {
6035       const double val = cimg::_rand(p_rng)/(double)~0U;
6036       return val_max*val;
6037     }
6038 
6039     inline double rand(const double val_max=1) {
6040       cimg::mutex(4);
6041       const double res = cimg::rand(val_max,&cimg::rng());
6042       cimg::mutex(4,0);
6043       return res;
6044     }
6045 
6046     inline double grand(cimg_uint64 *const p_rng) {
6047       double x1, w;
6048       do {
6049         const double x2 = cimg::rand(-1,1,p_rng);
6050         x1 = cimg::rand(-1,1,p_rng);
6051         w = x1*x1 + x2*x2;
6052       } while (w<=0 || w>=1.);
6053       return x1*std::sqrt((-2*std::log(w))/w);
6054     }
6055 
6056     inline double grand() {
6057       cimg::mutex(4);
6058       const double res = cimg::grand(&cimg::rng());
6059       cimg::mutex(4,0);
6060       return res;
6061     }
6062 
6063     inline unsigned int prand(const double z, cimg_uint64 *const p_rng) {
6064       if (z<=1.e-10) return 0;
6065       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z);
6066       unsigned int k = 0;
6067       const double y = std::exp(-z);
6068       for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng);
6069       return k - 1;
6070     }
6071 
6072     inline unsigned int prand(const double z) {
6073       cimg::mutex(4);
6074       const unsigned int res = cimg::prand(z,&cimg::rng());
6075       cimg::mutex(4,0);
6076       return res;
6077     }
6078 
6079     //! Cut (i.e. clamp) value in specified interval.
6080     template<typename T, typename t>
6081     inline T cut(const T& val, const t& val_min, const t& val_max) {
6082       return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val;
6083     }
6084 
6085     //! Bitwise-rotate value on the left.
6086     template<typename T>
6087     inline T rol(const T& a, const unsigned int n=1) {
6088       return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
6089     }
6090 
6091     inline float rol(const float a, const unsigned int n=1) {
6092       return (float)rol((int)a,n);
6093     }
6094 
6095     inline double rol(const double a, const unsigned int n=1) {
6096       return (double)rol((cimg_long)a,n);
6097     }
6098 
6099     inline double rol(const long double a, const unsigned int n=1) {
6100       return (double)rol((cimg_long)a,n);
6101     }
6102 
6103 #ifdef cimg_use_half
6104     inline half rol(const half a, const unsigned int n=1) {
6105       return (half)rol((int)a,n);
6106     }
6107 #endif
6108 
6109     //! Bitwise-rotate value on the right.
6110     template<typename T>
6111     inline T ror(const T& a, const unsigned int n=1) {
6112       return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
6113     }
6114 
6115     inline float ror(const float a, const unsigned int n=1) {
6116       return (float)ror((int)a,n);
6117     }
6118 
6119     inline double ror(const double a, const unsigned int n=1) {
6120       return (double)ror((cimg_long)a,n);
6121     }
6122 
6123     inline double ror(const long double a, const unsigned int n=1) {
6124       return (double)ror((cimg_long)a,n);
6125     }
6126 
6127 #ifdef cimg_use_half
6128     inline half ror(const half a, const unsigned int n=1) {
6129       return (half)ror((int)a,n);
6130     }
6131 #endif
6132 
6133     //! Return absolute value of a value.
6134     template<typename T>
6135     inline T abs(const T& a) {
6136       return a>=0?a:-a;
6137     }
6138     inline bool abs(const bool a) {
6139       return a;
6140     }
6141     inline int abs(const unsigned char a) {
6142       return (int)a;
6143     }
6144     inline int abs(const unsigned short a) {
6145       return (int)a;
6146     }
6147     inline int abs(const unsigned int a) {
6148       return (int)a;
6149     }
6150     inline int abs(const int a) {
6151       return std::abs(a);
6152     }
6153     inline cimg_int64 abs(const cimg_uint64 a) {
6154       return (cimg_int64)a;
6155     }
6156     inline double abs(const double a) {
6157       return std::fabs(a);
6158     }
6159     inline float abs(const float a) {
6160       return (float)std::fabs((double)a);
6161     }
6162 
6163     //! Return hyperbolic arcosine of a value.
6164     inline double acosh(const double x) {
6165 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6166       return std::acosh(x);
6167 #else
6168       return std::log(x + std::sqrt(x*x - 1));
6169 #endif
6170     }
6171 
6172     //! Return hyperbolic arcsine of a value.
6173     inline double asinh(const double x) {
6174 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6175       return std::asinh(x);
6176 #else
6177       return std::log(x + std::sqrt(x*x + 1));
6178 #endif
6179     }
6180 
6181     //! Return hyperbolic arctangent of a value.
6182     inline double atanh(const double x) {
6183 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6184       return std::atanh(x);
6185 #else
6186       return 0.5*std::log((1. + x)/(1. - x));
6187 #endif
6188     }
6189 
6190     //! Return the sinc of a given value.
6191     inline double sinc(const double x) {
6192       return x?std::sin(x)/x:1;
6193     }
6194 
6195     //! Return base-2 logarithm of a value.
6196     inline double log2(const double x) {
6197 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6198       return std::log2(x);
6199 #else
6200       const double base2 = std::log(2.);
6201       return std::log(x)/base2;
6202 #endif
6203     }
6204 
6205     //! Return square of a value.
6206     template<typename T>
6207     inline T sqr(const T& val) {
6208       return val*val;
6209     }
6210 
6211     // Return inverse of error function.
6212     template<typename T>
6213     inline T erfinv(const T& val) {
6214       const T
6215         sgn = val<0?-1:1,
6216         x = (1 - val)*(1 + val),
6217         lnx = std::log(x),
6218         tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx),
6219         tt2 = lnx/(T)0.147;
6220       return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2));
6221     }
6222 
6223     //! Return cubic root of a value.
6224     template<typename T>
6225     inline double cbrt(const T& x) {
6226 #if cimg_use_cpp11==1
6227       return std::cbrt(x);
6228 #else
6229       return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3);
6230 #endif
6231     }
6232 
6233     template<typename T>
6234     inline T pow3(const T& val) {
6235       return val*val*val;
6236     }
6237     template<typename T>
6238     inline T pow4(const T& val) {
6239       return val*val*val*val;
6240     }
6241 
6242     //! Return the minimum between three values.
6243     template<typename t>
6244     inline t min(const t& a, const t& b, const t& c) {
6245       return std::min(std::min(a,b),c);
6246     }
6247 
6248     //! Return the minimum between four values.
6249     template<typename t>
6250     inline t min(const t& a, const t& b, const t& c, const t& d) {
6251       return std::min(std::min(a,b),std::min(c,d));
6252     }
6253 
6254     //! Return the minabs between two values.
6255     template<typename t>
6256     inline t minabs(const t& a, const t& b) {
6257       return cimg::abs(b)<cimg::abs(a)?b:a;
6258     }
6259 
6260     template<typename t>
6261     inline t minabs(const t& a, const t& b, const t& abs_b) {
6262       return abs_b<cimg::abs(a)?b:a;
6263     }
6264 
6265     //! Return the maximum between three values.
6266     template<typename t>
6267     inline t max(const t& a, const t& b, const t& c) {
6268       return std::max(std::max(a,b),c);
6269     }
6270 
6271     //! Return the maximum between four values.
6272     template<typename t>
6273     inline t max(const t& a, const t& b, const t& c, const t& d) {
6274       return std::max(std::max(a,b),std::max(c,d));
6275     }
6276 
6277     //! Return the maxabs between two values.
6278     template<typename t>
6279     inline t maxabs(const t& a, const t& b) {
6280       return cimg::abs(b)>cimg::abs(a)?b:a;
6281     }
6282 
6283     template<typename t>
6284     inline t maxabs(const t& a, const t& b, const t& abs_b) {
6285       return abs_b>cimg::abs(a)?b:a;
6286     }
6287 
6288     //! Return the sign of a value.
6289     template<typename T>
6290     inline T sign(const T& x) {
6291       return (T)(cimg::type<T>::is_nan(x)?0:x<0?-1:x>0);
6292     }
6293 
6294     //! Return the nearest power of 2 higher than given value.
6295     template<typename T>
6296     inline cimg_uint64 nearest_pow2(const T& x) {
6297       cimg_uint64 i = 1;
6298       while (x>i) i<<=1;
6299       return i;
6300     }
6301 
6302     //! Return the modulo of a value.
6303     /**
6304        \param x Input value.
6305        \param m Modulo value.
6306        \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
6307     **/
6308     template<typename T>
6309     inline T mod(const T& x, const T& m) {
6310       const double dx = (double)x, dm = (double)m;
6311       if (!cimg::type<double>::is_finite(dm)) return x;
6312       if (cimg::type<double>::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm));
6313       return (T)0;
6314     }
6315     inline int mod(const bool x, const bool m) {
6316       return m?(x?1:0):0;
6317     }
6318     inline int mod(const unsigned char x, const unsigned char m) {
6319       return x%m;
6320     }
6321     inline int mod(const char x, const char m) {
6322 #if defined(CHAR_MAX) && CHAR_MAX==255
6323       return x%m;
6324 #else
6325       return x>=0?x%m:(x%m?m + x%m:0);
6326 #endif
6327     }
6328     inline int mod(const unsigned short x, const unsigned short m) {
6329       return (int)(x%m);
6330     }
6331     inline int mod(const short x, const short m) {
6332       return (int)(x>=0?x%m:(x%m?m + x%m:0));
6333     }
6334     inline int mod(const unsigned int x, const unsigned int m) {
6335       return (int)(x%m);
6336     }
6337     inline int mod(const int x, const int m) {
6338       return (int)(x>=0?x%m:(x%m?m + x%m:0));
6339     }
6340     inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
6341       return (cimg_int64)(x%m);
6342     }
6343     inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
6344       return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0));
6345     }
6346 
6347     //! Return the min-mod of two values.
6348     /**
6349        \note <i>minmod(\p a,\p b)</i> is defined to be:
6350        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
6351        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
6352     **/
6353     template<typename T>
6354     inline T minmod(const T& a, const T& b) {
6355       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
6356     }
6357 
6358     template<typename T>
6359     inline T round(const T& x) {
6360       return (T)std::floor((_cimg_Tfloat)x + 0.5f);
6361     }
6362 
6363     template<typename T>
6364     inline int uiround(const T x) {
6365       return cimg::type<T>::is_float()?(int)(x + 0.5f):(int)x;
6366     }
6367 
6368     //! Return rounded value.
6369     /**
6370        \param x Value to be rounded.
6371        \param y Rounding precision.
6372        \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
6373        \return Rounded value, having the same type as input value \c x.
6374     **/
6375     template<typename T>
6376     inline T round(const T& x, const double y, const int rounding_type=0) {
6377       if (y<=0) return x;
6378       if (y==1) switch (rounding_type) {
6379         case 0 : return cimg::round(x);
6380         case 1 : return (T)std::ceil((_cimg_Tfloat)x);
6381         default : return (T)std::floor((_cimg_Tfloat)x);
6382         }
6383       const double sx = (double)x/y, floor = std::floor(sx), delta =  sx - floor;
6384       return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
6385     }
6386 
6387     // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
6388     // (contribution by RawTherapee: http://rawtherapee.com/).
6389     template<typename T>
6390     inline T median(T val0, T val1) {
6391       return (val0 + val1)/2;
6392     }
6393 
6394     template<typename T>
6395     inline T median(T val0, T val1, T val2) {
6396       return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
6397     }
6398 
6399     template<typename T>
6400     inline T median(T val0, T val1, T val2, T val3, T val4) {
6401       T tmp = std::min(val0,val1);
6402       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
6403       val3 = std::max(val0,tmp);  val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
6404       val1 = tmp; tmp = std::min(val2,val3);
6405       return std::max(val1,tmp);
6406     }
6407 
6408     template<typename T>
6409     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
6410       T tmp = std::min(val0,val5);
6411       val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
6412       tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
6413       val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
6414       val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
6415       val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
6416       tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
6417       return std::min(val3,val4);
6418     }
6419 
6420     template<typename T>
6421     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
6422       T tmp = std::min(val1,val2);
6423       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
6424       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
6425       val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
6426       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
6427       val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
6428       val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
6429       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
6430       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
6431       val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
6432       val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
6433       val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
6434       tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
6435       return std::min(val4,val2);
6436     }
6437 
6438     template<typename T>
6439     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,
6440                     T val12) {
6441       T tmp = std::min(val1,val7);
6442       val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
6443       tmp = std::min(val3,val4);  val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
6444       val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
6445       val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
6446       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
6447       tmp = std::min(val4,val6);  val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
6448       val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
6449       tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
6450       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
6451       tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
6452       val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
6453       tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
6454       tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
6455       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
6456       tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
6457       val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
6458       tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
6459       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
6460       val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
6461       val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
6462       val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
6463       return std::max(val5,val6);
6464     }
6465 
6466     template<typename T>
6467     inline T median(T val0, T val1, T val2, T val3, T val4,
6468                     T val5, T val6, T val7, T val8, T val9,
6469                     T val10, T val11, T val12, T val13, T val14,
6470                     T val15, T val16, T val17, T val18, T val19,
6471                     T val20, T val21, T val22, T val23, T val24) {
6472       T tmp = std::min(val0,val1);
6473       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
6474       val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
6475       val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
6476       tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
6477       tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
6478       val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
6479       tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
6480       val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
6481       tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
6482       val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
6483       tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
6484       val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
6485       tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
6486       val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
6487       tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
6488       val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
6489       tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
6490       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
6491       val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
6492       val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
6493       val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
6494       val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
6495       val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
6496       val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
6497       val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
6498       val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
6499       val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
6500       val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
6501       val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
6502       val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
6503       val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
6504       val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
6505       tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
6506       val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
6507       tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
6508       tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
6509       val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
6510       tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
6511       val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
6512       val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
6513       val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
6514       val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
6515       val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
6516       tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
6517       val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
6518       val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
6519       tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
6520       val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
6521       val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
6522       tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
6523       tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
6524       val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
6525       val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
6526       val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
6527       tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
6528       val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
6529       tmp = std::min(val10,val20);
6530       return std::max(tmp,val12);
6531     }
6532 
6533     template<typename T>
6534     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
6535                     T val7, T val8, T val9, T val10, T val11, T val12, T val13,
6536                     T val14, T val15, T val16, T val17, T val18, T val19, T val20,
6537                     T val21, T val22, T val23, T val24, T val25, T val26, T val27,
6538                     T val28, T val29, T val30, T val31, T val32, T val33, T val34,
6539                     T val35, T val36, T val37, T val38, T val39, T val40, T val41,
6540                     T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
6541       T tmp = std::min(val0,val32);
6542       val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
6543       tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
6544       val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
6545       tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
6546       val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
6547       tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
6548       val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
6549       val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
6550       tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
6551       val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
6552       val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
6553       tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
6554       val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
6555       val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
6556       val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
6557       tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
6558       val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
6559       tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
6560       val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
6561       val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
6562       tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
6563       val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
6564       val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
6565       tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
6566       val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
6567       val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
6568       tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
6569       val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
6570       val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
6571       tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
6572       val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
6573       val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
6574       tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
6575       val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
6576       val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
6577       tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
6578       val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
6579       tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
6580       val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
6581       tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
6582       val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
6583       tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
6584       val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
6585       val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
6586       tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
6587       val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
6588       val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
6589       tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
6590       val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
6591       val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
6592       tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
6593       val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
6594       val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
6595       tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
6596       val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
6597       val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
6598       tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
6599       val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
6600       val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
6601       tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
6602       val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
6603       val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
6604       tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
6605       val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
6606       val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
6607       tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
6608       val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
6609       val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
6610       tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
6611       val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
6612       val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
6613       tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
6614       val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
6615       val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
6616       val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
6617       tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
6618       val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
6619       val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
6620       tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
6621       val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
6622       val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
6623       tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
6624       val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
6625       val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
6626       tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
6627       val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
6628       val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
6629       tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
6630       val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
6631       val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
6632       tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
6633       val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
6634       val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
6635       tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
6636       val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
6637       val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
6638       tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
6639       val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
6640       val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
6641       tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
6642       val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
6643       val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
6644       tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
6645       val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
6646       val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
6647       tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
6648       val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
6649       val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
6650       tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
6651       val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
6652       val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
6653       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
6654       tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
6655       val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
6656       val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
6657       tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
6658       val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
6659       val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
6660       tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
6661       val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
6662       val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
6663       tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
6664       val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
6665       val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
6666       tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
6667       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
6668       tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
6669       val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
6670       tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
6671       val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
6672       val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
6673       tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
6674       val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
6675       val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
6676       tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
6677       val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
6678       val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
6679       tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
6680       val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
6681       val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
6682       tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
6683       val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
6684       val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
6685       val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
6686       tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
6687       val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
6688       val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
6689       tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
6690       val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
6691       val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
6692       tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
6693       val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
6694       val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
6695       tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
6696       val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
6697       val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
6698       tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
6699       val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
6700       val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
6701       tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
6702       val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
6703       val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
6704       val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
6705       tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
6706       val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
6707       val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
6708       tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
6709       val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
6710       val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
6711       tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
6712       val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
6713       val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
6714       tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
6715       val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
6716       val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
6717       tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
6718       val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
6719       val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
6720       val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
6721       tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
6722       val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
6723       val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
6724       tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
6725       val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
6726       val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
6727       tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
6728       val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
6729       val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
6730       tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
6731       val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
6732       val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
6733       tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
6734       val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
6735       val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
6736       val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
6737       val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
6738       val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
6739       val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
6740       val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
6741       val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
6742       val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
6743       val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
6744       val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
6745       val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
6746       val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
6747       val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
6748       val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
6749       val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
6750       val24 = std::max(val21,val24); val23 = std::min(val23,val26);
6751       return std::max(val23,val24);
6752     }
6753 
6754     //! Return sqrt(x^2 + y^2).
6755     template<typename T>
6756     inline T hypot(const T x, const T y) {
6757       return std::sqrt(x*x + y*y);
6758     }
6759 
6760     template<typename T>
6761     inline T hypot(const T x, const T y, const T z) {
6762       return std::sqrt(x*x + y*y + z*z);
6763     }
6764 
6765     template<typename T>
6766     inline T _hypot(const T x, const T y) { // Slower but more precise version
6767       T nx = cimg::abs(x), ny = cimg::abs(y), t;
6768       if (nx<ny) { t = nx; nx = ny; } else t = ny;
6769       if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); }
6770       return 0;
6771     }
6772 
6773     //! Return the factorial of n
6774     inline double factorial(const int n) {
6775       if (n<0) return cimg::type<double>::nan();
6776       if (n<2) return 1;
6777       double res = 2;
6778       for (int i = 3; i<=n; ++i) res*=i;
6779       return res;
6780     }
6781 
6782     //! Return the number of permutations of k objects in a set of n objects.
6783     inline double permutations(const int k, const int n, const bool with_order) {
6784       if (n<0 || k<0) return cimg::type<double>::nan();
6785       if (k>n) return 0;
6786       double res = 1;
6787       for (int i = n; i>=n - k + 1; --i) res*=i;
6788       return with_order?res:res/cimg::factorial(k);
6789     }
6790 
6791     inline double _fibonacci(int exp) {
6792       double
6793         base = (1 + std::sqrt(5.))/2,
6794         result = 1/std::sqrt(5.);
6795       while (exp) {
6796         if (exp&1) result*=base;
6797         exp>>=1;
6798         base*=base;
6799       }
6800       return result;
6801     }
6802 
6803     //! Calculate fibonacci number.
6804     // (Precise up to n = 78, less precise for n>78).
6805     inline double fibonacci(const int n) {
6806       if (n<0) return cimg::type<double>::nan();
6807       if (n<3) return 1;
6808       if (n<11) {
6809         cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
6810         for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
6811         return (double)fn;
6812       }
6813       if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
6814         return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
6815 
6816       if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
6817         cimg_uint64
6818           fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL)
6819           fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL
6820           fn = 0;
6821         for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
6822         return (double)fn;
6823       }
6824       return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
6825     }
6826 
6827     //! Calculate greatest common divisor.
6828     inline long gcd(long a, long b) {
6829       while (a) { const long c = a; a = b%a; b = c; }
6830       return b;
6831     }
6832 
6833     //! Convert character to lower case.
6834     inline char lowercase(const char x) {
6835       return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
6836     }
6837     inline double lowercase(const double x) {
6838       return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
6839     }
6840 
6841     //! Convert C-string to lower case.
6842     inline void lowercase(char *const str) {
6843       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
6844     }
6845 
6846     //! Convert character to upper case.
6847     inline char uppercase(const char x) {
6848       return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
6849     }
6850 
6851     inline double uppercase(const double x) {
6852       return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
6853     }
6854 
6855     //! Convert C-string to upper case.
6856     inline void uppercase(char *const str) {
6857       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
6858     }
6859 
6860     //! Return \c true if input character is blank (space, tab, or non-printable character).
6861     inline bool is_blank(const char c) {
6862       return c>=0 && (unsigned char)c<=' ';
6863     }
6864 
6865     //! Read value in a C-string.
6866     /**
6867        \param str C-string containing the float value to read.
6868        \return Read value.
6869        \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
6870        as in <em>"1/2"</em>.
6871     **/
6872     inline double atof(const char *const str) {
6873       double x = 0, y = 1;
6874       return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
6875     }
6876 
6877     //! Compare the first \p l characters of two C-strings, ignoring the case.
6878     /**
6879        \param str1 C-string.
6880        \param str2 C-string.
6881        \param l Number of characters to compare.
6882        \return \c 0 if the two strings are equal, something else otherwise.
6883        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
6884     **/
6885     inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
6886       if (!l) return 0;
6887       if (!str1) return str2?-1:0;
6888       const char *nstr1 = str1, *nstr2 = str2;
6889       int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
6890       return k!=l?diff:0;
6891     }
6892 
6893     //! Compare two C-strings, ignoring the case.
6894     /**
6895        \param str1 C-string.
6896        \param str2 C-string.
6897        \return \c 0 if the two strings are equal, something else otherwise.
6898        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
6899     **/
6900     inline int strcasecmp(const char *const str1, const char *const str2) {
6901       if (!str1) return str2?-1:0;
6902       const int
6903         l1 = (int)std::strlen(str1),
6904         l2 = (int)std::strlen(str2);
6905       return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
6906     }
6907 
6908     //! Ellipsize a string.
6909     /**
6910        \param str C-string.
6911        \param l Max number of characters.
6912        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
6913     **/
6914     inline char *strellipsize(char *const str, const unsigned int l=64,
6915                               const bool is_ending=true) {
6916       if (!str) return str;
6917       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
6918       if (ls<=nl) return str;
6919       if (is_ending) std::strcpy(str + nl - 5,"(...)");
6920       else {
6921         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6922         std::strcpy(str + ll,"(...)");
6923         std::memmove(str + ll + 5,str + ls - lr,lr);
6924       }
6925       str[nl] = 0;
6926       return str;
6927     }
6928 
6929     //! Ellipsize a string.
6930     /**
6931        \param str C-string.
6932        \param res output C-string.
6933        \param l Max number of characters.
6934        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
6935     **/
6936     inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
6937                               const bool is_ending=true) {
6938       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
6939       if (ls<=nl) { std::strcpy(res,str); return res; }
6940       if (is_ending) {
6941         std::strncpy(res,str,nl - 5);
6942         std::strcpy(res + nl -5,"(...)");
6943       } else {
6944         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6945         std::strncpy(res,str,ll);
6946         std::strcpy(res + ll,"(...)");
6947         std::strncpy(res + ll + 5,str + ls - lr,lr);
6948       }
6949       res[nl] = 0;
6950       return res;
6951     }
6952 
6953     //! Remove delimiters on the start and/or end of a C-string.
6954     /**
6955        \param[in,out] str C-string to work with (modified at output).
6956        \param delimiter Delimiter character code to remove.
6957        \param is_symmetric Tells if the removal is done only if delimiters are symmetric
6958        (both at the beginning and the end of \c s).
6959        \param is_iterative Tells if the removal is done if several iterations are possible.
6960        \return \c true if delimiters have been removed, \c false otherwise.
6961    **/
6962     inline bool strpare(char *const str, const char delimiter,
6963                         const bool is_symmetric, const bool is_iterative) {
6964       if (!str) return false;
6965       const int l = (int)std::strlen(str);
6966       int p, q;
6967       if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
6968           --q; ++p; if (!is_iterative) break;
6969         } else {
6970         for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
6971         for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
6972       }
6973       const int n = q - p + 1;
6974       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6975       return false;
6976     }
6977 
6978     //! Remove white spaces on the start and/or end of a C-string.
6979     inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
6980       if (!str) return false;
6981       const int l = (int)std::strlen(str);
6982       int p, q;
6983       if (is_symmetric) for (p = 0, q = l - 1; p<q && is_blank(str[p]) && is_blank(str[q]); ) {
6984           --q; ++p; if (!is_iterative) break;
6985         } else {
6986         for (p = 0; p<l && is_blank(str[p]); ) { ++p; if (!is_iterative) break; }
6987         for (q = l - 1; q>p && is_blank(str[q]); ) { --q; if (!is_iterative) break; }
6988       }
6989       const int n = q - p + 1;
6990       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6991       return false;
6992     }
6993 
6994     //! Replace reserved characters (for Windows filename) by another character.
6995     /**
6996        \param[in,out] str C-string to work with (modified at output).
6997        \param[in] c Replacement character.
6998     **/
6999     inline void strwindows_reserved(char *const str, const char c='_') {
7000       for (char *s = str; *s; ++s) {
7001         const char i = *s;
7002         if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
7003       }
7004     }
7005 
7006     //! Replace escape sequences in C-strings by character values.
7007     /**
7008        \param[in,out] str C-string to work with (modified at output).
7009     **/
7010     inline void strunescape(char *const str) {
7011 #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
7012 
7013       unsigned char val = 0;
7014       for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
7015             cimg_strunescape('a','\a');
7016             cimg_strunescape('b','\b');
7017             cimg_strunescape('e',0x1B);
7018             cimg_strunescape('f','\f');
7019             cimg_strunescape('n','\n');
7020             cimg_strunescape('r','\r');
7021             cimg_strunescape('t','\t');
7022             cimg_strunescape('v','\v');
7023             cimg_strunescape('\\','\\');
7024             cimg_strunescape('\'','\'');
7025             cimg_strunescape('\"','\"');
7026             cimg_strunescape('\?','\?');
7027           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
7028             val = (unsigned char)(*(ns++) - '0');
7029             if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
7030             if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
7031             *nd = (char)val;
7032             break;
7033           case 'x' : {
7034             char c = lowercase(*(++ns));
7035             if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
7036               val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10);
7037               c = lowercase(*(++ns));
7038               if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
7039                 (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
7040                 ++ns;
7041               }
7042               *nd = (char)val;
7043             } else *nd = c;
7044           } break;
7045           case 'u' : { // UTF-8 BMP
7046             char c1, c2, c3, c4;
7047             if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
7048                 (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
7049                 (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
7050                 (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
7051               c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
7052               c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
7053               c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
7054               c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
7055               const unsigned int ival =
7056                 ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
7057               if (ival<=0x007f) *nd = (char)ival;
7058               else if (ival<=0x07ff) {
7059                 *(nd++) = (char)((ival>>6)|0xc0);
7060                 *nd = (char)((ival&0x3f)|0x80);
7061               } else {
7062                 *(nd++) = (char)((ival>>12)|0xe0);
7063                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7064                 *nd = (char)((ival&0x3f)|0x80);
7065               }
7066               ns+=5;
7067             } else *nd = *(ns++);
7068           } break;
7069           case 'U' : { // UTF-8 astral planes
7070             char c1, c2, c3, c4, c5, c6, c7, c8;
7071             if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
7072                 (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
7073                 (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
7074                 (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
7075                 (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
7076                 (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
7077                 (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
7078                 (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
7079               c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
7080               c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
7081               c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
7082               c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
7083               c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
7084               c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
7085               c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
7086               c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
7087               const unsigned int ival =
7088                 ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
7089                 ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
7090               if (ival<=0x007f) *nd = (char)ival;
7091               else if (ival<=0x07ff) {
7092                 *(nd++) = (char)((ival>>6)|0xc0);
7093                 *nd = (char)((ival&0x3f)|0x80);
7094               } else if (ival<=0xffff) {
7095                 *(nd++) = (char)((ival>>12)|0xe0);
7096                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7097                 *nd = (char)((ival&0x3f)|0x80);
7098               } else {
7099                 *(nd++) = (char)((ival>>18)|0xf0);
7100                 *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
7101                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7102                 *nd = (char)((ival&0x3f)|0x80);
7103               }
7104               ns+=9;
7105             } else *nd = *(ns++);
7106           } break;
7107           default : if (*ns) *nd = *(ns++);
7108           }
7109         else *nd = *(ns++);
7110     }
7111 
7112     // Return a temporary string describing the size of a memory buffer.
7113     inline const char *strbuffersize(const cimg_ulong size);
7114 
7115     // Return string that identifies the running OS.
7116     inline const char *stros() {
7117 #if defined(linux) || defined(__linux) || defined(__linux__)
7118       static const char *const str = "Linux";
7119 #elif defined(sun) || defined(__sun)
7120       static const char *const str = "Sun OS";
7121 #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
7122       static const char *const str = "BSD";
7123 #elif defined(sgi) || defined(__sgi)
7124       static const char *const str = "Irix";
7125 #elif defined(__MACOSX__) || defined(__APPLE__)
7126       static const char *const str = "Mac OS";
7127 #elif defined(unix) || defined(__unix) || defined(__unix__)
7128       static const char *const str = "Generic Unix";
7129 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) || \
7130   defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7131       static const char *const str = "Windows";
7132 #else
7133       const char
7134         *const _str1 = std::getenv("OSTYPE"),
7135         *const _str2 = _str1?_str1:std::getenv("OS"),
7136         *const str = _str2?_str2:"Unknown OS";
7137 #endif
7138       return str;
7139     }
7140 
7141     //! Return the basename of a filename.
7142     inline const char* basename(const char *const s, const char separator=cimg_file_separator)  {
7143       const char *p = 0, *np = s;
7144       while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
7145       return p;
7146     }
7147 
7148     // Return a random filename.
7149     inline const char* filenamerand() {
7150       cimg::mutex(6);
7151       static char randomid[9];
7152       for (unsigned int k = 0; k<8; ++k) {
7153         const int v = (int)cimg::rand(65535)%3;
7154         randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
7155                              (v==1?('a' + ((int)cimg::rand(65535)%26)):
7156                               ('A' + ((int)cimg::rand(65535)%26))));
7157       }
7158       cimg::mutex(6,0);
7159       return randomid;
7160     }
7161 
7162     // Convert filename as a Windows-style filename (short path name).
7163     inline void winformat_string(char *const str) {
7164       if (str && *str) {
7165 #if cimg_OS==2
7166         char *const nstr = new char[MAX_PATH];
7167         if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
7168         delete[] nstr;
7169 #endif
7170       }
7171     }
7172 
7173     // Open a file (similar to std:: fopen(), but with wide character support on Windows).
7174     inline std::FILE *std_fopen(const char *const path, const char *const mode);
7175 
7176 
7177     //! Open a file.
7178     /**
7179        \param path Path of the filename to open.
7180        \param mode C-string describing the opening mode.
7181        \return Opened file.
7182        \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
7183        the specified file cannot be opened, instead of returning \c 0.
7184     **/
7185     inline std::FILE *fopen(const char *const path, const char *const mode) {
7186       if (!path)
7187         throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
7188       if (!mode)
7189         throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
7190                                     path);
7191       std::FILE *res = 0;
7192       if (*path=='-' && (!path[1] || path[1]=='.')) {
7193         res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
7194 #if cimg_OS==2
7195         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode
7196 #ifdef __BORLANDC__
7197           if (setmode(_fileno(res),0x8000)==-1) res = 0;
7198 #else
7199           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
7200 #endif
7201         }
7202 #endif
7203       } else res = cimg::std_fopen(path,mode);
7204       if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
7205                                       path,mode);
7206       return res;
7207     }
7208 
7209     //! Close a file.
7210     /**
7211        \param file File to close.
7212        \return \c 0 if file has been closed properly, something else otherwise.
7213        \note Same as <tt>std::fclose()</tt> but display a warning message if
7214        the file has not been closed properly.
7215     **/
7216     inline int fclose(std::FILE *file) {
7217       if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
7218       if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
7219       const int errn = std::fclose(file);
7220       if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
7221                         errn);
7222       return errn;
7223     }
7224 
7225     //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
7226     inline int fseek(FILE *stream, cimg_long offset, int origin) {
7227 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7228       return _fseeki64(stream,(__int64)offset,origin);
7229 #else
7230       return std::fseek(stream,offset,origin);
7231 #endif
7232     }
7233 
7234     //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
7235     inline cimg_long ftell(FILE *stream) {
7236 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7237       return (cimg_long)_ftelli64(stream);
7238 #else
7239       return (cimg_long)std::ftell(stream);
7240 #endif
7241     }
7242 
7243     // Get the file or directory attributes with support for UTF-8 paths (Windows only).
7244 #if cimg_OS==2
7245     inline DWORD win_getfileattributes(const char *const path);
7246 #endif
7247 
7248     //! Check if a path is a directory.
7249     /**
7250        \param path Specified path to test.
7251     **/
7252     inline bool is_directory(const char *const path) {
7253       if (!path || !*path) return false;
7254 #if cimg_OS==1
7255       struct stat st_buf;
7256       return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
7257 #elif cimg_OS==2
7258       const DWORD res = win_getfileattributes(path);
7259       return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY);
7260 #else
7261       return false;
7262 #endif
7263     }
7264 
7265     //! Check if a path is a file.
7266     /**
7267        \param path Specified path to test.
7268     **/
7269     inline bool is_file(const char *const path) {
7270       if (!path || !*path) return false;
7271 #if cimg_OS==2
7272       const DWORD res = cimg::win_getfileattributes(path);
7273       return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY);
7274 #else
7275       std::FILE *const file = cimg::std_fopen(path,"rb");
7276       if (!file) return false;
7277       cimg::fclose(file);
7278       return !is_directory(path);
7279 #endif
7280     }
7281 
7282     //! Get file size.
7283     /**
7284        \param filename Specified filename to get size from.
7285        \return File size or '-1' if file does not exist.
7286     **/
7287     inline cimg_int64 fsize(const char *const filename) {
7288       std::FILE *const file = cimg::std_fopen(filename,"rb");
7289       if (!file) return (cimg_int64)-1;
7290       std::fseek(file,0,SEEK_END);
7291       const cimg_int64 siz = (cimg_int64)std::ftell(file);
7292       cimg::fclose(file);
7293       return siz;
7294     }
7295 
7296     //! Get last write time of a given file or directory (multiple-attributes version).
7297     /**
7298        \param path Specified path to get attributes from.
7299        \param[in,out] attr Type of requested time attributes.
7300                       Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
7301                       Replaced by read attributes after return (or -1 if an error occurred).
7302        \param nb_attr Number of attributes to read/write.
7303        \return Latest read attribute.
7304     **/
7305     template<typename T>
7306     inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
7307 #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
7308       int res = -1;
7309       if (!path || !*path) { _cimg_fdate_err(); return -1; }
7310       cimg::mutex(6);
7311 #if cimg_OS==2
7312       HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
7313       if (file!=INVALID_HANDLE_VALUE) {
7314         FILETIME _ft;
7315         SYSTEMTIME ft;
7316         if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
7317           for (unsigned int i = 0; i<nb_attr; ++i) {
7318             res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
7319                         attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
7320                         attr[i]==6?ft.wSecond:-1);
7321             attr[i] = (T)res;
7322           }
7323         } else _cimg_fdate_err();
7324         CloseHandle(file);
7325       } else _cimg_fdate_err();
7326 #elif cimg_OS==1
7327       struct stat st_buf;
7328       if (!stat(path,&st_buf)) {
7329         const time_t _ft = st_buf.st_mtime;
7330         const struct tm& ft = *std::localtime(&_ft);
7331         for (unsigned int i = 0; i<nb_attr; ++i) {
7332           res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
7333                       attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
7334                       attr[i]==6?ft.tm_sec:-1);
7335           attr[i] = (T)res;
7336         }
7337       } else _cimg_fdate_err();
7338 #endif
7339       cimg::mutex(6,0);
7340       return res;
7341     }
7342 
7343     //! Get last write time of a given file or directory (single-attribute version).
7344     /**
7345        \param path Specified path to get attributes from.
7346        \param attr Type of requested time attributes.
7347                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
7348        \return Specified attribute or -1 if an error occurred.
7349     **/
7350     inline int fdate(const char *const path, unsigned int attr) {
7351       int out = (int)attr;
7352       return fdate(path,&out,1);
7353     }
7354 
7355     //! Get current local time (multiple-attributes version).
7356     /**
7357        \param[in,out] attr Type of requested time attributes.
7358                            Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
7359                                     7=millisecond }
7360                            Replaced by read attributes after return (or -1 if an error occurred).
7361        \param nb_attr Number of attributes to read/write.
7362        \return Latest read attribute.
7363     **/
7364     template<typename T>
7365     inline int date(T *attr, const unsigned int nb_attr) {
7366       int res = -1;
7367       cimg::mutex(6);
7368 #if cimg_OS==2
7369       SYSTEMTIME st;
7370       GetLocalTime(&st);
7371       for (unsigned int i = 0; i<nb_attr; ++i) {
7372         res = (int)(attr[i]==0?st.wYear:
7373                     attr[i]==1?st.wMonth:
7374                     attr[i]==2?st.wDay:
7375                     attr[i]==3?st.wDayOfWeek:
7376                     attr[i]==4?st.wHour:
7377                     attr[i]==5?st.wMinute:
7378                     attr[i]==6?st.wSecond:
7379                     attr[i]==7?st.wMilliseconds:-1);
7380         attr[i] = (T)res;
7381       }
7382 #else
7383       struct timeval _st;
7384       gettimeofday(&_st,0);
7385       struct tm *st = std::localtime(&_st.tv_sec);
7386       for (unsigned int i = 0; i<nb_attr; ++i) {
7387         res = (int)(attr[i]==0?st->tm_year + 1900:
7388                     attr[i]==1?st->tm_mon + 1:
7389                     attr[i]==2?st->tm_mday:
7390                     attr[i]==3?st->tm_wday:
7391                     attr[i]==4?st->tm_hour:
7392                     attr[i]==5?st->tm_min:
7393                     attr[i]==6?st->tm_sec:
7394                     attr[i]==7?_st.tv_usec/1000:-1);
7395         attr[i] = (T)res;
7396       }
7397 #endif
7398       cimg::mutex(6,0);
7399       return res;
7400     }
7401 
7402     //! Get current local time (single-attribute version).
7403     /**
7404        \param attr Type of requested time attribute.
7405                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
7406                             7=millisecond }
7407        \return Specified attribute or -1 if an error occurred.
7408     **/
7409     inline int date(unsigned int attr) {
7410       int out = (int)attr;
7411       return date(&out,1);
7412     }
7413 
7414     // Get/set path to the \c curl binary.
7415     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
7416 
7417     // Get/set path to the \c dcraw binary.
7418     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
7419 
7420     // Get/set path to the FFMPEG's \c ffmpeg binary.
7421     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
7422 
7423     // Get/set path to the GraphicsMagick's \c gm binary.
7424     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
7425 
7426     // Get/set path to the \c gunzip binary.
7427     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
7428 
7429     // Get/set path to the \c gzip binary.
7430     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
7431 
7432     // Get/set path to the ImageMagick's \c convert binary.
7433     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
7434 
7435     // Get/set path to the Medcon's \c medcon binary.
7436     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
7437 
7438     // Get/set path to store temporary files.
7439     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
7440 
7441     // Get/set path to the \c wget binary.
7442     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
7443 
7444     //! Split filename into two C-strings \c body and \c extension.
7445     /**
7446        filename and body must not overlap!
7447     **/
7448     inline const char *split_filename(const char *const filename, char *const body=0) {
7449       if (!filename) { if (body) *body = 0; return ""; }
7450       const char * p = std::strrchr(filename,'.');
7451       if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension.
7452         if (body) std::strcpy(body,filename);
7453         return filename + std::strlen(filename);
7454       }
7455       const unsigned int l = (unsigned int)(p - filename);
7456       if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
7457       return p + 1;
7458     }
7459 
7460     // Generate a numbered version of a filename.
7461     inline char* number_filename(const char *const filename, const int number,
7462                                  const unsigned int digits, char *const str);
7463 
7464     //! Read data from file.
7465     /**
7466        \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
7467        \param nmemb Number of elements to read.
7468        \param stream File to read data from.
7469        \return Number of read elements.
7470        \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
7471     **/
7472     template<typename T>
7473     inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
7474       if (!ptr || !stream)
7475         throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
7476                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
7477       if (!nmemb) return 0;
7478       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
7479       size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
7480       do {
7481         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
7482         l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
7483         al_read+=l_al_read;
7484         to_read-=l_al_read;
7485       } while (l_to_read==l_al_read && to_read>0);
7486       if (to_read>0)
7487         warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
7488              (unsigned long)al_read,(unsigned long)nmemb);
7489       return al_read;
7490     }
7491 
7492     //! Write data to file.
7493     /**
7494        \param ptr Pointer to memory buffer containing the binary data to write on file.
7495        \param nmemb Number of elements to write.
7496        \param[out] stream File to write data on.
7497        \return Number of written elements.
7498        \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
7499     **/
7500     template<typename T>
7501     inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
7502       if (!ptr || !stream)
7503         throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
7504                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
7505       if (!nmemb) return 0;
7506       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
7507       size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
7508       do {
7509         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
7510         l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
7511         al_write+=l_al_write;
7512         to_write-=l_al_write;
7513       } while (l_to_write==l_al_write && to_write>0);
7514       if (to_write>0)
7515         warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
7516              (unsigned long)al_write,(unsigned long)nmemb);
7517       return al_write;
7518     }
7519 
7520     //! Create an empty file.
7521     /**
7522        \param file Input file (can be \c 0 if \c filename is set).
7523        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
7524     **/
7525     inline void fempty(std::FILE *const file, const char *const filename) {
7526       if (!file && !filename)
7527         throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
7528       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
7529       if (!file) cimg::fclose(nfile);
7530     }
7531 
7532     // Try to guess format from an image file.
7533     inline const char *ftype(std::FILE *const file, const char *const filename);
7534 
7535     // Get or set load from network mode (can be { 0=disabled | 1=enabled }).
7536     inline bool& network_mode(const bool value, const bool is_set) {
7537       static bool mode = true;
7538       if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); }
7539       return mode;
7540     }
7541 
7542     inline bool& network_mode() {
7543       return network_mode(false,false);
7544     }
7545 
7546     // Load file from network as a local temporary file.
7547     inline char *load_network(const char *const url, char *const filename_local,
7548                               const unsigned int timeout=0, const bool try_fallback=false,
7549                               const char *const referer=0);
7550 
7551     //! Return options specified on the command line.
7552     inline const char* option(const char *const name, const int argc, const char *const *const argv,
7553                               const char *const _default, const char *const usage, const bool reset_static) {
7554       static bool first = true, visu = false;
7555       if (reset_static) { first = true; return 0; }
7556       const char *res = 0;
7557       if (first) {
7558         first = false;
7559         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
7560         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
7561         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
7562       }
7563       if (!name && visu) {
7564         if (usage) {
7565           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
7566           std::fprintf(cimg::output(),": %s",usage);
7567           std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
7568         }
7569         if (_default) std::fprintf(cimg::output(),"%s\n",_default);
7570       }
7571       if (name) {
7572         if (argc>0) {
7573           int k = 0;
7574           while (k<argc && std::strcmp(argv[k],name)) ++k;
7575           res = (k++==argc?_default:(k==argc?argv[--k]:argv[k]));
7576         } else res = _default;
7577         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
7578                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",
7579                                         cimg::t_green,usage,cimg::t_normal);
7580       }
7581       return res;
7582     }
7583 
7584     inline const char* option(const char *const name, const int argc, const char *const *const argv,
7585                               const char *const _default, const char *const usage=0) {
7586       return option(name,argc,argv,_default,usage,false);
7587     }
7588 
7589     inline bool option(const char *const name, const int argc, const char *const *const argv,
7590                        const bool _default, const char *const usage=0) {
7591       const char *const s = cimg::option(name,argc,argv,(char*)0);
7592       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):_default;
7593       cimg::option(name,0,0,res?"true":"false",usage);
7594       return res;
7595     }
7596 
7597     inline int option(const char *const name, const int argc, const char *const *const argv,
7598                       const int _default, const char *const usage=0) {
7599       const char *const s = cimg::option(name,argc,argv,(char*)0);
7600       const int res = s?std::atoi(s):_default;
7601       char *const tmp = new char[256];
7602       cimg_snprintf(tmp,256,"%d",res);
7603       cimg::option(name,0,0,tmp,usage);
7604       delete[] tmp;
7605       return res;
7606     }
7607 
7608     inline char option(const char *const name, const int argc, const char *const *const argv,
7609                        const char _default, const char *const usage=0) {
7610       const char *const s = cimg::option(name,argc,argv,(char*)0);
7611       const char res = s?*s:_default;
7612       char tmp[8];
7613       *tmp = res; tmp[1] = 0;
7614       cimg::option(name,0,0,tmp,usage);
7615       return res;
7616     }
7617 
7618     inline float option(const char *const name, const int argc, const char *const *const argv,
7619                         const float _default, const char *const usage=0) {
7620       const char *const s = cimg::option(name,argc,argv,(char*)0);
7621       const float res = s?(float)cimg::atof(s):_default;
7622       char *const tmp = new char[256];
7623       cimg_snprintf(tmp,256,"%g",res);
7624       cimg::option(name,0,0,tmp,usage);
7625       delete[] tmp;
7626       return res;
7627     }
7628 
7629     inline double option(const char *const name, const int argc, const char *const *const argv,
7630                          const double _default, const char *const usage=0) {
7631       const char *const s = cimg::option(name,argc,argv,(char*)0);
7632       const double res = s?cimg::atof(s):_default;
7633       char *const tmp = new char[256];
7634       cimg_snprintf(tmp,256,"%g",res);
7635       cimg::option(name,0,0,tmp,usage);
7636       delete[] tmp;
7637       return res;
7638     }
7639 
7640     //! Print information about \CImg environment variables.
7641     /**
7642        \note Output is done on the default output stream.
7643     **/
7644     inline void info() {
7645       std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
7646                    cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
7647                    cimg::t_normal,cimg_date,cimg_time);
7648 
7649       std::fprintf(cimg::output(),"  > Operating System:         %s%-13s%s %s('cimg_OS'=%d)%s\n",
7650                    cimg::t_bold,
7651                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"),
7652                    cimg::t_normal,cimg::t_green,
7653                    cimg_OS,
7654                    cimg::t_normal);
7655 
7656       std::fprintf(cimg::output(),"  > CPU endianness:           %s%s Endian%s\n",
7657                    cimg::t_bold,
7658                    cimg::endianness()?"Big":"Little",
7659                    cimg::t_normal);
7660 
7661       std::fprintf(cimg::output(),"  > Verbosity mode:           %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
7662                    cimg::t_bold,
7663                    cimg_verbosity==0?"Quiet":
7664                    cimg_verbosity==1?"Console":
7665                    cimg_verbosity==2?"Dialog":
7666                    cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
7667                    cimg::t_normal,cimg::t_green,
7668                    cimg_verbosity,
7669                    cimg::t_normal);
7670 
7671       std::fprintf(cimg::output(),"  > Stricts warnings:         %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
7672                    cimg::t_bold,
7673 #ifdef cimg_strict_warnings
7674                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7675 #else
7676                    "No",cimg::t_normal,cimg::t_green,"undefined",
7677 #endif
7678                    cimg::t_normal);
7679 
7680       std::fprintf(cimg::output(),"  > Support for C++11:        %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
7681                    cimg::t_bold,
7682                    cimg_use_cpp11?"Yes":"No",
7683                    cimg::t_normal,cimg::t_green,
7684                    (int)cimg_use_cpp11,
7685                    cimg::t_normal);
7686 
7687       std::fprintf(cimg::output(),"  > Using VT100 messages:     %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
7688                    cimg::t_bold,
7689 #ifdef cimg_use_vt100
7690                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7691 #else
7692                    "No",cimg::t_normal,cimg::t_green,"undefined",
7693 #endif
7694                    cimg::t_normal);
7695 
7696       std::fprintf(cimg::output(),"  > Display type:             %s%-13s%s %s('cimg_display'=%d)%s\n",
7697                    cimg::t_bold,
7698                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
7699                    cimg::t_normal,cimg::t_green,
7700                    (int)cimg_display,
7701                    cimg::t_normal);
7702 
7703 #if cimg_display==1
7704       std::fprintf(cimg::output(),"  > Using XShm for X11:       %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
7705                    cimg::t_bold,
7706 #ifdef cimg_use_xshm
7707                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7708 #else
7709                    "No",cimg::t_normal,cimg::t_green,"undefined",
7710 #endif
7711                    cimg::t_normal);
7712 
7713       std::fprintf(cimg::output(),"  > Using XRand for X11:      %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
7714                    cimg::t_bold,
7715 #ifdef cimg_use_xrandr
7716                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7717 #else
7718                    "No",cimg::t_normal,cimg::t_green,"undefined",
7719 #endif
7720                    cimg::t_normal);
7721 #endif
7722       std::fprintf(cimg::output(),"  > Using OpenMP:             %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
7723                    cimg::t_bold,
7724 #if cimg_use_openmp!=0
7725                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7726 #else
7727                    "No",cimg::t_normal,cimg::t_green,"undefined",
7728 #endif
7729                    cimg::t_normal);
7730       std::fprintf(cimg::output(),"  > Using PNG library:        %s%-13s%s %s('cimg_use_png' %s)%s\n",
7731                    cimg::t_bold,
7732 #ifdef cimg_use_png
7733                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7734 #else
7735                    "No",cimg::t_normal,cimg::t_green,"undefined",
7736 #endif
7737                    cimg::t_normal);
7738       std::fprintf(cimg::output(),"  > Using JPEG library:       %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
7739                    cimg::t_bold,
7740 #ifdef cimg_use_jpeg
7741                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7742 #else
7743                    "No",cimg::t_normal,cimg::t_green,"undefined",
7744 #endif
7745                    cimg::t_normal);
7746 
7747       std::fprintf(cimg::output(),"  > Using TIFF library:       %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
7748                    cimg::t_bold,
7749 #ifdef cimg_use_tiff
7750                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7751 #else
7752                    "No",cimg::t_normal,cimg::t_green,"undefined",
7753 #endif
7754                    cimg::t_normal);
7755 
7756       std::fprintf(cimg::output(),"  > Using Magick++ library:   %s%-13s%s %s('cimg_use_magick' %s)%s\n",
7757                    cimg::t_bold,
7758 #ifdef cimg_use_magick
7759                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7760 #else
7761                    "No",cimg::t_normal,cimg::t_green,"undefined",
7762 #endif
7763                    cimg::t_normal);
7764 
7765       std::fprintf(cimg::output(),"  > Using FFTW3 library:      %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
7766                    cimg::t_bold,
7767 #ifdef cimg_use_fftw3
7768                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7769 #else
7770                    "No",cimg::t_normal,cimg::t_green,"undefined",
7771 #endif
7772                    cimg::t_normal);
7773 
7774       std::fprintf(cimg::output(),"  > Using LAPACK library:     %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
7775                    cimg::t_bold,
7776 #ifdef cimg_use_lapack
7777                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7778 #else
7779                    "No",cimg::t_normal,cimg::t_green,"undefined",
7780 #endif
7781                    cimg::t_normal);
7782 
7783       char *const tmp = new char[1024];
7784 
7785       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path());
7786       std::fprintf(cimg::output(),"  > Path of 'curl':           %s%-13s%s\n",
7787                    cimg::t_bold,
7788                    tmp,
7789                    cimg::t_normal);
7790 
7791       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path());
7792       std::fprintf(cimg::output(),"  > Path of 'dcraw':          %s%-13s%s\n",
7793                    cimg::t_bold,
7794                    tmp,
7795                    cimg::t_normal);
7796 
7797       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::ffmpeg_path());
7798       std::fprintf(cimg::output(),"  > Path of 'ffmpeg':         %s%-13s%s\n",
7799                    cimg::t_bold,
7800                    tmp,
7801                    cimg::t_normal);
7802 
7803       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
7804       std::fprintf(cimg::output(),"  > Path of 'graphicsmagick': %s%-13s%s\n",
7805                    cimg::t_bold,
7806                    tmp,
7807                    cimg::t_normal);
7808 
7809       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path());
7810       std::fprintf(cimg::output(),"  > Path of 'gunzip':         %s%-13s%s\n",
7811                    cimg::t_bold,
7812                    tmp,
7813                    cimg::t_normal);
7814 
7815       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path());
7816       std::fprintf(cimg::output(),"  > Path of 'gzip':           %s%-13s%s\n",
7817                    cimg::t_bold,
7818                    tmp,
7819                    cimg::t_normal);
7820 
7821       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
7822       std::fprintf(cimg::output(),"  > Path of 'imagemagick':    %s%-13s%s\n",
7823                    cimg::t_bold,
7824                    tmp,
7825                    cimg::t_normal);
7826 
7827       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
7828       std::fprintf(cimg::output(),"  > Path of 'medcon':         %s%-13s%s\n",
7829                    cimg::t_bold,
7830                    tmp,
7831                    cimg::t_normal);
7832 
7833       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
7834       std::fprintf(cimg::output(),"  > Temporary path:           %s%-13s%s\n",
7835                    cimg::t_bold,
7836                    tmp,
7837                    cimg::t_normal);
7838 
7839       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path());
7840       std::fprintf(cimg::output(),"  > Path of 'wget':           %s%-13s%s\n",
7841                    cimg::t_bold,
7842                    tmp,
7843                    cimg::t_normal);
7844 
7845       std::fprintf(cimg::output(),"\n");
7846       delete[] tmp;
7847     }
7848 
7849     // Declare LAPACK function signatures if LAPACK support is enabled.
7850 #ifdef cimg_use_lapack
7851     template<typename T>
7852     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
7853       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
7854     }
7855 
7856     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
7857       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
7858     }
7859 
7860     template<typename T>
7861     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
7862       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
7863     }
7864 
7865     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
7866       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
7867     }
7868 
7869     template<typename T>
7870     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
7871                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
7872       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
7873     }
7874 
7875     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
7876                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
7877       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
7878     }
7879 
7880     template<typename T>
7881     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
7882       int one = 1;
7883       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
7884     }
7885 
7886     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
7887       int one = 1;
7888       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
7889     }
7890 
7891     template<typename T>
7892     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
7893       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
7894     }
7895 
7896     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
7897       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
7898     }
7899 
7900     template<typename T>
7901     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
7902                       T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) {
7903       dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
7904     }
7905 
7906     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
7907                       float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) {
7908       sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
7909     }
7910 
7911 #endif
7912 
7913   } // namespace cimg { ...
7914 
7915   /*------------------------------------------------
7916    #
7917    #
7918    #   Definition of mathematical operators and
7919    #   external functions.
7920    #
7921    #
7922    -------------------------------------------------*/
7923 
7924 #define _cimg_create_operator(typ) \
7925   template<typename T> \
7926   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
7927     return img + val; \
7928   } \
7929   template<typename T> \
7930   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
7931     typedef typename cimg::superset<T,typ>::type Tt; \
7932     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
7933   } \
7934   template<typename T> \
7935   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
7936     return img*val; \
7937   } \
7938   template<typename T> \
7939   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
7940     return val*img.get_invert(); \
7941   } \
7942   template<typename T> \
7943   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
7944     return img & val; \
7945   } \
7946   template<typename T> \
7947   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
7948     return img | val; \
7949   } \
7950   template<typename T> \
7951   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
7952     return img ^ val; \
7953   } \
7954   template<typename T> \
7955   inline bool operator==(const typ val, const CImg<T>& img) {   \
7956     return img == val; \
7957   } \
7958   template<typename T> \
7959   inline bool operator!=(const typ val, const CImg<T>& img) { \
7960     return img != val; \
7961   }
7962 
7963   _cimg_create_operator(bool)
7964   _cimg_create_operator(unsigned char)
7965   _cimg_create_operator(char)
7966   _cimg_create_operator(signed char)
7967   _cimg_create_operator(unsigned short)
7968   _cimg_create_operator(short)
7969   _cimg_create_operator(unsigned int)
7970   _cimg_create_operator(int)
7971   _cimg_create_operator(cimg_uint64)
7972   _cimg_create_operator(cimg_int64)
7973   _cimg_create_operator(float)
7974   _cimg_create_operator(double)
7975   _cimg_create_operator(long double)
7976 
7977   template<typename T>
7978   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
7979     return img + expression;
7980   }
7981 
7982   template<typename T>
7983   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
7984     return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
7985   }
7986 
7987   template<typename T>
7988   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
7989     return img*expression;
7990   }
7991 
7992   template<typename T>
7993   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
7994     return expression*img.get_invert();
7995   }
7996 
7997   template<typename T>
7998   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
7999     return img & expression;
8000   }
8001 
8002   template<typename T>
8003   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
8004     return img | expression;
8005   }
8006 
8007   template<typename T>
8008   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
8009     return img ^ expression;
8010   }
8011 
8012   template<typename T>
8013   inline bool operator==(const char *const expression, const CImg<T>& img) {
8014     return img==expression;
8015   }
8016 
8017   template<typename T>
8018   inline bool operator!=(const char *const expression, const CImg<T>& img) {
8019     return img!=expression;
8020   }
8021 
8022   template<typename T>
8023   inline CImg<T> transpose(const CImg<T>& instance) {
8024     return instance.get_transpose();
8025   }
8026 
8027   template<typename T>
8028   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance, const bool use_LU=true) {
8029     return instance.get_invert(use_LU);
8030   }
8031 
8032   template<typename T>
8033   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance, const bool use_LU=false) {
8034     return instance.get_pseudoinvert(use_LU);
8035   }
8036 
8037 #define _cimg_create_pointwise_function(name) \
8038   template<typename T> \
8039   inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
8040     return instance.get_##name(); \
8041   }
8042 
8043   _cimg_create_pointwise_function(sqr)
8044   _cimg_create_pointwise_function(sqrt)
8045   _cimg_create_pointwise_function(erf)
8046   _cimg_create_pointwise_function(exp)
8047   _cimg_create_pointwise_function(log)
8048   _cimg_create_pointwise_function(log2)
8049   _cimg_create_pointwise_function(log10)
8050   _cimg_create_pointwise_function(abs)
8051   _cimg_create_pointwise_function(sign)
8052   _cimg_create_pointwise_function(cos)
8053   _cimg_create_pointwise_function(sin)
8054   _cimg_create_pointwise_function(sinc)
8055   _cimg_create_pointwise_function(tan)
8056   _cimg_create_pointwise_function(acos)
8057   _cimg_create_pointwise_function(asin)
8058   _cimg_create_pointwise_function(atan)
8059   _cimg_create_pointwise_function(cosh)
8060   _cimg_create_pointwise_function(sinh)
8061   _cimg_create_pointwise_function(tanh)
8062   _cimg_create_pointwise_function(acosh)
8063   _cimg_create_pointwise_function(asinh)
8064   _cimg_create_pointwise_function(atanh)
8065 
8066   /*-----------------------------------
8067    #
8068    # Define the CImgDisplay structure
8069    #
8070    ----------------------------------*/
8071   //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
8072   /**
8073      CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
8074      (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
8075      If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
8076      a minimal mode where warning messages will be outputted each time the program is trying to call one of the
8077      CImgDisplay method.
8078 
8079      The configuration variable \c cimg_display tells about the graphic library used.
8080      It is set automatically by \CImg when one of these graphic libraries has been detected.
8081      But, you can override its value if necessary. Valid choices are:
8082      - 0: Disable display capabilities.
8083      - 1: Use \b X-Window (X11) library.
8084      - 2: Use \b GDI32 library.
8085 
8086      Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
8087   **/
8088   struct CImgDisplay {
8089     cimg_uint64 _timer, _fps_frames, _fps_timer;
8090     unsigned int _width, _height, _normalization;
8091     float _fps_fps, _min, _max;
8092     bool _is_fullscreen;
8093     char *_title;
8094     unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
8095     int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
8096     bool _is_closed, _is_resized, _is_moved, _is_event,
8097       _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
8098       _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
8099       _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
8100       _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
8101       _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
8102       _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
8103       _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
8104       _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
8105       _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
8106       _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
8107       _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
8108       _is_keyPADMUL, _is_keyPADDIV;
8109 
8110     //@}
8111     //---------------------------
8112     //
8113     //! \name Plugins
8114     //@{
8115     //---------------------------
8116 
8117 #ifdef cimgdisplay_plugin
8118 #include cimgdisplay_plugin
8119 #endif
8120 #ifdef cimgdisplay_plugin1
8121 #include cimgdisplay_plugin1
8122 #endif
8123 #ifdef cimgdisplay_plugin2
8124 #include cimgdisplay_plugin2
8125 #endif
8126 #ifdef cimgdisplay_plugin3
8127 #include cimgdisplay_plugin3
8128 #endif
8129 #ifdef cimgdisplay_plugin4
8130 #include cimgdisplay_plugin4
8131 #endif
8132 #ifdef cimgdisplay_plugin5
8133 #include cimgdisplay_plugin5
8134 #endif
8135 #ifdef cimgdisplay_plugin6
8136 #include cimgdisplay_plugin6
8137 #endif
8138 #ifdef cimgdisplay_plugin7
8139 #include cimgdisplay_plugin7
8140 #endif
8141 #ifdef cimgdisplay_plugin8
8142 #include cimgdisplay_plugin8
8143 #endif
8144 
8145     //@}
8146     //--------------------------------------------------------
8147     //
8148     //! \name Constructors / Destructor / Instance Management
8149     //@{
8150     //--------------------------------------------------------
8151 
8152     //! Destructor.
8153     /**
8154        \note If the associated window is visible on the screen, it is closed by the call to the destructor.
8155     **/
8156     ~CImgDisplay() {
8157       assign();
8158       delete[] _keys;
8159       delete[] _released_keys;
8160     }
8161 
8162     //! Construct an empty display.
8163     /**
8164        \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
8165        display of valid data is performed.
8166        \par Example
8167        \code
8168        CImgDisplay disp;  // Does actually nothing
8169        ...
8170        disp.display(img); // Construct new window and display image in it
8171        \endcode
8172     **/
8173     CImgDisplay():
8174       _width(0),_height(0),_normalization(0),
8175       _min(0),_max(0),
8176       _is_fullscreen(false),
8177       _title(0),
8178       _window_width(0),_window_height(0),_button(0),
8179       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8180       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8181       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8182       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8183       assign();
8184     }
8185 
8186     //! Construct a display with specified dimensions.
8187     /** \param width Window width.
8188         \param height Window height.
8189         \param title Window title.
8190         \param normalization Normalization type
8191         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8192         \param is_fullscreen Tells if fullscreen mode is enabled.
8193         \param is_closed Tells if associated window is initially visible or not.
8194         \note A black background is initially displayed on the associated window.
8195     **/
8196     CImgDisplay(const unsigned int width, const unsigned int height,
8197                 const char *const title=0, const unsigned int normalization=3,
8198                 const bool is_fullscreen=false, const bool is_closed=false):
8199       _width(0),_height(0),_normalization(0),
8200       _min(0),_max(0),
8201       _is_fullscreen(false),
8202       _title(0),
8203       _window_width(0),_window_height(0),_button(0),
8204       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8205       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8206       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8207       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8208       assign(width,height,title,normalization,is_fullscreen,is_closed);
8209     }
8210 
8211     //! Construct a display from an image.
8212     /** \param img Image used as a model to create the window.
8213         \param title Window title.
8214         \param normalization Normalization type
8215         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8216         \param is_fullscreen Tells if fullscreen mode is enabled.
8217         \param is_closed Tells if associated window is initially visible or not.
8218         \note The pixels of the input image are initially displayed on the associated window.
8219     **/
8220     template<typename T>
8221     explicit CImgDisplay(const CImg<T>& img,
8222                          const char *const title=0, const unsigned int normalization=3,
8223                          const bool is_fullscreen=false, const bool is_closed=false):
8224       _width(0),_height(0),_normalization(0),
8225       _min(0),_max(0),
8226       _is_fullscreen(false),
8227       _title(0),
8228       _window_width(0),_window_height(0),_button(0),
8229       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8230       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8231       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8232       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8233       assign(img,title,normalization,is_fullscreen,is_closed);
8234     }
8235 
8236     //! Construct a display from an image list.
8237     /** \param list The images list to display.
8238         \param title Window title.
8239         \param normalization Normalization type
8240         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8241         \param is_fullscreen Tells if fullscreen mode is enabled.
8242         \param is_closed Tells if associated window is initially visible or not.
8243         \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
8244     **/
8245     template<typename T>
8246     explicit CImgDisplay(const CImgList<T>& list,
8247                          const char *const title=0, const unsigned int normalization=3,
8248                          const bool is_fullscreen=false, const bool is_closed=false):
8249       _width(0),_height(0),_normalization(0),
8250       _min(0),_max(0),
8251       _is_fullscreen(false),
8252       _title(0),
8253       _window_width(0),_window_height(0),_button(0),
8254       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8255       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8256       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8257       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8258       assign(list,title,normalization,is_fullscreen,is_closed);
8259     }
8260 
8261     //! Construct a display as a copy of an existing one.
8262     /**
8263         \param disp Display instance to copy.
8264         \note The pixel buffer of the input window is initially displayed on the associated window.
8265     **/
8266     CImgDisplay(const CImgDisplay& disp):
8267       _width(0),_height(0),_normalization(0),
8268       _min(0),_max(0),
8269       _is_fullscreen(false),
8270       _title(0),
8271       _window_width(0),_window_height(0),_button(0),
8272       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8273       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8274       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8275       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8276       assign(disp);
8277     }
8278 
8279     //! Take a screenshot.
8280     /**
8281        \param[out] img Output screenshot. Can be empty on input
8282     **/
8283     template<typename T>
8284     static void screenshot(CImg<T>& img) {
8285       return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
8286     }
8287 
8288 #if cimg_display==0
8289 
8290     static void _no_display_exception() {
8291       throw CImgDisplayException("CImgDisplay(): No display available.");
8292     }
8293 
8294     //! Destructor - Empty constructor \inplace.
8295     /**
8296        \note Replace the current instance by an empty display.
8297     **/
8298     CImgDisplay& assign() {
8299       return flush();
8300     }
8301 
8302     //! Construct a display with specified dimensions \inplace.
8303     /**
8304     **/
8305     CImgDisplay& assign(const unsigned int width, const unsigned int height,
8306                         const char *const title=0, const unsigned int normalization=3,
8307                         const bool is_fullscreen=false, const bool is_closed=false) {
8308       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
8309       _no_display_exception();
8310       return assign();
8311     }
8312 
8313     //! Construct a display from an image \inplace.
8314     /**
8315     **/
8316     template<typename T>
8317     CImgDisplay& assign(const CImg<T>& img,
8318                         const char *const title=0, const unsigned int normalization=3,
8319                         const bool is_fullscreen=false, const bool is_closed=false) {
8320       _no_display_exception();
8321       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
8322     }
8323 
8324     //! Construct a display from an image list \inplace.
8325     /**
8326     **/
8327     template<typename T>
8328     CImgDisplay& assign(const CImgList<T>& list,
8329                         const char *const title=0, const unsigned int normalization=3,
8330                         const bool is_fullscreen=false, const bool is_closed=false) {
8331       _no_display_exception();
8332       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
8333     }
8334 
8335     //! Construct a display as a copy of another one \inplace.
8336     /**
8337     **/
8338     CImgDisplay& assign(const CImgDisplay &disp) {
8339       _no_display_exception();
8340       return assign(disp._width,disp._height);
8341     }
8342 
8343 #endif
8344 
8345     //! Return a reference to an empty display.
8346     /**
8347        \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
8348        must have a default value.
8349        \par Example
8350        \code
8351        void foo(CImgDisplay& disp=CImgDisplay::empty());
8352        \endcode
8353     **/
8354     static CImgDisplay& empty() {
8355       static CImgDisplay _empty;
8356       return _empty.assign();
8357     }
8358 
8359     //! Return a reference to an empty display \const.
8360     static const CImgDisplay& const_empty() {
8361       static const CImgDisplay _empty;
8362       return _empty;
8363     }
8364 
8365 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \
8366                                  CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true)
8367     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
8368                                    const int dmin, const int dmax, const bool return_y) {
8369       const int
8370         u = CImgDisplay::screen_width(),
8371         v = CImgDisplay::screen_height();
8372       const float
8373         mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin,
8374         mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin,
8375         Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax,
8376         Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax;
8377       float
8378         w = (float)std::max(1U,dx),
8379         h = (float)std::max(1U,dy);
8380       if (dz>1) { w+=dz; h+=dz; }
8381       if (w<mw) { h = h*mw/w; w = mw; }
8382       if (h<mh) { w = w*mh/h; h = mh; }
8383       if (w>Mw) { h = h*Mw/w; w = Mw; }
8384       if (h>Mh) { w = w*Mh/h; h = Mh; }
8385       if (w<mw) w = mw;
8386       if (h<mh) h = mh;
8387       return std::max(1U,(unsigned int)cimg::round(return_y?h:w));
8388     }
8389 
8390     //@}
8391     //------------------------------------------
8392     //
8393     //! \name Overloaded Operators
8394     //@{
8395     //------------------------------------------
8396 
8397     //! Display image on associated window.
8398     /**
8399        \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
8400     **/
8401     template<typename t>
8402     CImgDisplay& operator=(const CImg<t>& img) {
8403       return display(img);
8404     }
8405 
8406     //! Display list of images on associated window.
8407     /**
8408        \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
8409     **/
8410     template<typename t>
8411     CImgDisplay& operator=(const CImgList<t>& list) {
8412       return display(list);
8413     }
8414 
8415     //! Construct a display as a copy of another one \inplace.
8416     /**
8417        \note Equivalent to assign(const CImgDisplay&).
8418      **/
8419     CImgDisplay& operator=(const CImgDisplay& disp) {
8420       return assign(disp);
8421     }
8422 
8423     //! Return \c false if display is empty, \c true otherwise.
8424     /**
8425        \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
8426     **/
8427     operator bool() const {
8428       return !is_empty();
8429     }
8430 
8431     //@}
8432     //------------------------------------------
8433     //
8434     //! \name Instance Checking
8435     //@{
8436     //------------------------------------------
8437 
8438     //! Return \c true if display is empty, \c false otherwise.
8439     /**
8440     **/
8441     bool is_empty() const {
8442       return !(_width && _height);
8443     }
8444 
8445     //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
8446     /**
8447        \note
8448        - When a user physically closes the associated window, the display is set to closed.
8449        - A closed display is not destroyed. Its associated window can be show again on the screen using show().
8450     **/
8451     bool is_closed() const {
8452       return _is_closed;
8453     }
8454 
8455     //! Return \c true if associated window has been resized on the screen, \c false otherwise.
8456     /**
8457     **/
8458     bool is_resized() const {
8459       return _is_resized;
8460     }
8461 
8462     //! Return \c true if associated window has been moved on the screen, \c false otherwise.
8463     /**
8464     **/
8465     bool is_moved() const {
8466       return _is_moved;
8467     }
8468 
8469     //! Return \c true if any event has occurred on the associated window, \c false otherwise.
8470     /**
8471     **/
8472     bool is_event() const {
8473       return _is_event;
8474     }
8475 
8476     //! Return \c true if current display is in fullscreen mode, \c false otherwise.
8477     /**
8478     **/
8479     bool is_fullscreen() const {
8480       return _is_fullscreen;
8481     }
8482 
8483     //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
8484     /**
8485        \note The methods below do the same only for specific keys.
8486     **/
8487     bool is_key() const {
8488       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
8489         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
8490         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
8491         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
8492         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
8493         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
8494         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
8495         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
8496         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
8497         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
8498         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
8499         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
8500         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
8501         _is_keyK || _is_keyL || _is_keyENTER ||
8502         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
8503         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
8504         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
8505         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
8506         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
8507         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
8508         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
8509         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
8510         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
8511         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
8512         _is_keyPADMUL || _is_keyPADDIV;
8513     }
8514 
8515     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
8516     /**
8517        \param keycode Keycode to test.
8518        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8519        your code stay portable (see cimg::keyESC).
8520        \par Example
8521        \code
8522        CImgDisplay disp(400,400);
8523        while (!disp.is_closed()) {
8524          if (disp.key(cimg::keyTAB)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'
8525          disp.wait();
8526        }
8527        \endcode
8528     **/
8529     bool is_key(const unsigned int keycode) const {
8530 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
8531       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
8532       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
8533       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
8534       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
8535       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
8536       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
8537       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
8538       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
8539       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
8540       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
8541       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
8542       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
8543       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
8544       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
8545       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
8546       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
8547       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
8548       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
8549       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
8550       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
8551       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
8552       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
8553       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
8554       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
8555       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
8556       return false;
8557     }
8558 
8559     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
8560     /**
8561        \param keycode C-string containing the keycode label of the key to test.
8562        \note Use it when the key you want to test can be dynamically set by the user.
8563        \par Example
8564        \code
8565        CImgDisplay disp(400,400);
8566        const char *const keycode = "TAB";
8567        while (!disp.is_closed()) {
8568          if (disp.is_key(keycode)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'
8569          disp.wait();
8570        }
8571        \endcode
8572     **/
8573     bool& is_key(const char *const keycode) {
8574       static bool f = false;
8575       f = false;
8576 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
8577       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
8578       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
8579       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
8580       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
8581       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
8582       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
8583       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
8584       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
8585       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
8586       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
8587       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
8588       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
8589       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
8590       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
8591       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
8592       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
8593       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
8594       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
8595       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
8596       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
8597       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
8598       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
8599       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
8600       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
8601       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
8602       return f;
8603     }
8604 
8605     //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
8606     /**
8607        \param keycodes_sequence Buffer of keycodes to test.
8608        \param length Number of keys in the \c keycodes_sequence buffer.
8609        \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
8610        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8611        your code stay portable (see cimg::keyESC).
8612        \par Example
8613        \code
8614        CImgDisplay disp(400,400);
8615        const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
8616        while (!disp.is_closed()) {
8617          if (disp.is_key_sequence(key_seq,2)) { ... }  // Test for the 'CTRL+D' keyboard event
8618          disp.wait();
8619        }
8620        \endcode
8621     **/
8622     bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
8623                          const bool remove_sequence=false) {
8624       if (keycodes_sequence && length) {
8625         const unsigned int
8626           *const ps_end = keycodes_sequence + length - 1,
8627           *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
8628           k = *ps_end;
8629         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
8630           if (*(pk++)==k) {
8631             bool res = true;
8632             const unsigned int *ps = ps_end, *pk2 = pk;
8633             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
8634             if (res) {
8635               if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
8636               return true;
8637             }
8638           }
8639         }
8640       }
8641       return false;
8642     }
8643 
8644 #define _cimg_iskey_def(k) \
8645     bool is_key##k() const { \
8646       return _is_key##k; \
8647     }
8648 
8649     //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
8650     /**
8651        \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
8652     **/
8653     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
8654     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
8655     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
8656     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
8657     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
8658     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
8659     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
8660     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
8661     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
8662     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
8663     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
8664     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
8665     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
8666     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
8667     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
8668     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
8669     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
8670     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
8671     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
8672     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
8673     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
8674     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
8675     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
8676     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
8677     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
8678 
8679     //@}
8680     //------------------------------------------
8681     //
8682     //! \name Instance Characteristics
8683     //@{
8684     //------------------------------------------
8685 
8686 #if cimg_display==0
8687 
8688     //! Return width of the screen (current resolution along the X-axis).
8689     /**
8690     **/
8691     static int screen_width() {
8692       _no_display_exception();
8693       return 0;
8694     }
8695 
8696     //! Return height of the screen (current resolution along the Y-axis).
8697     /**
8698     **/
8699     static int screen_height() {
8700       _no_display_exception();
8701       return 0;
8702     }
8703 
8704 #endif
8705 
8706     //! Return display width.
8707     /**
8708        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
8709        may be different from the actual width of the associated window.
8710     **/
8711     int width() const {
8712       return (int)_width;
8713     }
8714 
8715     //! Return display height.
8716     /**
8717        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
8718        may be different from the actual height of the associated window.
8719     **/
8720     int height() const {
8721       return (int)_height;
8722     }
8723 
8724     //! Return normalization type of the display.
8725     /**
8726        The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
8727        correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
8728        If the range of values of the data to display is different, a normalization may be required for displaying
8729        the data in a correct way. The normalization type can be one of:
8730        - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
8731        CImgDisplay instance have values in range <tt>[0,255]</tt>.
8732        - \c 1: Value normalization is always performed (this is the default behavior).
8733        Before displaying an input image, its values will be (virtually) stretched
8734        in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
8735        Use this mode for images whose minimum and maximum values are not prescribed to known values
8736        (e.g. float-valued images).
8737        Note that when normalized versions of images are computed for display purposes, the actual values of these
8738        images are not modified.
8739        - \c 2: Value normalization is performed once (on the first image display), then the same normalization
8740        coefficients are kept for next displayed frames.
8741        - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
8742        the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
8743        for <tt>unsigned char</tt>).
8744        For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
8745        data instead.
8746     **/
8747     unsigned int normalization() const {
8748       return _normalization;
8749     }
8750 
8751     //! Return title of the associated window as a C-string.
8752     /**
8753        \note Window title may be not visible, depending on the used window manager or if the current display is
8754        in fullscreen mode.
8755     **/
8756     const char *title() const {
8757       return _title?_title:"";
8758     }
8759 
8760     //! Return width of the associated window.
8761     /**
8762        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
8763        may be different from the actual width of the associated window.
8764     **/
8765     int window_width() const {
8766       return (int)_window_width;
8767     }
8768 
8769     //! Return height of the associated window.
8770     /**
8771        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
8772        may be different from the actual height of the associated window.
8773     **/
8774     int window_height() const {
8775       return (int)_window_height;
8776     }
8777 
8778     //! Return X-coordinate of the associated window.
8779     /**
8780        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
8781     **/
8782     int window_x() const {
8783       return _window_x;
8784     }
8785 
8786     //! Return Y-coordinate of the associated window.
8787     /**
8788        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
8789     **/
8790     int window_y() const {
8791       return _window_y;
8792     }
8793 
8794     //! Return X-coordinate of the mouse pointer.
8795     /**
8796        \note
8797        - If the mouse pointer is outside window area, \c -1 is returned.
8798        - Otherwise, the returned value is in the range [0,width()-1].
8799     **/
8800     int mouse_x() const {
8801       return _mouse_x;
8802     }
8803 
8804     //! Return Y-coordinate of the mouse pointer.
8805     /**
8806        \note
8807        - If the mouse pointer is outside window area, \c -1 is returned.
8808        - Otherwise, the returned value is in the range [0,height()-1].
8809     **/
8810     int mouse_y() const {
8811       return _mouse_y;
8812     }
8813 
8814     //! Return current state of the mouse buttons.
8815     /**
8816        \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
8817        value is set:
8818        - bit \c 0 (value \c 0x1): State of the left mouse button.
8819        - bit \c 1 (value \c 0x2): State of the right mouse button.
8820        - bit \c 2 (value \c 0x4): State of the middle mouse button.
8821 
8822        Several bits can be activated if more than one button are pressed at the same time.
8823        \par Example
8824        \code
8825        CImgDisplay disp(400,400);
8826        while (!disp.is_closed()) {
8827          if (disp.button()&1) { // Left button clicked
8828            ...
8829          }
8830          if (disp.button()&2) { // Right button clicked
8831            ...
8832          }
8833          if (disp.button()&4) { // Middle button clicked
8834            ...
8835          }
8836          disp.wait();
8837        }
8838        \endcode
8839     **/
8840     unsigned int button() const {
8841       return _button;
8842     }
8843 
8844     //! Return current state of the mouse wheel.
8845     /**
8846        \note
8847        - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
8848        forward or backward.
8849        - Scrolling the wheel forward add \c 1 to the wheel value.
8850        - Scrolling the wheel backward subtract \c 1 to the wheel value.
8851        - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
8852        or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
8853        the wheel counter when an action has been performed regarding the current wheel value.
8854        Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
8855        (as many in forward as in backward directions).
8856        \par Example
8857        \code
8858        CImgDisplay disp(400,400);
8859        while (!disp.is_closed()) {
8860          if (disp.wheel()) {
8861            int counter = disp.wheel();  // Read the state of the mouse wheel
8862            ...                          // Do what you want with 'counter'
8863            disp.set_wheel();            // Reset the wheel value to 0
8864          }
8865          disp.wait();
8866        }
8867        \endcode
8868     **/
8869     int wheel() const {
8870       return _wheel;
8871     }
8872 
8873     //! Return one entry from the pressed keys history.
8874     /**
8875        \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry).
8876        \return Keycode of a pressed key or \c 0 for a released key.
8877        \note
8878        - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
8879        its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
8880        This means that up to the 64 last pressed keys may be read from the pressed keys history.
8881        When a new value is stored, the pressed keys history is shifted so that the latest entry is always
8882        stored at position \c 0.
8883        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8884        your code stay portable (see cimg::keyESC).
8885     **/
8886     unsigned int& key(const unsigned int pos=0) const {
8887       static unsigned int key0;
8888       return pos<128?_keys[pos]:(key0 = 0);
8889 
8890     }
8891 
8892     //! Return one entry from the released keys history.
8893     /**
8894        \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry).
8895        \return Keycode of a released key or \c 0 for a pressed key.
8896        \note
8897        - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
8898        its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
8899        This means that up to the 64 last released keys may be read from the released keys history.
8900        When a new value is stored, the released keys history is shifted so that the latest entry is always
8901        stored at position \c 0.
8902        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8903        your code stay portable (see cimg::keyESC).
8904     **/
8905     unsigned int& released_key(const unsigned int pos=0) const {
8906       static unsigned int key0;
8907       return pos<128?_released_keys[pos]:(key0 = 0);
8908     }
8909 
8910     //! Return keycode corresponding to the specified string.
8911     /**
8912        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8913        your code stay portable (see cimg::keyESC).
8914        \par Example
8915        \code
8916        const unsigned int keyTAB = CImgDisplay::keycode("TAB");  // Return cimg::keyTAB
8917        \endcode
8918     **/
8919     static unsigned int keycode(const char *const keycode) {
8920 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
8921       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
8922       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
8923       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
8924       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
8925       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
8926       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
8927       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
8928       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
8929       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
8930       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
8931       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
8932       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
8933       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
8934       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
8935       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
8936       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
8937       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
8938       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
8939       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
8940       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
8941       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
8942       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
8943       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
8944       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
8945       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
8946       return 0;
8947     }
8948 
8949     //! Return the current refresh rate, in frames per second.
8950     /**
8951        \note Returns a significant value when the current instance is used to display successive frames.
8952        It measures the delay between successive calls to frames_per_second().
8953     **/
8954     float frames_per_second() {
8955       if (!_fps_timer) _fps_timer = cimg::time();
8956       const float delta = (float)((cimg::time() - _fps_timer)/1000.f);
8957       ++_fps_frames;
8958       if (delta>=1) {
8959         _fps_fps = _fps_frames/delta;
8960         _fps_frames = 0;
8961         _fps_timer = cimg::time();
8962       }
8963       return _fps_fps;
8964     }
8965 
8966     // Move current display window so that its content stays inside the current screen.
8967     CImgDisplay& move_inside_screen() {
8968       if (is_empty()) return *this;
8969       const int
8970         x0 = window_x(),
8971         y0 = window_y(),
8972         x1 = x0 + window_width() - 1,
8973         y1 = y0 + window_height() - 1,
8974         sw = CImgDisplay::screen_width(),
8975         sh = CImgDisplay::screen_height();
8976       if (x0<0 || y0<0 || x1>=sw || y1>=sh)
8977         move(std::max(0,std::min(x0,sw - x1 + x0)),
8978              std::max(0,std::min(y0,sh - y1 + y0)));
8979       return *this;
8980     }
8981 
8982     //@}
8983     //---------------------------------------
8984     //
8985     //! \name Window Manipulation
8986     //@{
8987     //---------------------------------------
8988 
8989 #if cimg_display==0
8990 
8991     //! Display image on associated window.
8992     /**
8993        \param img Input image to display.
8994        \note This method returns immediately.
8995     **/
8996     template<typename T>
8997     CImgDisplay& display(const CImg<T>& img) {
8998       return assign(img);
8999     }
9000 
9001 #endif
9002 
9003     //! Display list of images on associated window.
9004     /**
9005        \param list List of images to display.
9006        \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
9007        \param align Relative position of aligned images when displaying lists with images of different sizes
9008        (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
9009        \note This method returns immediately.
9010     **/
9011     template<typename T>
9012     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
9013       if (list._width==1) {
9014         const CImg<T>& img = list[0];
9015         if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
9016       }
9017       CImgList<typename CImg<T>::ucharT> visu(list._width);
9018       unsigned int dims = 0;
9019       cimglist_for(list,l) {
9020         const CImg<T>& img = list._data[l];
9021         img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
9022                         (img._depth - 1)/2).move_to(visu[l]);
9023         dims = std::max(dims,visu[l]._spectrum);
9024       }
9025       cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
9026       visu.get_append(axis,align).display(*this);
9027       return *this;
9028     }
9029 
9030 #if cimg_display==0
9031 
9032     //! Show (closed) associated window on the screen.
9033     /**
9034        \note
9035        - Force the associated window of a display to be visible on the screen, even if it has been closed before.
9036        - Using show() on a visible display does nothing.
9037     **/
9038     CImgDisplay& show() {
9039       return assign();
9040     }
9041 
9042     //! Close (visible) associated window and make it disappear from the screen.
9043     /**
9044        \note
9045        - A closed display only means the associated window is not visible anymore. This does not mean the display has
9046        been destroyed.
9047        Use show() to make the associated window reappear.
9048        - Using close() on a closed display does nothing.
9049     **/
9050     CImgDisplay& close() {
9051       return assign();
9052     }
9053 
9054     //! Move associated window to a new location.
9055     /**
9056        \param pos_x X-coordinate of the new window location.
9057        \param pos_y Y-coordinate of the new window location.
9058        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
9059        nevertheless).
9060     **/
9061     CImgDisplay& move(const int pos_x, const int pos_y) {
9062       return assign(pos_x,pos_y);
9063     }
9064 
9065 #endif
9066 
9067     //! Resize display to the size of the associated window.
9068     /**
9069        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
9070        \note
9071        - Calling this method ensures that width() and window_width() become equal, as well as height() and
9072        window_height().
9073        - The associated window is also resized to specified dimensions.
9074     **/
9075     CImgDisplay& resize(const bool force_redraw=true) {
9076       resize(window_width(),window_height(),force_redraw);
9077       return *this;
9078     }
9079 
9080 #if cimg_display==0
9081 
9082     //! Resize display to the specified size.
9083     /**
9084        \param width Requested display width.
9085        \param height Requested display height.
9086        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
9087        \note The associated window is also resized to specified dimensions.
9088     **/
9089     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
9090       return assign(width,height,0,3,force_redraw);
9091     }
9092 
9093 #endif
9094 
9095     //! Resize display to the size of an input image.
9096     /**
9097        \param img Input image to take size from.
9098        \param force_redraw Tells if the previous window content must be resized and updated as well.
9099        \note
9100        - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
9101        <tt>img.height()</tt>.
9102        - The associated window is also resized to specified dimensions.
9103     **/
9104     template<typename T>
9105     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
9106       return resize(img._width,img._height,force_redraw);
9107     }
9108 
9109     //! Resize display to the size of another CImgDisplay instance.
9110     /**
9111        \param disp Input display to take size from.
9112        \param force_redraw Tells if the previous window content must be resized and updated as well.
9113        \note
9114        - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
9115        <tt>disp.height()</tt>.
9116        - The associated window is also resized to specified dimensions.
9117     **/
9118     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
9119       return resize(disp.width(),disp.height(),force_redraw);
9120     }
9121 
9122     // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
9123     template<typename t, typename T>
9124     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
9125                                t *ptrd, const unsigned int wd, const unsigned int hd) {
9126       typedef typename cimg::last<T,cimg_ulong>::type ulongT;
9127       const ulongT one = (ulongT)1;
9128       CImg<ulongT> off_x(wd), off_y(hd + 1);
9129       if (wd==ws) off_x.fill(1);
9130       else {
9131         ulongT *poff_x = off_x._data, curr = 0;
9132         for (unsigned int x = 0; x<wd; ++x) {
9133           const ulongT old = curr;
9134           curr = (x + one)*ws/wd;
9135           *(poff_x++) = curr - old;
9136         }
9137       }
9138       if (hd==hs) off_y.fill(ws);
9139       else {
9140         ulongT *poff_y = off_y._data, curr = 0;
9141         for (unsigned int y = 0; y<hd; ++y) {
9142           const ulongT old = curr;
9143           curr = (y + one)*hs/hd;
9144           *(poff_y++) = ws*(curr - old);
9145         }
9146         *poff_y = 0;
9147       }
9148       ulongT *poff_y = off_y._data;
9149       for (unsigned int y = 0; y<hd; ) {
9150         const T *ptr = ptrs;
9151         ulongT *poff_x = off_x._data;
9152         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poff_x++); }
9153         ++y;
9154         ulongT dy = *(poff_y++);
9155         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poff_y++)) {}
9156         ptrs+=dy;
9157       }
9158     }
9159 
9160     //! Set normalization type.
9161     /**
9162        \param normalization New normalization mode.
9163     **/
9164     CImgDisplay& set_normalization(const unsigned int normalization) {
9165       _normalization = normalization;
9166       _min = _max = 0;
9167       return *this;
9168     }
9169 
9170 #if cimg_display==0
9171 
9172     //! Set title of the associated window.
9173     /**
9174        \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
9175        \warning As the first argument is a format string, it is highly recommended to write
9176        \code
9177        disp.set_title("%s",window_title);
9178        \endcode
9179        instead of
9180        \code
9181        disp.set_title(window_title);
9182        \endcode
9183        if \c window_title can be arbitrary, to prevent nasty memory access.
9184     **/
9185     CImgDisplay& set_title(const char *const format, ...) {
9186       return assign(0,0,format);
9187     }
9188 
9189 #endif
9190 
9191     //! Enable or disable fullscreen mode.
9192     /**
9193        \param is_fullscreen Tells is the fullscreen mode must be activated or not.
9194        \param force_redraw Tells if the previous window content must be displayed as well.
9195        \note
9196        - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
9197        current display is not modified.
9198        - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
9199        as possible.
9200        For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
9201        resolution change (requires the X11 extensions to be enabled).
9202     **/
9203     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
9204       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
9205       return toggle_fullscreen(force_redraw);
9206     }
9207 
9208 #if cimg_display==0
9209 
9210     //! Toggle fullscreen mode.
9211     /**
9212        \param force_redraw Tells if the previous window content must be displayed as well.
9213        \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
9214     **/
9215     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
9216       return assign(_width,_height,0,3,force_redraw);
9217     }
9218 
9219     //! Show mouse pointer.
9220     /**
9221        \note Depending on the window manager behavior, this method may not succeed
9222        (no exceptions are thrown nevertheless).
9223     **/
9224     CImgDisplay& show_mouse() {
9225       return assign();
9226     }
9227 
9228     //! Hide mouse pointer.
9229     /**
9230        \note Depending on the window manager behavior, this method may not succeed
9231        (no exceptions are thrown nevertheless).
9232     **/
9233     CImgDisplay& hide_mouse() {
9234       return assign();
9235     }
9236 
9237     //! Move mouse pointer to a specified location.
9238     /**
9239        \note Depending on the window manager behavior, this method may not succeed
9240        (no exceptions are thrown nevertheless).
9241     **/
9242     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
9243       return assign(pos_x,pos_y);
9244     }
9245 
9246 #endif
9247 
9248     //! Simulate a mouse button release event.
9249     /**
9250        \note All mouse buttons are considered released at the same time.
9251     **/
9252     CImgDisplay& set_button() {
9253       _button = 0;
9254       _is_event = true;
9255 #if cimg_display==1
9256       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9257 #elif cimg_display==2
9258       SetEvent(cimg::Win32_attr().wait_event);
9259 #endif
9260       return *this;
9261     }
9262 
9263     //! Simulate a mouse button press or release event.
9264     /**
9265        \param button Buttons event code, where each button is associated to a single bit.
9266        \param is_pressed Tells if the mouse button is considered as pressed or released.
9267     **/
9268     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
9269       const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
9270       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
9271       _is_event = buttoncode?true:false;
9272       if (buttoncode) {
9273 #if cimg_display==1
9274         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9275 #elif cimg_display==2
9276         SetEvent(cimg::Win32_attr().wait_event);
9277 #endif
9278       }
9279       return *this;
9280     }
9281 
9282     //! Flush all mouse wheel events.
9283     /**
9284        \note Make wheel() to return \c 0, if called afterwards.
9285     **/
9286     CImgDisplay& set_wheel() {
9287       _wheel = 0;
9288       _is_event = true;
9289 #if cimg_display==1
9290       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9291 #elif cimg_display==2
9292       SetEvent(cimg::Win32_attr().wait_event);
9293 #endif
9294       return *this;
9295     }
9296 
9297     //! Simulate a wheel event.
9298     /**
9299        \param amplitude Amplitude of the wheel scrolling to simulate.
9300        \note Make wheel() to return \c amplitude, if called afterwards.
9301     **/
9302     CImgDisplay& set_wheel(const int amplitude) {
9303       _wheel+=amplitude;
9304       _is_event = amplitude?true:false;
9305       if (amplitude) {
9306 #if cimg_display==1
9307         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9308 #elif cimg_display==2
9309         SetEvent(cimg::Win32_attr().wait_event);
9310 #endif
9311       }
9312       return *this;
9313     }
9314 
9315     //! Flush all key events.
9316     /**
9317        \note Make key() to return \c 0, if called afterwards.
9318     **/
9319     CImgDisplay& set_key() {
9320       std::memset((void*)_keys,0,128*sizeof(unsigned int));
9321       std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
9322       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
9323         _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
9324         _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
9325         _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
9326         _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
9327         _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
9328         _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
9329         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
9330         _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
9331         _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
9332         _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
9333         _is_keyPADDIV = false;
9334       _is_event = true;
9335 #if cimg_display==1
9336       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9337 #elif cimg_display==2
9338       SetEvent(cimg::Win32_attr().wait_event);
9339 #endif
9340       return *this;
9341     }
9342 
9343     //! Simulate a keyboard press/release event.
9344     /**
9345        \param keycode Keycode of the associated key.
9346        \param is_pressed Tells if the key is considered as pressed or released.
9347        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
9348        your code stay portable (see cimg::keyESC).
9349     **/
9350     CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
9351 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
9352       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
9353       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
9354       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
9355       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
9356       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
9357       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
9358       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
9359       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
9360       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
9361       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
9362       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
9363       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
9364       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
9365       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
9366       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
9367       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
9368       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
9369       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
9370       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
9371       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
9372       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
9373       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
9374       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
9375       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
9376       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
9377       if (is_pressed) {
9378         if (*_keys)
9379           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
9380         *_keys = keycode;
9381         if (*_released_keys) {
9382           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
9383           *_released_keys = 0;
9384         }
9385       } else {
9386         if (*_keys) {
9387           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
9388           *_keys = 0;
9389         }
9390         if (*_released_keys)
9391           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
9392         *_released_keys = keycode;
9393       }
9394       _is_event = keycode?true:false;
9395       if (keycode) {
9396 #if cimg_display==1
9397         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9398 #elif cimg_display==2
9399         SetEvent(cimg::Win32_attr().wait_event);
9400 #endif
9401       }
9402       return *this;
9403     }
9404 
9405     //! Flush all display events.
9406     /**
9407        \note Remove all passed events from the current display.
9408     **/
9409     CImgDisplay& flush() {
9410       set_key().set_button().set_wheel();
9411       _is_resized = _is_moved = _is_event = false;
9412       _fps_timer = _fps_frames = _timer = 0;
9413       _fps_fps = 0;
9414       return *this;
9415     }
9416 
9417     //! Wait for any user event occurring on the current display.
9418     CImgDisplay& wait() {
9419       wait(*this);
9420       return *this;
9421     }
9422 
9423     //! Wait for a given number of milliseconds since the last call to wait().
9424     /**
9425        \param milliseconds Number of milliseconds to wait for.
9426        \note Similar to cimg::wait().
9427     **/
9428     CImgDisplay& wait(const unsigned int milliseconds) {
9429       cimg::wait(milliseconds,&_timer);
9430       return *this;
9431     }
9432 
9433     //! Wait for any event occurring on the display \c disp1.
9434     static void wait(CImgDisplay& disp1) {
9435       disp1._is_event = false;
9436       while (!disp1._is_closed && !disp1._is_event) wait_all();
9437     }
9438 
9439     //! Wait for any event occurring either on the display \c disp1 or \c disp2.
9440     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
9441       disp1._is_event = disp2._is_event = false;
9442       while ((!disp1._is_closed || !disp2._is_closed) &&
9443              !disp1._is_event && !disp2._is_event) wait_all();
9444     }
9445 
9446     //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3.
9447     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
9448       disp1._is_event = disp2._is_event = disp3._is_event = false;
9449       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
9450              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
9451     }
9452 
9453     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
9454     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
9455       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
9456       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
9457              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
9458     }
9459 
9460     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
9461     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
9462                      CImgDisplay& disp5) {
9463       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
9464       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
9465              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
9466         wait_all();
9467     }
9468 
9469     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
9470     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9471                      CImgDisplay& disp6) {
9472       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9473         disp6._is_event = false;
9474       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9475               !disp6._is_closed) &&
9476              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9477              !disp6._is_event) wait_all();
9478     }
9479 
9480     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
9481     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9482                      CImgDisplay& disp6, CImgDisplay& disp7) {
9483       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9484         disp6._is_event = disp7._is_event = false;
9485       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9486               !disp6._is_closed || !disp7._is_closed) &&
9487              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9488              !disp6._is_event && !disp7._is_event) wait_all();
9489     }
9490 
9491     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
9492     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9493                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
9494       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9495         disp6._is_event = disp7._is_event = disp8._is_event = false;
9496       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9497               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
9498              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9499              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
9500     }
9501 
9502     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
9503     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9504                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
9505       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9506         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
9507       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9508               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
9509              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9510              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
9511     }
9512 
9513     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
9514     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9515                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
9516                      CImgDisplay& disp10) {
9517       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9518         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
9519       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9520               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
9521              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9522              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
9523         wait_all();
9524     }
9525 
9526 #if cimg_display==0
9527 
9528     //! Wait for any window event occurring in any opened CImgDisplay.
9529     static void wait_all() {
9530       return _no_display_exception();
9531     }
9532 
9533     //! Render image into internal display buffer.
9534     /**
9535        \param img Input image data to render.
9536        \note
9537        - Convert image data representation into the internal display buffer (architecture-dependent structure).
9538        - The content of the associated window is not modified, until paint() is called.
9539        - Should not be used for common CImgDisplay uses, since display() is more useful.
9540     **/
9541     template<typename T>
9542     CImgDisplay& render(const CImg<T>& img) {
9543       return assign(img);
9544     }
9545 
9546     //! Paint internal display buffer on associated window.
9547     /**
9548        \note
9549        - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
9550        - Should not be used for common CImgDisplay uses, since display() is more useful.
9551     **/
9552     CImgDisplay& paint() {
9553       return assign();
9554     }
9555 
9556 
9557     //! Take a snapshot of the current screen content.
9558     /**
9559        \param x0 X-coordinate of the upper left corner.
9560        \param y0 Y-coordinate of the upper left corner.
9561        \param x1 X-coordinate of the lower right corner.
9562        \param y1 Y-coordinate of the lower right corner.
9563        \param[out] img Output screenshot. Can be empty on input
9564     **/
9565     template<typename T>
9566     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
9567       cimg::unused(x0,y0,x1,y1,&img);
9568       _no_display_exception();
9569     }
9570 
9571     //! Take a snapshot of the associated window content.
9572     /**
9573        \param[out] img Output snapshot. Can be empty on input.
9574     **/
9575     template<typename T>
9576     const CImgDisplay& snapshot(CImg<T>& img) const {
9577       cimg::unused(img);
9578       _no_display_exception();
9579       return *this;
9580     }
9581 #endif
9582 
9583     // X11-based implementation
9584     //--------------------------
9585 #if cimg_display==1
9586 
9587     Atom _wm_window_atom, _wm_protocol_atom;
9588     Window _window, _background_window;
9589     Colormap _colormap;
9590     XImage *_image;
9591     void *_data;
9592 
9593 #ifdef cimg_use_xshm
9594     XShmSegmentInfo *_shminfo;
9595 #endif
9596 
9597     static int screen_width() {
9598       Display *const dpy = cimg::X11_attr().display;
9599       int res = 0;
9600       if (!dpy) {
9601         Display *const _dpy = XOpenDisplay(0);
9602         if (!_dpy)
9603           throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
9604         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
9605         XCloseDisplay(_dpy);
9606       } else {
9607 
9608 #ifdef cimg_use_xrandr
9609         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
9610           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
9611         else res = DisplayWidth(dpy,DefaultScreen(dpy));
9612 #else
9613         res = DisplayWidth(dpy,DefaultScreen(dpy));
9614 #endif
9615       }
9616       return res;
9617     }
9618 
9619     static int screen_height() {
9620       Display *const dpy = cimg::X11_attr().display;
9621       int res = 0;
9622       if (!dpy) {
9623         Display *const _dpy = XOpenDisplay(0);
9624         if (!_dpy)
9625           throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
9626         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
9627         XCloseDisplay(_dpy);
9628       } else {
9629 
9630 #ifdef cimg_use_xrandr
9631         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
9632           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
9633         else res = DisplayHeight(dpy,DefaultScreen(dpy));
9634 #else
9635         res = DisplayHeight(dpy,DefaultScreen(dpy));
9636 #endif
9637       }
9638       return res;
9639     }
9640 
9641     static void wait_all() {
9642       if (!cimg::X11_attr().display) return;
9643       pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
9644       pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
9645       pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
9646     }
9647 
9648     void _handle_events(const XEvent *const pevent) {
9649       Display *const dpy = cimg::X11_attr().display;
9650       XEvent event = *pevent;
9651       switch (event.type) {
9652       case ClientMessage : {
9653         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
9654             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
9655           XUnmapWindow(cimg::X11_attr().display,_window);
9656           _is_closed = _is_event = true;
9657           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9658         }
9659       } break;
9660       case ConfigureNotify : {
9661         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
9662         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
9663         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
9664         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
9665           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
9666           XResizeWindow(dpy,_window,_window_width,_window_height);
9667           _is_resized = _is_event = true;
9668           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9669         }
9670         if (nx!=_window_x || ny!=_window_y) {
9671           _window_x = nx;
9672           _window_y = ny;
9673           _is_moved = _is_event = true;
9674           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9675         }
9676       } break;
9677       case Expose : {
9678         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
9679         _paint(false);
9680         if (_is_fullscreen) {
9681           XWindowAttributes attr;
9682           do {
9683             XGetWindowAttributes(dpy,_window,&attr);
9684             if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9685           } while (attr.map_state!=IsViewable);
9686           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
9687         }
9688       } break;
9689       case ButtonPress : {
9690         do {
9691           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
9692           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9693           switch (event.xbutton.button) {
9694           case 1 : set_button(1); break;
9695           case 3 : set_button(2); break;
9696           case 2 : set_button(3); break;
9697           }
9698         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
9699       } break;
9700       case ButtonRelease : {
9701         do {
9702           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
9703           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9704           switch (event.xbutton.button) {
9705           case 1 : set_button(1,false); break;
9706           case 3 : set_button(2,false); break;
9707           case 2 : set_button(3,false); break;
9708           case 4 : set_wheel(1); break;
9709           case 5 : set_wheel(-1); break;
9710           }
9711         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
9712       } break;
9713       case KeyPress : {
9714         char tmp = 0; KeySym ksym;
9715         XLookupString(&event.xkey,&tmp,1,&ksym,0);
9716         set_key((unsigned int)ksym,true);
9717       } break;
9718       case KeyRelease : {
9719         char keys_return[32];  // Check that the key has been physically unpressed
9720         XQueryKeymap(dpy,keys_return);
9721         const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
9722         const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
9723         if (!is_key_pressed) {
9724           char tmp = 0; KeySym ksym;
9725           XLookupString(&event.xkey,&tmp,1,&ksym,0);
9726           set_key((unsigned int)ksym,false);
9727         }
9728       } break;
9729       case EnterNotify: {
9730         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
9731         _mouse_x = event.xmotion.x;
9732         _mouse_y = event.xmotion.y;
9733         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9734       } break;
9735       case LeaveNotify : {
9736         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
9737         _mouse_x = _mouse_y = -1; _is_event = true;
9738         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9739       } break;
9740       case MotionNotify : {
9741         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
9742         _mouse_x = event.xmotion.x;
9743         _mouse_y = event.xmotion.y;
9744         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9745         _is_event = true;
9746         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9747       } break;
9748       }
9749     }
9750 
9751     static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows
9752       Display *const dpy = cimg::X11_attr().display;
9753       XEvent event;
9754       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
9755       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
9756       if (!arg) for ( ; ; ) {
9757         cimg_lock_display();
9758         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
9759         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
9760                                                       ExposureMask | StructureNotifyMask | ButtonPressMask |
9761                                                       KeyPressMask | PointerMotionMask | EnterWindowMask |
9762                                                       LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
9763         if (event_flag)
9764           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
9765             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
9766               cimg::X11_attr().wins[i]->_handle_events(&event);
9767         cimg_unlock_display();
9768         pthread_testcancel();
9769         cimg::sleep(8);
9770       }
9771       return 0;
9772     }
9773 
9774     void _set_colormap(Colormap& cmap, const unsigned int dim) {
9775       XColor *const colormap = new XColor[256];
9776       switch (dim) {
9777       case 1 : { // colormap for greyscale images
9778         for (unsigned int index = 0; index<256; ++index) {
9779           colormap[index].pixel = index;
9780           colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
9781           colormap[index].flags = DoRed | DoGreen | DoBlue;
9782         }
9783       } break;
9784       case 2 : { // colormap for RG images
9785         for (unsigned int index = 0, r = 8; r<256; r+=16)
9786           for (unsigned int g = 8; g<256; g+=16) {
9787             colormap[index].pixel = index;
9788             colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
9789             colormap[index].green = (unsigned short)(g<<8);
9790             colormap[index++].flags = DoRed | DoGreen | DoBlue;
9791           }
9792       } break;
9793       default : { // colormap for RGB images
9794         for (unsigned int index = 0, r = 16; r<256; r+=32)
9795           for (unsigned int g = 16; g<256; g+=32)
9796             for (unsigned int b = 32; b<256; b+=64) {
9797               colormap[index].pixel = index;
9798               colormap[index].red = (unsigned short)(r<<8);
9799               colormap[index].green = (unsigned short)(g<<8);
9800               colormap[index].blue = (unsigned short)(b<<8);
9801               colormap[index++].flags = DoRed | DoGreen | DoBlue;
9802             }
9803       }
9804       }
9805       XStoreColors(cimg::X11_attr().display,cmap,colormap,256);
9806       delete[] colormap;
9807     }
9808 
9809     void _map_window() {
9810       Display *const dpy = cimg::X11_attr().display;
9811       bool is_exposed = false, is_mapped = false;
9812       XWindowAttributes attr;
9813       XEvent event;
9814       XMapRaised(dpy,_window);
9815       do { // Wait for the window to be mapped
9816         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
9817         switch (event.type) {
9818         case MapNotify : is_mapped = true; break;
9819         case Expose : is_exposed = true; break;
9820         }
9821       } while (!is_exposed || !is_mapped);
9822       do { // Wait for the window to be visible
9823         XGetWindowAttributes(dpy,_window,&attr);
9824         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9825       } while (attr.map_state!=IsViewable);
9826       _window_x = attr.x;
9827       _window_y = attr.y;
9828     }
9829 
9830     void _paint(const bool wait_expose=true) {
9831       if (_is_closed || !_image) return;
9832       Display *const dpy = cimg::X11_attr().display;
9833       if (wait_expose) { // Send an expose event sticked to display window to force repaint
9834         XEvent event;
9835         event.xexpose.type = Expose;
9836         event.xexpose.serial = 0;
9837         event.xexpose.send_event = 1;
9838         event.xexpose.display = dpy;
9839         event.xexpose.window = _window;
9840         event.xexpose.x = 0;
9841         event.xexpose.y = 0;
9842         event.xexpose.width = width();
9843         event.xexpose.height = height();
9844         event.xexpose.count = 0;
9845         XSendEvent(dpy,_window,0,0,&event);
9846       } else { // Repaint directly (may be called from the expose event)
9847         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
9848 
9849 #ifdef cimg_use_xshm
9850         if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
9851         else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
9852 #else
9853         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
9854 #endif
9855       }
9856     }
9857 
9858     template<typename T>
9859     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
9860       Display *const dpy = cimg::X11_attr().display;
9861       cimg::unused(pixel_type);
9862 
9863 #ifdef cimg_use_xshm
9864       if (_shminfo) {
9865         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
9866         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
9867                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
9868         if (!nimage) { delete nshminfo; return; }
9869         else {
9870           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
9871           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
9872           else {
9873             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
9874             if (nshminfo->shmaddr==(char*)-1) {
9875               shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
9876             } else {
9877               nshminfo->readOnly = 0;
9878               cimg::X11_attr().is_shm_enabled = true;
9879               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
9880               XShmAttach(dpy,nshminfo);
9881               XFlush(dpy);
9882               XSetErrorHandler(oldXErrorHandler);
9883               if (!cimg::X11_attr().is_shm_enabled) {
9884                 shmdt(nshminfo->shmaddr);
9885                 shmctl(nshminfo->shmid,IPC_RMID,0);
9886                 XDestroyImage(nimage);
9887                 delete nshminfo;
9888                 return;
9889               } else {
9890                 T *const ndata = (T*)nimage->data;
9891                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
9892                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
9893                 XShmDetach(dpy,_shminfo);
9894                 XDestroyImage(_image);
9895                 shmdt(_shminfo->shmaddr);
9896                 shmctl(_shminfo->shmid,IPC_RMID,0);
9897                 delete _shminfo;
9898                 _shminfo = nshminfo;
9899                 _image = nimage;
9900                 _data = (void*)ndata;
9901               }
9902             }
9903           }
9904         }
9905       } else
9906 #endif
9907         {
9908           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
9909           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
9910           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
9911           _data = (void*)ndata;
9912           XDestroyImage(_image);
9913           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
9914                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
9915         }
9916     }
9917 
9918     void _init_fullscreen() {
9919       if (!_is_fullscreen || _is_closed) return;
9920       Display *const dpy = cimg::X11_attr().display;
9921       _background_window = 0;
9922 
9923 #ifdef cimg_use_xrandr
9924       int foo;
9925       if (XRRQueryExtension(dpy,&foo,&foo)) {
9926         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
9927         if (!cimg::X11_attr().resolutions) {
9928           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
9929           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
9930         }
9931         if (cimg::X11_attr().resolutions) {
9932           cimg::X11_attr().curr_resolution = 0;
9933           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
9934             const unsigned int
9935               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
9936               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
9937             if (nw>=_width && nh>=_height &&
9938                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
9939                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
9940               cimg::X11_attr().curr_resolution = i;
9941           }
9942           if (cimg::X11_attr().curr_resolution>0) {
9943             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
9944             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
9945                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
9946             XRRFreeScreenConfigInfo(config);
9947             XSync(dpy,0);
9948           }
9949         }
9950       }
9951       if (!cimg::X11_attr().resolutions)
9952         cimg::warn(_cimgdisplay_instance
9953                    "init_fullscreen(): Xrandr extension not supported by the X server.",
9954                    cimgdisplay_instance);
9955 #endif
9956 
9957       const unsigned int sx = screen_width(), sy = screen_height();
9958       if (sx==_width && sy==_height) return;
9959       XSetWindowAttributes attr_set;
9960 
9961       attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy));
9962       attr_set.override_redirect = 1;
9963       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
9964                                          InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set);
9965       XEvent event;
9966       XSelectInput(dpy,_background_window,StructureNotifyMask);
9967       XMapRaised(dpy,_background_window);
9968       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
9969       while (event.type!=MapNotify);
9970 
9971       XWindowAttributes attr;
9972       do {
9973         XGetWindowAttributes(dpy,_background_window,&attr);
9974         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9975       } while (attr.map_state!=IsViewable);
9976     }
9977 
9978     void _desinit_fullscreen() {
9979       if (!_is_fullscreen) return;
9980       Display *const dpy = cimg::X11_attr().display;
9981       XUngrabKeyboard(dpy,CurrentTime);
9982 
9983 #ifdef cimg_use_xrandr
9984       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
9985         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
9986         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
9987         XRRFreeScreenConfigInfo(config);
9988         XSync(dpy,0);
9989         cimg::X11_attr().curr_resolution = 0;
9990       }
9991 #endif
9992       if (_background_window) XDestroyWindow(dpy,_background_window);
9993       _background_window = 0;
9994       _is_fullscreen = false;
9995     }
9996 
9997     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
9998       cimg::unused(dpy,error);
9999       cimg::X11_attr().is_shm_enabled = false;
10000       return 0;
10001     }
10002 
10003     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
10004                  const unsigned int normalization_type=3,
10005                  const bool fullscreen_flag=false, const bool closed_flag=false) {
10006       cimg::mutex(14);
10007 
10008       // Allocate space for window title
10009       const char *const nptitle = ptitle?ptitle:"";
10010       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
10011       char *const tmp_title = s?new char[s]:0;
10012       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
10013 
10014       // Destroy previous display window if existing
10015       if (!is_empty()) assign();
10016 
10017       // Open X11 display and retrieve graphical properties.
10018       Display* &dpy = cimg::X11_attr().display;
10019       if (!dpy) {
10020         dpy = XOpenDisplay(0);
10021         if (!dpy)
10022           throw CImgDisplayException(_cimgdisplay_instance
10023                                      "assign(): Failed to open X11 display.",
10024                                      cimgdisplay_instance);
10025 
10026         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
10027         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
10028             cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
10029           throw CImgDisplayException(_cimgdisplay_instance
10030                                      "assign(): Invalid %u bits screen mode detected "
10031                                      "(only 8, 16, 24 and 32 bits modes are managed).",
10032                                      cimgdisplay_instance,
10033                                      cimg::X11_attr().nb_bits);
10034         XVisualInfo vtemplate;
10035         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
10036         int nb_visuals;
10037         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
10038         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
10039         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
10040         XFree(vinfo);
10041 
10042         cimg_lock_display();
10043         cimg::X11_attr().events_thread = new pthread_t;
10044         pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
10045       } else cimg_lock_display();
10046 
10047       // Set display variables.
10048       _width = std::min(dimw,(unsigned int)screen_width());
10049       _height = std::min(dimh,(unsigned int)screen_height());
10050       _normalization = normalization_type<4?normalization_type:3;
10051       _is_fullscreen = fullscreen_flag;
10052       _window_x = _window_y = cimg::type<int>::min();
10053       _is_closed = closed_flag;
10054       _title = tmp_title;
10055       flush();
10056 
10057       // Create X11 window (and LUT, if 8bits display)
10058       if (_is_fullscreen) {
10059         if (!_is_closed) _init_fullscreen();
10060         const unsigned int sx = screen_width(), sy = screen_height();
10061         XSetWindowAttributes attr_set;
10062         attr_set.override_redirect = 1;
10063         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
10064                                 InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set);
10065       } else
10066         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
10067 
10068       XSelectInput(dpy,_window,
10069                    ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
10070                    EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
10071 
10072       XStoreName(dpy,_window,_title?_title:" ");
10073       if (cimg::X11_attr().nb_bits==8) {
10074         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
10075         _set_colormap(_colormap,3);
10076         XSetWindowColormap(dpy,_window,_colormap);
10077       }
10078 
10079       static const char *const _window_class = cimg_appname;
10080       XClassHint *const window_class = XAllocClassHint();
10081       window_class->res_name = (char*)_window_class;
10082       window_class->res_class = (char*)_window_class;
10083       XSetClassHint(dpy,_window,window_class);
10084       XFree(window_class);
10085 
10086       _window_width = _width;
10087       _window_height = _height;
10088 
10089       // Create XImage
10090 #ifdef cimg_use_xshm
10091       _shminfo = 0;
10092       if (XShmQueryExtension(dpy)) {
10093         _shminfo = new XShmSegmentInfo;
10094         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
10095                                  ZPixmap,0,_shminfo,_width,_height);
10096         if (!_image) { delete _shminfo; _shminfo = 0; }
10097         else {
10098           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
10099           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
10100           else {
10101             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
10102             if (_shminfo->shmaddr==(char*)-1) {
10103               shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
10104             } else {
10105               _shminfo->readOnly = 0;
10106               cimg::X11_attr().is_shm_enabled = true;
10107               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
10108               XShmAttach(dpy,_shminfo);
10109               XSync(dpy,0);
10110               XSetErrorHandler(oldXErrorHandler);
10111               if (!cimg::X11_attr().is_shm_enabled) {
10112                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
10113                 delete _shminfo; _shminfo = 0;
10114               }
10115             }
10116           }
10117         }
10118       }
10119       if (!_shminfo)
10120 #endif
10121         {
10122           const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
10123                                                                   (cimg::X11_attr().nb_bits==16?2:4));
10124           _data = std::malloc(buf_size);
10125           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
10126                                 ZPixmap,0,(char*)_data,_width,_height,8,0);
10127         }
10128 
10129       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
10130       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
10131       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
10132 
10133       if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
10134       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
10135       if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type<int>::min();
10136       cimg_unlock_display();
10137       cimg::mutex(14,0);
10138     }
10139 
10140     CImgDisplay& assign() {
10141       if (is_empty()) return flush();
10142       Display *const dpy = cimg::X11_attr().display;
10143       cimg_lock_display();
10144 
10145       // Remove display window from event thread list.
10146       unsigned int i;
10147       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
10148       for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
10149       --cimg::X11_attr().nb_wins;
10150 
10151       // Destroy window, image, colormap and title.
10152       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
10153 
10154 
10155 #ifdef cimg_use_xshm
10156       if (_shminfo) {
10157         XShmDetach(dpy,_shminfo);
10158         shmdt(_shminfo->shmaddr);
10159         shmctl(_shminfo->shmid,IPC_RMID,0);
10160         delete _shminfo;
10161         _shminfo = 0;
10162       }
10163 #endif
10164 
10165       XDestroyImage(_image);
10166       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
10167       XDestroyWindow(dpy,_window);
10168       XSync(dpy,0);
10169       _window = 0; _colormap = 0; _data = 0; _image = 0;
10170 
10171       // Reset display variables.
10172       delete[] _title;
10173       _width = _height = _normalization = _window_width = _window_height = 0;
10174       _window_x = _window_y = cimg::type<int>::min();
10175       _is_fullscreen = false;
10176       _is_closed = true;
10177       _min = _max = 0;
10178       _title = 0;
10179       flush();
10180 
10181       cimg_unlock_display();
10182       return *this;
10183     }
10184 
10185     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
10186                         const unsigned int normalization_type=3,
10187                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10188       if (!dimw || !dimh) return assign();
10189       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
10190       _min = _max = 0;
10191       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
10192                            (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
10193                   (size_t)_width*_height);
10194       return paint();
10195     }
10196 
10197     template<typename T>
10198     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
10199                         const unsigned int normalization_type=3,
10200                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10201       if (!img) return assign();
10202       CImg<T> tmp;
10203       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10204                                                                            (img._height - 1)/2,
10205                                                                            (img._depth - 1)/2));
10206       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10207       if (_normalization==2) _min = (float)nimg.min_max(_max);
10208       return render(nimg).paint();
10209     }
10210 
10211     template<typename T>
10212     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
10213                         const unsigned int normalization_type=3,
10214                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10215       if (!list) return assign();
10216       CImg<T> tmp;
10217       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10218                                                                                            (img._height - 1)/2,
10219                                                                                            (img._depth - 1)/2));
10220       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10221       if (_normalization==2) _min = (float)nimg.min_max(_max);
10222       return render(nimg).paint();
10223     }
10224 
10225     CImgDisplay& assign(const CImgDisplay& disp) {
10226       if (!disp) return assign();
10227       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
10228       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
10229                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
10230                                     sizeof(unsigned int))*(size_t)_width*_height);
10231       return paint();
10232     }
10233 
10234     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
10235       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
10236       if (is_empty()) return assign(nwidth,nheight);
10237       Display *const dpy = cimg::X11_attr().display;
10238       const unsigned int
10239         tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
10240         tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
10241         dimx = tmpdimx?tmpdimx:1,
10242         dimy = tmpdimy?tmpdimy:1;
10243       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
10244         show();
10245         cimg_lock_display();
10246         if (_window_width!=dimx || _window_height!=dimy) {
10247           XWindowAttributes attr;
10248           for (unsigned int i = 0; i<10; ++i) {
10249             XResizeWindow(dpy,_window,dimx,dimy);
10250             XGetWindowAttributes(dpy,_window,&attr);
10251             if (attr.width==(int)dimx && attr.height==(int)dimy) break;
10252             cimg::wait(5,&_timer);
10253           }
10254         }
10255         if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
10256           case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
10257           case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
10258           default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
10259           }
10260         _window_width = _width = dimx; _window_height = _height = dimy;
10261         cimg_unlock_display();
10262       }
10263       _is_resized = false;
10264       if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
10265       if (force_redraw) return paint();
10266       return *this;
10267     }
10268 
10269     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
10270       if (is_empty()) return *this;
10271       if (force_redraw) {
10272         const cimg_ulong buf_size = (cimg_ulong)_width*_height*
10273           (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
10274         void *image_data = std::malloc(buf_size);
10275         std::memcpy(image_data,_data,buf_size);
10276         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10277         std::memcpy(_data,image_data,buf_size);
10278         std::free(image_data);
10279         return paint();
10280       }
10281       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10282     }
10283 
10284     CImgDisplay& show() {
10285       if (is_empty() || !_is_closed) return *this;
10286       cimg_lock_display();
10287       _is_closed = false;
10288       if (_is_fullscreen) _init_fullscreen();
10289       _map_window();
10290       cimg_unlock_display();
10291       return paint();
10292     }
10293 
10294     CImgDisplay& close() {
10295       if (is_empty() || _is_closed) return *this;
10296       Display *const dpy = cimg::X11_attr().display;
10297       cimg_lock_display();
10298       if (_is_fullscreen) _desinit_fullscreen();
10299       XUnmapWindow(dpy,_window);
10300       _window_x = _window_y = cimg::type<int>::min();
10301       _is_closed = true;
10302       cimg_unlock_display();
10303       return *this;
10304     }
10305 
10306     CImgDisplay& move(const int posx, const int posy) {
10307       if (is_empty()) return *this;
10308       show();
10309       if (_window_x!=posx || _window_y!=posy) {
10310         Display *const dpy = cimg::X11_attr().display;
10311         cimg_lock_display();
10312         XMoveWindow(dpy,_window,posx,posy);
10313         _window_x = posx;
10314         _window_y = posy;
10315         cimg_unlock_display();
10316       }
10317       _is_moved = false;
10318       return paint();
10319     }
10320 
10321     CImgDisplay& show_mouse() {
10322       if (is_empty()) return *this;
10323       Display *const dpy = cimg::X11_attr().display;
10324       cimg_lock_display();
10325       XUndefineCursor(dpy,_window);
10326       cimg_unlock_display();
10327       return *this;
10328     }
10329 
10330     CImgDisplay& hide_mouse() {
10331       if (is_empty()) return *this;
10332       Display *const dpy = cimg::X11_attr().display;
10333       cimg_lock_display();
10334       static const char pix_data[8] = { 0 };
10335       XColor col;
10336       col.red = col.green = col.blue = 0;
10337       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
10338       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
10339       XFreePixmap(dpy,pix);
10340       XDefineCursor(dpy,_window,cur);
10341       cimg_unlock_display();
10342       return *this;
10343     }
10344 
10345     CImgDisplay& set_mouse(const int posx, const int posy) {
10346       if (is_empty() || _is_closed) return *this;
10347       Display *const dpy = cimg::X11_attr().display;
10348       cimg_lock_display();
10349       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
10350       _mouse_x = posx; _mouse_y = posy;
10351       _is_moved = false;
10352       XSync(dpy,0);
10353       cimg_unlock_display();
10354       return *this;
10355     }
10356 
10357     CImgDisplay& set_title(const char *const format, ...) {
10358       if (is_empty()) return *this;
10359       char *const tmp = new char[1024];
10360       va_list ap;
10361       va_start(ap, format);
10362       cimg_vsnprintf(tmp,1024,format,ap);
10363       va_end(ap);
10364       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
10365       delete[] _title;
10366       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
10367       _title = new char[s];
10368       std::memcpy(_title,tmp,s*sizeof(char));
10369       Display *const dpy = cimg::X11_attr().display;
10370       cimg_lock_display();
10371       XStoreName(dpy,_window,tmp);
10372       cimg_unlock_display();
10373       delete[] tmp;
10374       return *this;
10375     }
10376 
10377     template<typename T>
10378     CImgDisplay& display(const CImg<T>& img) {
10379       if (!img)
10380         throw CImgArgumentException(_cimgdisplay_instance
10381                                     "display(): Empty specified image.",
10382                                     cimgdisplay_instance);
10383       if (is_empty()) return assign(img);
10384       return render(img).paint(false);
10385     }
10386 
10387     CImgDisplay& paint(const bool wait_expose=true) {
10388       if (is_empty()) return *this;
10389       cimg_lock_display();
10390       _paint(wait_expose);
10391       cimg_unlock_display();
10392       return *this;
10393     }
10394 
10395     template<typename T>
10396     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
10397       if (!img)
10398         throw CImgArgumentException(_cimgdisplay_instance
10399                                     "render(): Empty specified image.",
10400                                     cimgdisplay_instance);
10401       if (is_empty()) return *this;
10402       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
10403                                                              (img._depth - 1)/2));
10404       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
10405         return render(img.get_resize(_width,_height,1,-100,1));
10406       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
10407         static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
10408         return render(img.get_index(default_colormap,1,false));
10409       }
10410 
10411       const T
10412         *data1 = img._data,
10413         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
10414         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
10415 
10416       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
10417       cimg_lock_display();
10418 
10419       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
10420         _min = _max = 0;
10421         switch (cimg::X11_attr().nb_bits) {
10422         case 8 : { // 256 colormap, no normalization
10423           _set_colormap(_colormap,img._spectrum);
10424           unsigned char
10425             *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
10426             new unsigned char[(size_t)img._width*img._height],
10427             *ptrd = (unsigned char*)ndata;
10428           switch (img._spectrum) {
10429           case 1 :
10430             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10431               (*ptrd++) = (unsigned char)*(data1++);
10432             break;
10433           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10434               const unsigned char
10435                 R = (unsigned char)*(data1++),
10436                 G = (unsigned char)*(data2++);
10437               (*ptrd++) = (R&0xf0) | (G>>4);
10438             } break;
10439           default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10440               const unsigned char
10441                 R = (unsigned char)*(data1++),
10442                 G = (unsigned char)*(data2++),
10443                 B = (unsigned char)*(data3++);
10444               (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
10445             }
10446           }
10447           if (ndata!=_data) {
10448             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
10449             delete[] ndata;
10450           }
10451         } break;
10452         case 16 : { // 16 bits colors, no normalization
10453           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
10454             new unsigned short[(size_t)img._width*img._height];
10455           unsigned char *ptrd = (unsigned char*)ndata;
10456           const unsigned int M = 248;
10457           switch (img._spectrum) {
10458           case 1 :
10459             if (cimg::X11_attr().byte_order)
10460               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10461                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
10462                 ptrd[0] = (val&M) | (G>>3);
10463                 ptrd[1] = (G<<5) | (G>>1);
10464                 ptrd+=2;
10465               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10466                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
10467                 ptrd[0] = (G<<5) | (G>>1);
10468                 ptrd[1] = (val&M) | (G>>3);
10469                 ptrd+=2;
10470               }
10471             break;
10472           case 2 :
10473             if (cimg::X11_attr().byte_order)
10474               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10475                 const unsigned char G = (unsigned char)*(data2++)>>2;
10476                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
10477                 ptrd[1] = (G<<5);
10478                 ptrd+=2;
10479               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10480                 const unsigned char G = (unsigned char)*(data2++)>>2;
10481                 ptrd[0] = (G<<5);
10482                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
10483                 ptrd+=2;
10484               }
10485             break;
10486           default :
10487             if (cimg::X11_attr().byte_order)
10488               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10489                 const unsigned char G = (unsigned char)*(data2++)>>2;
10490                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
10491                 ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
10492                 ptrd+=2;
10493               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10494                 const unsigned char G = (unsigned char)*(data2++)>>2;
10495                 ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
10496                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
10497                 ptrd+=2;
10498               }
10499           }
10500           if (ndata!=_data) {
10501             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
10502             delete[] ndata;
10503           }
10504         } break;
10505         default : { // 24 bits colors, no normalization
10506           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
10507             new unsigned int[(size_t)img._width*img._height];
10508           if (sizeof(int)==4) { // 32 bits int uses optimized version
10509             unsigned int *ptrd = ndata;
10510             switch (img._spectrum) {
10511             case 1 :
10512               if (cimg::X11_attr().byte_order==cimg::endianness())
10513                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10514                   const unsigned char val = (unsigned char)*(data1++);
10515                   *(ptrd++) = (val<<16) | (val<<8) | val;
10516                 }
10517               else
10518                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10519                  const unsigned char val = (unsigned char)*(data1++);
10520                   *(ptrd++) = (val<<16) | (val<<8) | val;
10521                 }
10522               break;
10523             case 2 :
10524               if (cimg::X11_attr().byte_order==cimg::endianness())
10525                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10526                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
10527               else
10528                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10529                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
10530               break;
10531             default :
10532               if (cimg::X11_attr().byte_order==cimg::endianness())
10533                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10534                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
10535                     (unsigned char)*(data3++);
10536               else
10537                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10538                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
10539                     ((unsigned char)*(data1++)<<8);
10540             }
10541           } else {
10542             unsigned char *ptrd = (unsigned char*)ndata;
10543             switch (img._spectrum) {
10544             case 1 :
10545               if (cimg::X11_attr().byte_order)
10546                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10547                   ptrd[0] = 0;
10548                   ptrd[1] = (unsigned char)*(data1++);
10549                   ptrd[2] = 0;
10550                   ptrd[3] = 0;
10551                   ptrd+=4;
10552                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10553                   ptrd[0] = 0;
10554                   ptrd[1] = 0;
10555                   ptrd[2] = (unsigned char)*(data1++);
10556                   ptrd[3] = 0;
10557                   ptrd+=4;
10558                 }
10559               break;
10560             case 2 :
10561               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
10562               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10563                 ptrd[0] = 0;
10564                 ptrd[1] = (unsigned char)*(data2++);
10565                 ptrd[2] = (unsigned char)*(data1++);
10566                 ptrd[3] = 0;
10567                 ptrd+=4;
10568               }
10569               break;
10570             default :
10571               if (cimg::X11_attr().byte_order)
10572                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10573                   ptrd[0] = 0;
10574                   ptrd[1] = (unsigned char)*(data1++);
10575                   ptrd[2] = (unsigned char)*(data2++);
10576                   ptrd[3] = (unsigned char)*(data3++);
10577                   ptrd+=4;
10578                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10579                   ptrd[0] = (unsigned char)*(data3++);
10580                   ptrd[1] = (unsigned char)*(data2++);
10581                   ptrd[2] = (unsigned char)*(data1++);
10582                   ptrd[3] = 0;
10583                   ptrd+=4;
10584                 }
10585             }
10586           }
10587           if (ndata!=_data) {
10588             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
10589             delete[] ndata;
10590           }
10591         }
10592         }
10593       } else {
10594         if (_normalization==3) {
10595           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
10596           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
10597         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
10598         const float delta = _max - _min, mm = 255/(delta?delta:1.f);
10599         switch (cimg::X11_attr().nb_bits) {
10600         case 8 : { // 256 colormap, with normalization
10601           _set_colormap(_colormap,img._spectrum);
10602           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
10603             new unsigned char[(size_t)img._width*img._height];
10604           unsigned char *ptrd = (unsigned char*)ndata;
10605           switch (img._spectrum) {
10606           case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10607               const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
10608               *(ptrd++) = R;
10609             } break;
10610           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10611               const unsigned char
10612                 R = (unsigned char)((*(data1++) - _min)*mm),
10613                 G = (unsigned char)((*(data2++) - _min)*mm);
10614             (*ptrd++) = (R&0xf0) | (G>>4);
10615           } break;
10616           default :
10617             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10618               const unsigned char
10619                 R = (unsigned char)((*(data1++) - _min)*mm),
10620                 G = (unsigned char)((*(data2++) - _min)*mm),
10621                 B = (unsigned char)((*(data3++) - _min)*mm);
10622               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
10623             }
10624           }
10625           if (ndata!=_data) {
10626             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
10627             delete[] ndata;
10628           }
10629         } break;
10630         case 16 : { // 16 bits colors, with normalization
10631           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
10632             new unsigned short[(size_t)img._width*img._height];
10633           unsigned char *ptrd = (unsigned char*)ndata;
10634           const unsigned int M = 248;
10635           switch (img._spectrum) {
10636           case 1 :
10637             if (cimg::X11_attr().byte_order)
10638               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10639                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
10640                 ptrd[0] = (val&M) | (G>>3);
10641                 ptrd[1] = (G<<5) | (val>>3);
10642                 ptrd+=2;
10643               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10644                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
10645                 ptrd[0] = (G<<5) | (val>>3);
10646                 ptrd[1] = (val&M) | (G>>3);
10647                 ptrd+=2;
10648               }
10649             break;
10650           case 2 :
10651             if (cimg::X11_attr().byte_order)
10652               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10653                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10654                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10655                 ptrd[1] = (G<<5);
10656                 ptrd+=2;
10657               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10658                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10659                 ptrd[0] = (G<<5);
10660                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10661                 ptrd+=2;
10662               }
10663             break;
10664           default :
10665             if (cimg::X11_attr().byte_order)
10666               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10667                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10668                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10669                 ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
10670                 ptrd+=2;
10671               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10672                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10673                 ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
10674                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10675                 ptrd+=2;
10676               }
10677           }
10678           if (ndata!=_data) {
10679             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
10680             delete[] ndata;
10681           }
10682         } break;
10683         default : { // 24 bits colors, with normalization
10684           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
10685             new unsigned int[(size_t)img._width*img._height];
10686           if (sizeof(int)==4) { // 32 bits int uses optimized version
10687             unsigned int *ptrd = ndata;
10688             switch (img._spectrum) {
10689             case 1 :
10690               if (cimg::X11_attr().byte_order==cimg::endianness())
10691                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10692                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10693                   *(ptrd++) = (val<<16) | (val<<8) | val;
10694                 }
10695               else
10696                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10697                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10698                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
10699                 }
10700               break;
10701             case 2 :
10702               if (cimg::X11_attr().byte_order==cimg::endianness())
10703                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10704                   *(ptrd++) =
10705                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
10706                     ((unsigned char)((*(data2++) - _min)*mm)<<8);
10707               else
10708                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10709                   *(ptrd++) =
10710                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
10711                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
10712               break;
10713             default :
10714               if (cimg::X11_attr().byte_order==cimg::endianness())
10715                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10716                   *(ptrd++) =
10717                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
10718                     ((unsigned char)((*(data2++) - _min)*mm)<<8) |
10719                     (unsigned char)((*(data3++) - _min)*mm);
10720               else
10721                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10722                   *(ptrd++) =
10723                     ((unsigned char)((*(data3++) - _min)*mm)<<24) |
10724                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
10725                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
10726             }
10727           } else {
10728             unsigned char *ptrd = (unsigned char*)ndata;
10729             switch (img._spectrum) {
10730             case 1 :
10731               if (cimg::X11_attr().byte_order)
10732                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10733                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10734                   ptrd[0] = 0;
10735                   ptrd[1] = val;
10736                   ptrd[2] = val;
10737                   ptrd[3] = val;
10738                   ptrd+=4;
10739                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10740                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10741                   ptrd[0] = val;
10742                   ptrd[1] = val;
10743                   ptrd[2] = val;
10744                   ptrd[3] = 0;
10745                   ptrd+=4;
10746                 }
10747               break;
10748             case 2 :
10749               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
10750               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10751                 ptrd[0] = 0;
10752                 ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
10753                 ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
10754                 ptrd[3] = 0;
10755                 ptrd+=4;
10756               }
10757               break;
10758             default :
10759               if (cimg::X11_attr().byte_order)
10760                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10761                   ptrd[0] = 0;
10762                   ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
10763                   ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
10764                   ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
10765                   ptrd+=4;
10766                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10767                   ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
10768                   ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
10769                   ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
10770                   ptrd[3] = 0;
10771                   ptrd+=4;
10772                 }
10773             }
10774           }
10775           if (ndata!=_data) {
10776             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
10777             delete[] ndata;
10778           }
10779         }
10780         }
10781       }
10782       cimg_unlock_display();
10783       return *this;
10784     }
10785 
10786     template<typename T>
10787     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
10788       img.assign();
10789       Display *dpy = cimg::X11_attr().display;
10790       cimg_lock_display();
10791       if (!dpy) {
10792         dpy = XOpenDisplay(0);
10793         if (!dpy)
10794           throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
10795       }
10796       Window root = DefaultRootWindow(dpy);
10797       XWindowAttributes gwa;
10798       XGetWindowAttributes(dpy,root,&gwa);
10799       const int width = gwa.width, height = gwa.height;
10800       int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
10801       if (_x0>_x1) cimg::swap(_x0,_x1);
10802       if (_y0>_y1) cimg::swap(_y0,_y1);
10803 
10804       XImage *image = 0;
10805       if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
10806         _x0 = std::max(_x0,0);
10807         _y0 = std::max(_y0,0);
10808         _x1 = std::min(_x1,width - 1);
10809         _y1 = std::min(_y1,height - 1);
10810         image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
10811 
10812         if (image) {
10813           const unsigned long
10814             red_mask = image->red_mask,
10815             green_mask = image->green_mask,
10816             blue_mask = image->blue_mask;
10817           img.assign(image->width,image->height,1,3);
10818           T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
10819           cimg_forXY(img,x,y) {
10820             const unsigned long pixel = XGetPixel(image,x,y);
10821             *(pR++) = (T)((pixel & red_mask)>>16);
10822             *(pG++) = (T)((pixel & green_mask)>>8);
10823             *(pB++) = (T)(pixel & blue_mask);
10824           }
10825           XDestroyImage(image);
10826         }
10827       }
10828       if (!cimg::X11_attr().display) XCloseDisplay(dpy);
10829       cimg_unlock_display();
10830       if (img.is_empty())
10831         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
10832                                    "with coordinates (%d,%d)-(%d,%d).",
10833                                    x0,y0,x1,y1);
10834     }
10835 
10836     template<typename T>
10837     const CImgDisplay& snapshot(CImg<T>& img) const {
10838       if (is_empty()) { img.assign(); return *this; }
10839       const unsigned char *ptrs = (unsigned char*)_data;
10840       img.assign(_width,_height,1,3);
10841       T
10842         *data1 = img.data(0,0,0,0),
10843         *data2 = img.data(0,0,0,1),
10844         *data3 = img.data(0,0,0,2);
10845       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
10846       switch (cimg::X11_attr().nb_bits) {
10847       case 8 : {
10848         for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10849           const unsigned char val = *(ptrs++);
10850           *(data1++) = (T)(val&0xe0);
10851           *(data2++) = (T)((val&0x1c)<<3);
10852           *(data3++) = (T)(val<<6);
10853         }
10854       } break;
10855       case 16 : {
10856         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10857           const unsigned char
10858             val0 = ptrs[0],
10859             val1 = ptrs[1];
10860           ptrs+=2;
10861           *(data1++) = (T)(val0&0xf8);
10862           *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
10863           *(data3++) = (T)(val1<<3);
10864           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10865           const unsigned short
10866             val0 = ptrs[0],
10867             val1 = ptrs[1];
10868           ptrs+=2;
10869           *(data1++) = (T)(val1&0xf8);
10870           *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
10871           *(data3++) = (T)(val0<<3);
10872         }
10873       } break;
10874       default : {
10875         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10876           ++ptrs;
10877           *(data1++) = (T)ptrs[0];
10878           *(data2++) = (T)ptrs[1];
10879           *(data3++) = (T)ptrs[2];
10880           ptrs+=3;
10881           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10882             *(data3++) = (T)ptrs[0];
10883             *(data2++) = (T)ptrs[1];
10884             *(data1++) = (T)ptrs[2];
10885             ptrs+=3;
10886             ++ptrs;
10887           }
10888       }
10889       }
10890       return *this;
10891     }
10892 
10893     // Windows-based implementation.
10894     //-------------------------------
10895 #elif cimg_display==2
10896 
10897     bool _is_mouse_tracked, _is_cursor_visible;
10898     HANDLE _thread, _is_created, _mutex;
10899     HWND _window, _background_window;
10900     CLIENTCREATESTRUCT _ccs;
10901     unsigned int *_data;
10902     DEVMODE _curr_mode;
10903     BITMAPINFO _bmi;
10904     HDC _hdc;
10905 
10906     static int screen_width() {
10907       DEVMODE mode;
10908       mode.dmSize = sizeof(DEVMODE);
10909       mode.dmDriverExtra = 0;
10910       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
10911       return (int)mode.dmPelsWidth;
10912     }
10913 
10914     static int screen_height() {
10915       DEVMODE mode;
10916       mode.dmSize = sizeof(DEVMODE);
10917       mode.dmDriverExtra = 0;
10918       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
10919       return (int)mode.dmPelsHeight;
10920     }
10921 
10922     static void wait_all() {
10923       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
10924     }
10925 
10926     static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
10927 #ifdef _WIN64
10928       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
10929 #else
10930       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
10931 #endif
10932       MSG st_msg;
10933       switch (msg) {
10934       case WM_CLOSE :
10935         disp->_mouse_x = disp->_mouse_y = -1;
10936         disp->_window_x = disp->_window_y = cimg::type<int>::min();
10937         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
10938         ReleaseMutex(disp->_mutex);
10939         ShowWindow(disp->_window,SW_HIDE);
10940         disp->_is_event = true;
10941         SetEvent(cimg::Win32_attr().wait_event);
10942         return 0;
10943       case WM_SIZE : {
10944         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
10945         WaitForSingleObject(disp->_mutex,INFINITE);
10946         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
10947         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
10948           disp->_window_width = nw;
10949           disp->_window_height = nh;
10950           disp->_mouse_x = disp->_mouse_y = -1;
10951           disp->_is_resized = disp->_is_event = true;
10952           SetEvent(cimg::Win32_attr().wait_event);
10953         }
10954         ReleaseMutex(disp->_mutex);
10955       } break;
10956       case WM_MOVE : {
10957         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
10958         WaitForSingleObject(disp->_mutex,INFINITE);
10959         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
10960         if (nx!=disp->_window_x || ny!=disp->_window_y) {
10961           disp->_window_x = nx;
10962           disp->_window_y = ny;
10963           disp->_is_moved = disp->_is_event = true;
10964           SetEvent(cimg::Win32_attr().wait_event);
10965         }
10966         ReleaseMutex(disp->_mutex);
10967       } break;
10968       case WM_PAINT :
10969         disp->paint();
10970         cimg_lock_display();
10971         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
10972         cimg_unlock_display();
10973         break;
10974       case WM_ERASEBKGND :
10975         //        return 0;
10976         break;
10977       case WM_KEYDOWN :
10978         disp->set_key((unsigned int)wParam);
10979         SetEvent(cimg::Win32_attr().wait_event);
10980         break;
10981       case WM_KEYUP :
10982         disp->set_key((unsigned int)wParam,false);
10983         SetEvent(cimg::Win32_attr().wait_event);
10984         break;
10985       case WM_MOUSEMOVE : {
10986         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
10987         disp->_mouse_x = LOWORD(lParam);
10988         disp->_mouse_y = HIWORD(lParam);
10989 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
10990         if (!disp->_is_mouse_tracked) {
10991           TRACKMOUSEEVENT tme;
10992           tme.cbSize = sizeof(TRACKMOUSEEVENT);
10993           tme.dwFlags = TME_LEAVE;
10994           tme.hwndTrack = disp->_window;
10995           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
10996         }
10997 #endif
10998         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
10999           disp->_mouse_x = disp->_mouse_y = -1;
11000         disp->_is_event = true;
11001         SetEvent(cimg::Win32_attr().wait_event);
11002         cimg_lock_display();
11003         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
11004         cimg_unlock_display();
11005       } break;
11006       case WM_MOUSELEAVE : {
11007         disp->_mouse_x = disp->_mouse_y = -1;
11008         disp->_is_mouse_tracked = false;
11009         cimg_lock_display();
11010         while (ShowCursor(TRUE)<0) {}
11011         cimg_unlock_display();
11012       } break;
11013       case WM_LBUTTONDOWN :
11014         disp->set_button(1);
11015         SetEvent(cimg::Win32_attr().wait_event);
11016         break;
11017       case WM_RBUTTONDOWN :
11018         disp->set_button(2);
11019         SetEvent(cimg::Win32_attr().wait_event);
11020         break;
11021       case WM_MBUTTONDOWN :
11022         disp->set_button(3);
11023         SetEvent(cimg::Win32_attr().wait_event);
11024         break;
11025       case WM_LBUTTONUP :
11026         disp->set_button(1,false);
11027         SetEvent(cimg::Win32_attr().wait_event);
11028         break;
11029       case WM_RBUTTONUP :
11030         disp->set_button(2,false);
11031         SetEvent(cimg::Win32_attr().wait_event);
11032         break;
11033       case WM_MBUTTONUP :
11034         disp->set_button(3,false);
11035         SetEvent(cimg::Win32_attr().wait_event);
11036         break;
11037       case 0x020A : // WM_MOUSEWHEEL:
11038         disp->set_wheel((int)((short)HIWORD(wParam))/120);
11039         SetEvent(cimg::Win32_attr().wait_event);
11040       }
11041       return DefWindowProc(window,msg,wParam,lParam);
11042     }
11043 
11044     static DWORD WINAPI _events_thread(void* arg) {
11045       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
11046       const char *const title = (const char*)(((void**)arg)[1]);
11047       MSG msg;
11048       delete[] (void**)arg;
11049       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
11050       disp->_bmi.bmiHeader.biWidth = disp->width();
11051       disp->_bmi.bmiHeader.biHeight = -disp->height();
11052       disp->_bmi.bmiHeader.biPlanes = 1;
11053       disp->_bmi.bmiHeader.biBitCount = 32;
11054       disp->_bmi.bmiHeader.biCompression = BI_RGB;
11055       disp->_bmi.bmiHeader.biSizeImage = 0;
11056       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
11057       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
11058       disp->_bmi.bmiHeader.biClrUsed = 0;
11059       disp->_bmi.bmiHeader.biClrImportant = 0;
11060       disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
11061       if (!disp->_is_fullscreen) { // Normal window
11062         RECT rect;
11063         rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
11064         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11065         const int
11066           border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
11067           border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1),
11068           ww = disp->width() + 2*border1,
11069           wh = disp->height() + border1 + border2,
11070           sw = CImgDisplay::screen_width(),
11071           sh = CImgDisplay::screen_height();
11072         int
11073           wx = (int)cimg::round(cimg::rand(0,sw - ww -1)),
11074           wy = (int)cimg::round(cimg::rand(64,sh - wh - 65));
11075         if (wx + ww>=sw) wx = sw - ww;
11076         if (wy + wh>=sh) wy = sh - wh;
11077         if (wx<0) wx = 0;
11078         if (wy<0) wy = 0;
11079         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
11080                                       (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)),
11081                                       wx,wy,ww,wh,0,0,0,&(disp->_ccs));
11082         if (!disp->_is_closed) {
11083           GetWindowRect(disp->_window,&rect);
11084           disp->_window_x = rect.left;
11085           disp->_window_y = rect.top;
11086         } else disp->_window_x = disp->_window_y = cimg::type<int>::min();
11087       } else { // Fullscreen window
11088         const unsigned int
11089           sx = (unsigned int)screen_width(),
11090           sy = (unsigned int)screen_height();
11091         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
11092                                       (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)),
11093                                       (int)(sx - disp->_width)/2,
11094                                       (int)(sy - disp->_height)/2,
11095                                       disp->width(),disp->height(),0,0,0,&(disp->_ccs));
11096         disp->_window_x = disp->_window_y = 0;
11097       }
11098       SetForegroundWindow(disp->_window);
11099       disp->_hdc = GetDC(disp->_window);
11100       disp->_window_width = disp->_width;
11101       disp->_window_height = disp->_height;
11102       disp->flush();
11103 #ifdef _WIN64
11104       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
11105       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
11106 #else
11107       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
11108       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
11109 #endif
11110       SetEvent(disp->_is_created);
11111       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
11112       return 0;
11113     }
11114 
11115     CImgDisplay& _update_window_pos() {
11116       if (_is_closed) _window_x = _window_y = cimg::type<int>::min();
11117       else {
11118         RECT rect;
11119         rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
11120         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11121         GetWindowRect(_window,&rect);
11122         _window_x = rect.left;
11123         _window_y = rect.top;
11124       }
11125       return *this;
11126     }
11127 
11128     void _init_fullscreen() {
11129       _background_window = 0;
11130       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
11131       else {
11132 /*        DEVMODE mode;
11133         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
11134         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
11135           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
11136           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
11137             bestbpp = mode.dmBitsPerPel;
11138             ibest = imode;
11139             bw = nw; bh = nh;
11140           }
11141         }
11142         if (bestbpp) {
11143           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
11144           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
11145           EnumDisplaySettings(0,ibest,&mode);
11146           ChangeDisplaySettings(&mode,0);
11147         } else _curr_mode.dmSize = 0;
11148 */
11149         _curr_mode.dmSize = 0;
11150         const unsigned int
11151           sx = (unsigned int)screen_width(),
11152           sy = (unsigned int)screen_height();
11153         if (sx!=_width || sy!=_height) {
11154           CLIENTCREATESTRUCT background_ccs = { 0,0 };
11155           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE,
11156                                              0,0,(int)sx,(int)sy,0,0,0,&background_ccs);
11157           SetForegroundWindow(_background_window);
11158         }
11159       }
11160     }
11161 
11162     void _desinit_fullscreen() {
11163       if (!_is_fullscreen) return;
11164       if (_background_window) DestroyWindow(_background_window);
11165       _background_window = 0;
11166       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
11167       _is_fullscreen = false;
11168     }
11169 
11170     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
11171                          const unsigned int normalization_type=3,
11172                          const bool fullscreen_flag=false, const bool closed_flag=false) {
11173 
11174       // Allocate space for window title
11175       const char *const nptitle = ptitle?ptitle:"";
11176       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
11177       char *const tmp_title = s?new char[s]:0;
11178       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
11179 
11180       // Destroy previous window if existing
11181       if (!is_empty()) assign();
11182 
11183       // Set display variables
11184       _width = std::min(dimw,(unsigned int)screen_width());
11185       _height = std::min(dimh,(unsigned int)screen_height());
11186       _normalization = normalization_type<4?normalization_type:3;
11187       _is_fullscreen = fullscreen_flag;
11188       _window_x = _window_y = cimg::type<int>::min();
11189       _is_closed = closed_flag;
11190       _is_cursor_visible = true;
11191       _is_mouse_tracked = false;
11192       _title = tmp_title;
11193       flush();
11194       if (_is_fullscreen) _init_fullscreen();
11195 
11196       // Create event thread
11197       void *const arg = (void*)(new void*[2]);
11198       ((void**)arg)[0] = (void*)this;
11199       ((void**)arg)[1] = (void*)_title;
11200       _mutex = CreateMutex(0,FALSE_WIN,0);
11201       _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0);
11202       _thread = CreateThread(0,0,_events_thread,arg,0,0);
11203       WaitForSingleObject(_is_created,INFINITE);
11204       return *this;
11205     }
11206 
11207     CImgDisplay& assign() {
11208       if (is_empty()) return flush();
11209       DestroyWindow(_window);
11210       TerminateThread(_thread,0);
11211       delete[] _data;
11212       delete[] _title;
11213       _data = 0;
11214       _title = 0;
11215       if (_is_fullscreen) _desinit_fullscreen();
11216       _width = _height = _normalization = _window_width = _window_height = 0;
11217       _window_x = _window_y = cimg::type<int>::min();
11218       _is_fullscreen = false;
11219       _is_closed = true;
11220       _min = _max = 0;
11221       _title = 0;
11222       flush();
11223       return *this;
11224     }
11225 
11226     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
11227                         const unsigned int normalization_type=3,
11228                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11229       if (!dimw || !dimh) return assign();
11230       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
11231       _min = _max = 0;
11232       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
11233       return paint();
11234     }
11235 
11236     template<typename T>
11237     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
11238                         const unsigned int normalization_type=3,
11239                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11240       if (!img) return assign();
11241       CImg<T> tmp;
11242       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
11243                                                                            (img._height - 1)/2,
11244                                                                            (img._depth - 1)/2));
11245       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
11246       if (_normalization==2) _min = (float)nimg.min_max(_max);
11247       return display(nimg);
11248     }
11249 
11250     template<typename T>
11251     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
11252                         const unsigned int normalization_type=3,
11253                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11254       if (!list) return assign();
11255       CImg<T> tmp;
11256       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
11257                                                                                            (img._height - 1)/2,
11258                                                                                            (img._depth - 1)/2));
11259       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
11260       if (_normalization==2) _min = (float)nimg.min_max(_max);
11261       return display(nimg);
11262     }
11263 
11264     CImgDisplay& assign(const CImgDisplay& disp) {
11265       if (!disp) return assign();
11266       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
11267       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
11268       return paint();
11269     }
11270 
11271     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
11272       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
11273       if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight);
11274       const unsigned int
11275         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
11276         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
11277         dimx = tmpdimx?tmpdimx:1,
11278         dimy = tmpdimy?tmpdimy:1;
11279       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
11280         if (_window_width!=dimx || _window_height!=dimy) {
11281           RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
11282           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11283           const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
11284           SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
11285         }
11286         if (_width!=dimx || _height!=dimy) {
11287           unsigned int *const ndata = new unsigned int[dimx*dimy];
11288           if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
11289           else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
11290           delete[] _data;
11291           _data = ndata;
11292           _bmi.bmiHeader.biWidth = (LONG)dimx;
11293           _bmi.bmiHeader.biHeight = -(int)dimy;
11294           _width = dimx;
11295           _height = dimy;
11296         }
11297         _window_width = dimx; _window_height = dimy;
11298         show();
11299       }
11300       _is_resized = false;
11301       if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
11302       if (force_redraw) return paint();
11303       return *this;
11304     }
11305 
11306     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
11307       if (is_empty()) return *this;
11308       if (force_redraw) {
11309         const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
11310         void *odata = std::malloc(buf_size);
11311         if (odata) {
11312           std::memcpy(odata,_data,buf_size);
11313           assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
11314           std::memcpy(_data,odata,buf_size);
11315           std::free(odata);
11316         }
11317         return paint();
11318       }
11319       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
11320     }
11321 
11322     CImgDisplay& show() {
11323       if (is_empty() || !_is_closed) return *this;
11324       _is_closed = false;
11325       if (_is_fullscreen) _init_fullscreen();
11326       ShowWindow(_window,SW_SHOW);
11327       _update_window_pos();
11328       return paint();
11329     }
11330 
11331     CImgDisplay& close() {
11332       if (is_empty() || _is_closed) return *this;
11333       _is_closed = true;
11334       if (_is_fullscreen) _desinit_fullscreen();
11335       ShowWindow(_window,SW_HIDE);
11336       _window_x = _window_y = cimg::type<int>::min();
11337       return *this;
11338     }
11339 
11340     CImgDisplay& move(const int posx, const int posy) {
11341       if (is_empty()) return *this;
11342       if (_window_x!=posx || _window_y!=posy) {
11343         SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
11344         _window_x = posx;
11345         _window_y = posy;
11346       }
11347       show();
11348       _is_moved = false;
11349       return *this;
11350     }
11351 
11352     CImgDisplay& show_mouse() {
11353       if (is_empty()) return *this;
11354       _is_cursor_visible = true;
11355       return *this;
11356     }
11357 
11358     CImgDisplay& hide_mouse() {
11359       if (is_empty()) return *this;
11360       _is_cursor_visible = false;
11361       return *this;
11362     }
11363 
11364     CImgDisplay& set_mouse(const int posx, const int posy) {
11365       if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
11366       if (!_is_closed) {
11367         _update_window_pos();
11368         const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
11369         if (res) { _mouse_x = posx; _mouse_y = posy; }
11370       }
11371       return *this;
11372     }
11373 
11374     CImgDisplay& set_title(const char *const format, ...) {
11375       if (is_empty()) return *this;
11376       char *const tmp = new char[1024];
11377       va_list ap;
11378       va_start(ap, format);
11379       cimg_vsnprintf(tmp,1024,format,ap);
11380       va_end(ap);
11381       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
11382       delete[] _title;
11383       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
11384       _title = new char[s];
11385       std::memcpy(_title,tmp,s*sizeof(char));
11386       SetWindowTextA(_window, tmp);
11387       delete[] tmp;
11388       return *this;
11389     }
11390 
11391     template<typename T>
11392     CImgDisplay& display(const CImg<T>& img) {
11393       if (!img)
11394         throw CImgArgumentException(_cimgdisplay_instance
11395                                     "display(): Empty specified image.",
11396                                     cimgdisplay_instance);
11397       if (is_empty()) return assign(img);
11398       return render(img).paint();
11399     }
11400 
11401     CImgDisplay& paint() {
11402       if (_is_closed) return *this;
11403       WaitForSingleObject(_mutex,INFINITE);
11404       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
11405       ReleaseMutex(_mutex);
11406       return *this;
11407     }
11408 
11409     template<typename T>
11410     CImgDisplay& render(const CImg<T>& img) {
11411       if (!img)
11412         throw CImgArgumentException(_cimgdisplay_instance
11413                                     "render(): Empty specified image.",
11414                                     cimgdisplay_instance);
11415 
11416       if (is_empty()) return *this;
11417       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
11418                                                              (img._depth - 1)/2));
11419 
11420       const T
11421         *data1 = img._data,
11422         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
11423         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
11424 
11425       WaitForSingleObject(_mutex,INFINITE);
11426       unsigned int
11427         *const ndata = (img._width==_width && img._height==_height)?_data:
11428         new unsigned int[(size_t)img._width*img._height],
11429         *ptrd = ndata;
11430 
11431       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
11432         _min = _max = 0;
11433         switch (img._spectrum) {
11434         case 1 : {
11435           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11436             const unsigned char val = (unsigned char)*(data1++);
11437             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
11438           }
11439         } break;
11440         case 2 : {
11441           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11442             const unsigned char
11443               R = (unsigned char)*(data1++),
11444               G = (unsigned char)*(data2++);
11445             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
11446           }
11447         } break;
11448         default : {
11449           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11450             const unsigned char
11451               R = (unsigned char)*(data1++),
11452               G = (unsigned char)*(data2++),
11453               B = (unsigned char)*(data3++);
11454             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
11455           }
11456         }
11457         }
11458       } else {
11459         if (_normalization==3) {
11460           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
11461           else {
11462             _min = (float)cimg::type<T>::min();
11463             _max = (float)cimg::type<T>::max();
11464           }
11465         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
11466         const float delta = _max - _min, mm = 255/(delta?delta:1.f);
11467         switch (img._spectrum) {
11468         case 1 : {
11469           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11470             const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
11471             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
11472           }
11473         } break;
11474         case 2 : {
11475           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11476             const unsigned char
11477               R = (unsigned char)((*(data1++) - _min)*mm),
11478               G = (unsigned char)((*(data2++) - _min)*mm);
11479             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
11480           }
11481         } break;
11482         default : {
11483           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11484             const unsigned char
11485               R = (unsigned char)((*(data1++) - _min)*mm),
11486               G = (unsigned char)((*(data2++) - _min)*mm),
11487               B = (unsigned char)((*(data3++) - _min)*mm);
11488             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
11489           }
11490         }
11491         }
11492       }
11493       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
11494       ReleaseMutex(_mutex);
11495       return *this;
11496     }
11497 
11498     template<typename T>
11499     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
11500       img.assign();
11501       HDC hScreen = GetDC(GetDesktopWindow());
11502       if (hScreen) {
11503         const int
11504           width = GetDeviceCaps(hScreen,HORZRES),
11505           height = GetDeviceCaps(hScreen,VERTRES);
11506         int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
11507         if (_x0>_x1) cimg::swap(_x0,_x1);
11508         if (_y0>_y1) cimg::swap(_y0,_y1);
11509         if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
11510           _x0 = std::max(_x0,0);
11511           _y0 = std::max(_y0,0);
11512           _x1 = std::min(_x1,width - 1);
11513           _y1 = std::min(_y1,height - 1);
11514           const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
11515           HDC hdcMem = CreateCompatibleDC(hScreen);
11516           if (hdcMem) {
11517             HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
11518             if (hBitmap) {
11519               HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
11520               if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
11521                 BITMAPINFOHEADER bmi;
11522                 bmi.biSize = sizeof(BITMAPINFOHEADER);
11523                 bmi.biWidth = bw;
11524                 bmi.biHeight = -bh;
11525                 bmi.biPlanes = 1;
11526                 bmi.biBitCount = 32;
11527                 bmi.biCompression = BI_RGB;
11528                 bmi.biSizeImage = 0;
11529                 bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
11530                 bmi.biClrUsed = bmi.biClrImportant = 0;
11531                 unsigned char *buf = new unsigned char[4*bw*bh];
11532                 if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
11533                   img.assign(bw,bh,1,3);
11534                   const unsigned char *ptrs = buf;
11535                   T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
11536                   cimg_forXY(img,x,y) {
11537                     *(pR++) = (T)ptrs[2];
11538                     *(pG++) = (T)ptrs[1];
11539                     *(pB++) = (T)ptrs[0];
11540                     ptrs+=4;
11541                   }
11542                 }
11543                 delete[] buf;
11544               }
11545               DeleteObject(hBitmap);
11546             }
11547             DeleteDC(hdcMem);
11548           }
11549         }
11550         ReleaseDC(GetDesktopWindow(),hScreen);
11551       }
11552       if (img.is_empty())
11553         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
11554                                    "with coordinates (%d,%d)-(%d,%d).",
11555                                    x0,y0,x1,y1);
11556     }
11557 
11558     template<typename T>
11559     const CImgDisplay& snapshot(CImg<T>& img) const {
11560       if (is_empty()) { img.assign(); return *this; }
11561       const unsigned int *ptrs = _data;
11562       img.assign(_width,_height,1,3);
11563       T
11564         *data1 = img.data(0,0,0,0),
11565         *data2 = img.data(0,0,0,1),
11566         *data3 = img.data(0,0,0,2);
11567       for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11568         const unsigned int val = *(ptrs++);
11569         *(data1++) = (T)(unsigned char)(val>>16);
11570         *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
11571         *(data3++) = (T)(unsigned char)(val&0xFF);
11572       }
11573       return *this;
11574     }
11575 #endif
11576 
11577     //@}
11578   }; // struct CImgDisplay { ...
11579 
11580   /*
11581    #--------------------------------------
11582    #
11583    #
11584    #
11585    # Definition of the CImg<T> structure
11586    #
11587    #
11588    #
11589    #--------------------------------------
11590    */
11591 
11592   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
11593   /**
11594      This is the main class of the %CImg Library. It declares and constructs
11595      an image, allows access to its pixel values, and is able to perform various image operations.
11596 
11597      \par Image representation
11598 
11599      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
11600      each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
11601      and number of channels.
11602      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
11603      while the number of channels is rather used as a vector-valued dimension
11604      (it may describe the R,G,B color channels for instance).
11605      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
11606 
11607      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
11608      as well as images with less dimensions (1D scalar signal, 2D color images, ...).
11609      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
11610 
11611      Concerning the pixel value type \c T:
11612      fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
11613      unsigned long, long, float, double, ... </tt>.
11614      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
11615      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
11616      images that have floating-point pixel values. The default value for the template T is \c float.
11617      Using your own template types may be possible. However, you will certainly have to define the complete set
11618      of arithmetic and logical operators for your class.
11619 
11620      \par Image structure
11621 
11622      The \c CImg<T> structure contains \e six fields:
11623      - \c _width defines the number of \a columns of the image (size along the X-axis).
11624      - \c _height defines the number of \a rows of the image (size along the Y-axis).
11625      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
11626      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
11627      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
11628      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
11629        another image.
11630 
11631      You can access these fields publicly although it is recommended to use the dedicated functions
11632      width(), height(), depth(), spectrum() and ptr() to do so.
11633      Image dimensions are not limited to a specific range (as long as you got enough available memory).
11634      A value of \e 1 usually means that the corresponding dimension is \a flat.
11635      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
11636      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
11637      (a CImgInstanceException will be thrown instead).
11638      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
11639 
11640      \par Image declaration and construction
11641 
11642      Declaring an image can be done by using one of the several available constructors.
11643      Here is a list of the most used:
11644 
11645      - Construct images from arbitrary dimensions:
11646          - <tt>CImg<char> img;</tt> declares an empty image.
11647          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
11648          \c unsigned \c char pixel values.
11649          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
11650          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
11651          (colors are stored as an image with three channels).
11652          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
11653          (with \c double pixel values).
11654          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
11655          (with \c float pixels, which is the default value of the template parameter \c T).
11656          - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
11657          do it, or use the specific constructor taking 5 parameters like this:
11658          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
11659 
11660      - Construct images from filenames:
11661          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
11662          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
11663          file "analyze.hdr".
11664          - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
11665          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
11666 
11667      - Construct images from C-style arrays:
11668          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
11669          \c data_buffer (of size 256x256=65536).
11670          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
11671          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
11672 
11673          The complete list of constructors can be found <a href="#constructors">here</a>.
11674 
11675      \par Most useful functions
11676 
11677      The \c CImg<T> class contains a lot of functions that operates on images.
11678      Some of the most useful are:
11679 
11680      - operator()(): Read or write pixel values.
11681      - display(): displays the image in a new window.
11682   **/
11683   template<typename T>
11684   struct CImg {
11685 
11686     unsigned int _width, _height, _depth, _spectrum;
11687     bool _is_shared;
11688     T *_data;
11689 
11690     //! Simple iterator type, to loop through each pixel value of an image instance.
11691     /**
11692        \note
11693        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
11694        - You will seldom have to use iterators in %CImg, most classical operations
11695          being achieved (often in a faster way) using methods of \c CImg<T>.
11696        \par Example
11697        \code
11698        CImg<float> img("reference.jpg");                                         // Load image from file
11699        // Set all pixels to '0', with a CImg iterator.
11700        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
11701        img.fill(0);                                                              // Do the same with a built-in method
11702        \endcode
11703    **/
11704     typedef T* iterator;
11705 
11706     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
11707     /**
11708        \note
11709        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
11710        - You will seldom have to use iterators in %CImg, most classical operations
11711          being achieved (often in a faster way) using methods of \c CImg<T>.
11712        \par Example
11713        \code
11714        const CImg<float> img("reference.jpg");                                    // Load image from file
11715        float sum = 0;
11716        // Compute sum of all pixel values, with a CImg iterator.
11717        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
11718        const float sum2 = img.sum();                                              // Do the same with a built-in method
11719        \endcode
11720     **/
11721     typedef const T* const_iterator;
11722 
11723     //! Pixel value type.
11724     /**
11725        Refer to the type of the pixel values of an image instance.
11726        \note
11727        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
11728        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
11729          compatibility with STL naming conventions.
11730     **/
11731     typedef T value_type;
11732 
11733     // Define common types related to template type T.
11734     typedef typename cimg::superset<T,bool>::type Tbool;
11735     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
11736     typedef typename cimg::superset<T,char>::type Tchar;
11737     typedef typename cimg::superset<T,unsigned short>::type Tushort;
11738     typedef typename cimg::superset<T,short>::type Tshort;
11739     typedef typename cimg::superset<T,unsigned int>::type Tuint;
11740     typedef typename cimg::superset<T,int>::type Tint;
11741     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
11742     typedef typename cimg::superset<T,cimg_long>::type Tlong;
11743     typedef typename cimg::superset<T,float>::type Tfloat;
11744     typedef typename cimg::superset<T,double>::type Tdouble;
11745     typedef typename cimg::last<T,bool>::type boolT;
11746     typedef typename cimg::last<T,unsigned char>::type ucharT;
11747     typedef typename cimg::last<T,char>::type charT;
11748     typedef typename cimg::last<T,unsigned short>::type ushortT;
11749     typedef typename cimg::last<T,short>::type shortT;
11750     typedef typename cimg::last<T,unsigned int>::type uintT;
11751     typedef typename cimg::last<T,int>::type intT;
11752     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
11753     typedef typename cimg::last<T,cimg_long>::type longT;
11754     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
11755     typedef typename cimg::last<T,cimg_int64>::type int64T;
11756     typedef typename cimg::last<T,float>::type floatT;
11757     typedef typename cimg::last<T,double>::type doubleT;
11758 
11759     // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs.
11760     static size_t safe_size(const unsigned int dx, const unsigned int dy,
11761                             const unsigned int dz, const unsigned int dc) {
11762       if (!(dx && dy && dz && dc)) return 0;
11763       size_t siz = (size_t)dx, osiz = siz;
11764       if ((dy==1 || (siz*=dy)>osiz) &&
11765           ((osiz = siz), dz==1 || (siz*=dz)>osiz) &&
11766           ((osiz = siz), dc==1 || (siz*=dc)>osiz) &&
11767           ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz;
11768       throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.",
11769                                   pixel_type(),dx,dy,dz,dc);
11770     }
11771 
11772     //@}
11773     //---------------------------
11774     //
11775     //! \name Plugins
11776     //@{
11777     //---------------------------
11778 #ifdef cimg_plugin
11779 #include cimg_plugin
11780 #endif
11781 #ifdef cimg_plugin1
11782 #include cimg_plugin1
11783 #endif
11784 #ifdef cimg_plugin2
11785 #include cimg_plugin2
11786 #endif
11787 #ifdef cimg_plugin3
11788 #include cimg_plugin3
11789 #endif
11790 #ifdef cimg_plugin4
11791 #include cimg_plugin4
11792 #endif
11793 #ifdef cimg_plugin5
11794 #include cimg_plugin5
11795 #endif
11796 #ifdef cimg_plugin6
11797 #include cimg_plugin6
11798 #endif
11799 #ifdef cimg_plugin7
11800 #include cimg_plugin7
11801 #endif
11802 #ifdef cimg_plugin8
11803 #include cimg_plugin8
11804 #endif
11805 
11806     //@}
11807     //---------------------------------------------------------
11808     //
11809     //! \name Constructors / Destructor / Instance Management
11810     //@{
11811     //---------------------------------------------------------
11812 
11813     //! Destroy image.
11814     /**
11815        \note
11816        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
11817        - Destroying an empty or shared image does nothing actually.
11818        \warning
11819        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
11820          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
11821          (to a deallocated buffer).
11822     **/
11823     ~CImg() {
11824       if (!_is_shared) delete[] _data;
11825     }
11826 
11827     //! Construct empty image.
11828     /**
11829        \note
11830        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
11831          are set to \c 0, as well as its pixel buffer pointer data().
11832        - An empty image may be re-assigned afterwards, e.g. with the family of
11833          assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
11834          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
11835        - An empty image is never shared.
11836        \par Example
11837        \code
11838        CImg<float> img1, img2;      // Construct two empty images
11839        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image
11840        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'
11841        img2.assign();               // Re-assign 'img2' to be an empty image again
11842        \endcode
11843     **/
11844     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
11845 
11846     //! Construct image with specified size.
11847     /**
11848        \param size_x Image width().
11849        \param size_y Image height().
11850        \param size_z Image depth().
11851        \param size_c Image spectrum() (number of channels).
11852        \note
11853        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
11854          for each constructed image instance.
11855        - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
11856          an \e empty image.
11857        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11858          (e.g. when requested size is too big for available memory).
11859        \warning
11860        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
11861          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
11862          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
11863        \par Example
11864        \code
11865        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values
11866        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'
11867        \endcode
11868     **/
11869     explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
11870                   const unsigned int size_z=1, const unsigned int size_c=1):
11871       _is_shared(false) {
11872       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
11873       if (siz) {
11874         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11875         try { _data = new T[siz]; } catch (...) {
11876           _width = _height = _depth = _spectrum = 0; _data = 0;
11877           throw CImgInstanceException(_cimg_instance
11878                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11879                                       cimg_instance,
11880                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11881                                       size_x,size_y,size_z,size_c);
11882         }
11883       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11884     }
11885 
11886     //! Construct image with specified size and initialize pixel values.
11887     /**
11888        \param size_x Image width().
11889        \param size_y Image height().
11890        \param size_z Image depth().
11891        \param size_c Image spectrum() (number of channels).
11892        \param value Initialization value.
11893        \note
11894        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
11895          but it also fills the pixel buffer with the specified \c value.
11896        \warning
11897        - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
11898          (e.g. RGB vector, for color images).
11899          For this task, you may use fillC() after construction.
11900     **/
11901     CImg(const unsigned int size_x, const unsigned int size_y,
11902          const unsigned int size_z, const unsigned int size_c, const T& value):
11903       _is_shared(false) {
11904       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
11905       if (siz) {
11906         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11907         try { _data = new T[siz]; } catch (...) {
11908           _width = _height = _depth = _spectrum = 0; _data = 0;
11909           throw CImgInstanceException(_cimg_instance
11910                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11911                                       cimg_instance,
11912                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11913                                       size_x,size_y,size_z,size_c);
11914         }
11915         fill(value);
11916       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11917     }
11918 
11919     //! Construct image with specified size and initialize pixel values from a sequence of integers.
11920     /**
11921        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
11922        with pixels of type \c T, and initialize pixel
11923        values from the specified sequence of integers \c value0,\c value1,\c ...
11924        \param size_x Image width().
11925        \param size_y Image height().
11926        \param size_z Image depth().
11927        \param size_c Image spectrum() (number of channels).
11928        \param value0 First value of the initialization sequence (must be an \e integer).
11929        \param value1 Second value of the initialization sequence (must be an \e integer).
11930        \param ...
11931        \note
11932        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11933          the pixel buffer with a sequence of specified integer values.
11934        \warning
11935        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
11936          Otherwise, the constructor may crash or fill your image pixels with garbage.
11937        \par Example
11938        \code
11939        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image
11940                              0,255,0,255,  // Set the 4 values for the red component
11941                              0,0,255,255,  // Set the 4 values for the green component
11942                              64,64,64,64); // Set the 4 values for the blue component
11943        img.resize(150,150).display();
11944        \endcode
11945        \image html ref_constructor1.jpg
11946      **/
11947     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11948          const int value0, const int value1, ...):
11949       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11950 #define _CImg_stdarg(img,a0,a1,N,t) { \
11951         size_t _siz = (size_t)N; \
11952         if (_siz--) { \
11953           va_list ap; \
11954           va_start(ap,a1); \
11955           T *ptrd = (img)._data; \
11956           *(ptrd++) = (T)a0; \
11957           if (_siz--) { \
11958             *(ptrd++) = (T)a1; \
11959             for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
11960           } \
11961           va_end(ap); \
11962         } \
11963       }
11964       assign(size_x,size_y,size_z,size_c);
11965       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
11966     }
11967 
11968 #if cimg_use_cpp11==1
11969     //! Construct image with specified size and initialize pixel values from an initializer list of integers.
11970     /**
11971        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
11972        with pixels of type \c T, and initialize pixel
11973        values from the specified initializer list of integers { \c value0,\c value1,\c ... }
11974        \param size_x Image width().
11975        \param size_y Image height().
11976        \param size_z Image depth().
11977        \param size_c Image spectrum() (number of channels).
11978        \param { value0, value1, ... } Initialization list
11979        \param repeat_values Tells if the value filling process is repeated over the image.
11980 
11981        \note
11982        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11983          the pixel buffer with a sequence of specified integer values.
11984        \par Example
11985        \code
11986        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image
11987                              { 0,255,0,255,    // Set the 4 values for the red component
11988                                0,0,255,255,    // Set the 4 values for the green component
11989                                64,64,64,64 }); // Set the 4 values for the blue component
11990        img.resize(150,150).display();
11991        \endcode
11992        \image html ref_constructor1.jpg
11993     **/
11994     template<typename t>
11995     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11996          const std::initializer_list<t> values,
11997          const bool repeat_values=true):
11998       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11999 #define _cimg_constructor_cpp11(repeat_values) \
12000   auto it = values.begin(); \
12001   size_t siz = size(); \
12002   if (repeat_values) for (T *ptrd = _data; siz--; ) { \
12003     *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
12004   else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
12005       assign(size_x,size_y,size_z,size_c);
12006       _cimg_constructor_cpp11(repeat_values);
12007     }
12008 
12009     template<typename t>
12010     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
12011          std::initializer_list<t> values,
12012          const bool repeat_values=true):
12013       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12014       assign(size_x,size_y,size_z);
12015       _cimg_constructor_cpp11(repeat_values);
12016     }
12017 
12018     template<typename t>
12019     CImg(const unsigned int size_x, const unsigned int size_y,
12020          std::initializer_list<t> values,
12021          const bool repeat_values=true):
12022       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12023       assign(size_x,size_y);
12024       _cimg_constructor_cpp11(repeat_values);
12025     }
12026 
12027     template<typename t>
12028     CImg(const unsigned int size_x,
12029          std::initializer_list<t> values,
12030          const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12031       assign(size_x);
12032       _cimg_constructor_cpp11(repeat_values);
12033     }
12034 
12035     //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
12036     /**
12037        Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
12038        with pixels of type \c T, and initialize pixel
12039        values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
12040        given by the size of the initializer list.
12041        \param { value0, value1, ... } Initialization list
12042        \note
12043        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
12044          but it also fills the pixel buffer with a sequence of specified integer values.
12045        \par Example
12046        \code
12047        const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values
12048        img.resize(150,150).display();
12049        \endcode
12050        \image html ref_constructor1.jpg
12051      **/
12052     template<typename t>
12053     CImg(const std::initializer_list<t> values):
12054       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12055       assign(values.size(),1,1,1);
12056       auto it = values.begin();
12057       unsigned int siz = _width;
12058       for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
12059     }
12060 
12061     template<typename t>
12062     CImg<T>& operator=(std::initializer_list<t> values) {
12063       _cimg_constructor_cpp11(siz>values.size());
12064       return *this;
12065     }
12066 #endif
12067 
12068     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
12069     /**
12070        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,
12071        and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
12072        \param size_x Image width().
12073        \param size_y Image height().
12074        \param size_z Image depth().
12075        \param size_c Image spectrum() (number of channels).
12076        \param value0 First value of the initialization sequence (must be a \e double).
12077        \param value1 Second value of the initialization sequence (must be a \e double).
12078        \param ...
12079        \note
12080        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
12081          takes a sequence of double values instead of integers.
12082        \warning
12083        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
12084          Otherwise, the constructor may crash or fill your image with garbage.
12085          For instance, the code below will probably crash on most platforms:
12086          \code
12087          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
12088          \endcode
12089      **/
12090     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
12091          const double value0, const double value1, ...):
12092       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12093       assign(size_x,size_y,size_z,size_c);
12094       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
12095     }
12096 
12097     //! Construct image with specified size and initialize pixel values from a value string.
12098     /**
12099        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,
12100        and initializes pixel values from the specified string \c values.
12101        \param size_x Image width().
12102        \param size_y Image height().
12103        \param size_z Image depth().
12104        \param size_c Image spectrum() (number of channels).
12105        \param values Value string describing the way pixel values are set.
12106        \param repeat_values Tells if the value filling process is repeated over the image.
12107        \note
12108        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
12109          the pixel buffer with values described in the value string \c values.
12110        - Value string \c values may describe two different filling processes:
12111          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
12112            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
12113          - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
12114            In this case, parameter \c repeat_values is pointless.
12115        - For both cases, specifying \c repeat_values is mandatory.
12116          It disambiguates the possible overloading of constructor
12117          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
12118        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
12119        \par Example
12120        \code
12121        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence
12122                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula
12123        (img1,img2).display();
12124        \endcode
12125        \image html ref_constructor2.jpg
12126      **/
12127     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
12128          const char *const values, const bool repeat_values):_is_shared(false) {
12129       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12130       if (siz) {
12131         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12132         try { _data = new T[siz]; } catch (...) {
12133           _width = _height = _depth = _spectrum = 0; _data = 0;
12134           throw CImgInstanceException(_cimg_instance
12135                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12136                                       cimg_instance,
12137                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12138                                       size_x,size_y,size_z,size_c);
12139         }
12140         fill(values,repeat_values);
12141       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12142     }
12143 
12144     //! Construct image with specified size and initialize pixel values from a memory buffer.
12145     /**
12146        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,
12147        and initializes pixel values from the specified \c t* memory buffer.
12148        \param values Pointer to the input memory buffer.
12149        \param size_x Image width().
12150        \param size_y Image height().
12151        \param size_z Image depth().
12152        \param size_c Image spectrum() (number of channels).
12153        \param is_shared Tells if input memory buffer must be shared by the current instance.
12154        \note
12155        - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
12156          and values from the specified input buffer are copied to the instance buffer.
12157          If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
12158        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
12159          own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
12160          image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
12161        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
12162          (e.g. when requested size is too big for available memory).
12163        \warning
12164        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
12165          (e.g. already deallocated).
12166        \par Example
12167        \code
12168        unsigned char tab[256*256] = { 0 };
12169        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'
12170                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'
12171        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'
12172        \endcode
12173     **/
12174     template<typename t>
12175     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
12176          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
12177       if (is_shared) {
12178         _width = _height = _depth = _spectrum = 0; _data = 0;
12179         throw CImgArgumentException(_cimg_instance
12180                                     "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
12181                                     "from a (%s*) buffer (pixel types are different).",
12182                                     cimg_instance,
12183                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
12184       }
12185       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12186       if (values && siz) {
12187         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12188         try { _data = new T[siz]; } catch (...) {
12189           _width = _height = _depth = _spectrum = 0; _data = 0;
12190           throw CImgInstanceException(_cimg_instance
12191                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12192                                       cimg_instance,
12193                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12194                                       size_x,size_y,size_z,size_c);
12195 
12196         }
12197         const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12198       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12199     }
12200 
12201     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
12202     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
12203          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
12204       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12205       if (values && siz) {
12206         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
12207         if (_is_shared) _data = const_cast<T*>(values);
12208         else {
12209           try { _data = new T[siz]; } catch (...) {
12210             _width = _height = _depth = _spectrum = 0; _data = 0;
12211             throw CImgInstanceException(_cimg_instance
12212                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12213                                         cimg_instance,
12214                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12215                                         size_x,size_y,size_z,size_c);
12216           }
12217           std::memcpy(_data,values,siz*sizeof(T));
12218         }
12219       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12220     }
12221 
12222     //! Construct image from memory buffer with specified size and pixel ordering scheme.
12223     template<typename t>
12224     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y,
12225          const unsigned int size_z, const unsigned int size_c,
12226          const char *const axes_order):_data(0),_is_shared(false) {
12227       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12228       if (values && siz) {
12229         unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
12230         for (unsigned int l = 0; axes_order[l]; ++l) {
12231           int c = cimg::lowercase(axes_order[l]);
12232           if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
12233           else { ++n_code[c%=4]; s_code[l] = c; }
12234         }
12235         if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
12236           const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
12237           int s0 = 0, s1 = 0, s2 = 0, s3 = 0;
12238           const char *inv_order = 0;
12239           switch (code) {
12240             case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc
12241             case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz
12242             case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc
12243             case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy
12244             case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz
12245             case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy
12246             case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc
12247             case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz
12248             case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc
12249             case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx
12250             case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz
12251             case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx
12252             case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc
12253             case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy
12254             case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc
12255             case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx
12256             case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy
12257             case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx
12258             case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz
12259             case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy
12260             case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz
12261             case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx
12262             case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy
12263             case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx
12264           }
12265           CImg<t>(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this);
12266         } else {
12267           _width = _height = _depth = _spectrum = 0; _data = 0;
12268           throw CImgArgumentException(_cimg_instance
12269                                       "CImg(): Invalid specified axes order '%s'.",
12270                                       cimg_instance,
12271                                       axes_order);
12272         }
12273       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12274     }
12275 
12276     //! Construct image from reading an image file.
12277     /**
12278        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
12279        an image file.
12280        \param filename Filename, as a C-string.
12281        \note
12282        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
12283          dimensions and pixel values from the specified image file.
12284        - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system
12285          and on the external libraries you used to link your code against.
12286        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
12287          file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
12288        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
12289          recognized.
12290        \par Example
12291        \code
12292        const CImg<float> img("reference.jpg");
12293        img.display();
12294        \endcode
12295        \image html ref_image.jpg
12296     **/
12297     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12298       assign(filename);
12299     }
12300 
12301     //! Construct image copy.
12302     /**
12303        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
12304        \param img Input image to copy.
12305        \note
12306        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
12307          input image \c img.
12308        - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
12309          \e shared, and shares its pixel buffer with \c img.
12310          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
12311          This behavior is needful to allow functions to return shared images.
12312        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
12313          image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
12314          \c t are different.
12315        - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
12316          with different types.
12317        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
12318          (e.g. not enough available memory).
12319     **/
12320     template<typename t>
12321     CImg(const CImg<t>& img):_is_shared(false) {
12322       const size_t siz = (size_t)img.size();
12323       if (img._data && siz) {
12324         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12325         try { _data = new T[siz]; } catch (...) {
12326           _width = _height = _depth = _spectrum = 0; _data = 0;
12327           throw CImgInstanceException(_cimg_instance
12328                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12329                                       cimg_instance,
12330                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12331                                       img._width,img._height,img._depth,img._spectrum);
12332         }
12333         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12334       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12335     }
12336 
12337     //! Construct image copy \specialization.
12338     CImg(const CImg<T>& img) {
12339       const size_t siz = (size_t)img.size();
12340       if (img._data && siz) {
12341         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12342         _is_shared = img._is_shared;
12343         if (_is_shared) _data = const_cast<T*>(img._data);
12344         else {
12345           try { _data = new T[siz]; } catch (...) {
12346             _width = _height = _depth = _spectrum = 0; _data = 0;
12347             throw CImgInstanceException(_cimg_instance
12348                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12349                                         cimg_instance,
12350                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12351                                         img._width,img._height,img._depth,img._spectrum);
12352 
12353           }
12354           std::memcpy(_data,img._data,siz*sizeof(T));
12355         }
12356       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12357     }
12358 
12359     //! Advanced copy constructor.
12360     /**
12361        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
12362        while forcing the shared state of the constructed copy.
12363        \param img Input image to copy.
12364        \param is_shared Tells about the shared state of the constructed copy.
12365        \note
12366        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
12367          the constructed image, which does not depend anymore on the shared state of the input image \c img:
12368          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
12369            For that case, the pixel types \c T and \c t \e must be the same.
12370          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
12371            image \c img is shared or not.
12372        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
12373     **/
12374     template<typename t>
12375     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
12376       if (is_shared) {
12377         _width = _height = _depth = _spectrum = 0; _data = 0;
12378         throw CImgArgumentException(_cimg_instance
12379                                     "CImg(): Invalid construction request of a shared instance from a "
12380                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
12381                                     cimg_instance,
12382                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
12383       }
12384       const size_t siz = (size_t)img.size();
12385       if (img._data && siz) {
12386         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12387         try { _data = new T[siz]; } catch (...) {
12388           _width = _height = _depth = _spectrum = 0; _data = 0;
12389           throw CImgInstanceException(_cimg_instance
12390                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12391                                       cimg_instance,
12392                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12393                                       img._width,img._height,img._depth,img._spectrum);
12394         }
12395         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12396       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12397     }
12398 
12399     //! Advanced copy constructor \specialization.
12400     CImg(const CImg<T>& img, const bool is_shared) {
12401       const size_t siz = (size_t)img.size();
12402       if (img._data && siz) {
12403         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12404         _is_shared = is_shared;
12405         if (_is_shared) _data = const_cast<T*>(img._data);
12406         else {
12407           try { _data = new T[siz]; } catch (...) {
12408             _width = _height = _depth = _spectrum = 0; _data = 0;
12409             throw CImgInstanceException(_cimg_instance
12410                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12411                                         cimg_instance,
12412                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12413                                         img._width,img._height,img._depth,img._spectrum);
12414           }
12415           std::memcpy(_data,img._data,siz*sizeof(T));
12416         }
12417       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12418     }
12419 
12420     //! Construct image with dimensions borrowed from another image.
12421     /**
12422        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
12423        \c CImg<t> instance.
12424        \param img Input image from which dimensions are borrowed.
12425        \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
12426        \note
12427        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
12428          (\e not its pixel values) from an existing \c CImg<t> instance.
12429        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
12430          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
12431          instead.
12432        \par Example
12433        \code
12434        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image
12435                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image
12436                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image
12437                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0')
12438        \endcode
12439      **/
12440     template<typename t>
12441     CImg(const CImg<t>& img, const char *const dimensions):
12442       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12443       assign(img,dimensions);
12444     }
12445 
12446     //! Construct image with dimensions borrowed from another image and initialize pixel values.
12447     /**
12448        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
12449        \c CImg<t> instance, and set all pixel values to specified \c value.
12450        \param img Input image from which dimensions are borrowed.
12451        \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
12452        \param value Value used for initialization.
12453        \note
12454        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
12455      **/
12456     template<typename t>
12457     CImg(const CImg<t>& img, const char *const dimensions, const T& value):
12458       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12459       assign(img,dimensions).fill(value);
12460     }
12461 
12462     //! Construct image from a display window.
12463     /**
12464        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
12465        \param disp Input display window.
12466        \note
12467        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
12468        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
12469          (i.e. a 2D color image).
12470        - The image pixels are read as 8-bits RGB values.
12471      **/
12472     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12473       disp.snapshot(*this);
12474     }
12475 
12476     // Constructor and assignment operator for rvalue references (c++11).
12477     // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
12478 #if cimg_use_cpp11==1
12479     CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12480       swap(img);
12481     }
12482 
12483     CImg<T>& operator=(CImg<T>&& img) {
12484       if (_is_shared) return assign(img);
12485       return img.swap(*this);
12486     }
12487 #endif
12488 
12489     //! Construct empty image \inplace.
12490     /**
12491        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
12492     **/
12493     CImg<T>& assign() {
12494       if (!_is_shared) delete[] _data;
12495       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
12496       return *this;
12497     }
12498 
12499     //! Construct image with specified size \inplace.
12500     /**
12501        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
12502     **/
12503     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
12504                     const unsigned int size_z=1, const unsigned int size_c=1) {
12505       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12506       if (!siz) return assign();
12507       const size_t curr_siz = (size_t)size();
12508       if (siz!=curr_siz) {
12509         if (_is_shared)
12510           throw CImgArgumentException(_cimg_instance
12511                                       "assign(): Invalid assignment request of shared instance from specified "
12512                                       "image (%u,%u,%u,%u).",
12513                                       cimg_instance,
12514                                       size_x,size_y,size_z,size_c);
12515         else {
12516           delete[] _data;
12517           try { _data = new T[siz]; } catch (...) {
12518             _width = _height = _depth = _spectrum = 0; _data = 0;
12519             throw CImgInstanceException(_cimg_instance
12520                                         "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12521                                         cimg_instance,
12522                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12523                                         size_x,size_y,size_z,size_c);
12524           }
12525         }
12526       }
12527       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12528       return *this;
12529     }
12530 
12531     //! Construct image with specified size and initialize pixel values \inplace.
12532     /**
12533        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
12534     **/
12535     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12536                     const unsigned int size_z, const unsigned int size_c, const T& value) {
12537       return assign(size_x,size_y,size_z,size_c).fill(value);
12538     }
12539 
12540     //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
12541     /**
12542        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
12543     **/
12544     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12545                     const unsigned int size_z, const unsigned int size_c,
12546                     const int value0, const int value1, ...) {
12547       assign(size_x,size_y,size_z,size_c);
12548       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
12549       return *this;
12550     }
12551 
12552     //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
12553     /**
12554        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
12555     **/
12556     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12557                     const unsigned int size_z, const unsigned int size_c,
12558                     const double value0, const double value1, ...) {
12559       assign(size_x,size_y,size_z,size_c);
12560       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
12561       return *this;
12562     }
12563 
12564     //! Construct image with specified size and initialize pixel values from a value string \inplace.
12565     /**
12566        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
12567     **/
12568     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12569                     const unsigned int size_z, const unsigned int size_c,
12570                     const char *const values, const bool repeat_values) {
12571       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
12572     }
12573 
12574     //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
12575     /**
12576        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
12577     **/
12578     template<typename t>
12579     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
12580                     const unsigned int size_z=1, const unsigned int size_c=1) {
12581       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12582       if (!values || !siz) return assign();
12583       assign(size_x,size_y,size_z,size_c);
12584       const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12585       return *this;
12586     }
12587 
12588     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
12589     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
12590                     const unsigned int size_z=1, const unsigned int size_c=1) {
12591       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12592       if (!values || !siz) return assign();
12593       const size_t curr_siz = (size_t)size();
12594       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
12595       if (_is_shared || values + siz<_data || values>=_data + size()) {
12596         assign(size_x,size_y,size_z,size_c);
12597         if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
12598         else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
12599       } else {
12600         T *new_data = 0;
12601         try { new_data = new T[siz]; } catch (...) {
12602           _width = _height = _depth = _spectrum = 0; _data = 0;
12603           throw CImgInstanceException(_cimg_instance
12604                                       "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12605                                       cimg_instance,
12606                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12607                                       size_x,size_y,size_z,size_c);
12608         }
12609         std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
12610         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12611       }
12612       return *this;
12613     }
12614 
12615     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
12616     template<typename t>
12617     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
12618                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
12619       if (is_shared)
12620         throw CImgArgumentException(_cimg_instance
12621                                     "assign(): Invalid assignment request of shared instance from (%s*) buffer"
12622                                     "(pixel types are different).",
12623                                     cimg_instance,
12624                                     CImg<t>::pixel_type());
12625       return assign(values,size_x,size_y,size_z,size_c);
12626     }
12627 
12628     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
12629     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
12630                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
12631       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12632       if (!values || !siz) return assign();
12633       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
12634       else {
12635         if (!_is_shared) {
12636           if (values + siz<_data || values>=_data + size()) assign();
12637           else cimg::warn(_cimg_instance
12638                           "assign(): Shared image instance has overlapping memory.",
12639                           cimg_instance);
12640         }
12641         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
12642         _data = const_cast<T*>(values);
12643       }
12644       return *this;
12645     }
12646 
12647     //! Construct image from memory buffer with specified size and pixel ordering scheme.
12648     template<typename t>
12649     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
12650                     const unsigned int size_z, const unsigned int size_c,
12651                     const char *const axes_order) {
12652       CImg<T>(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this);
12653     }
12654 
12655     //! Construct image from reading an image file \inplace.
12656     /**
12657        In-place version of the constructor CImg(const char*).
12658     **/
12659     CImg<T>& assign(const char *const filename) {
12660       return load(filename);
12661     }
12662 
12663     //! Construct image copy \inplace.
12664     /**
12665        In-place version of the constructor CImg(const CImg<t>&).
12666     **/
12667     template<typename t>
12668     CImg<T>& assign(const CImg<t>& img) {
12669       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
12670     }
12671 
12672     //! In-place version of the advanced copy constructor.
12673     /**
12674        In-place version of the constructor CImg(const CImg<t>&,bool).
12675      **/
12676     template<typename t>
12677     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
12678       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
12679     }
12680 
12681     //! Construct image with dimensions borrowed from another image \inplace.
12682     /**
12683        In-place version of the constructor CImg(const CImg<t>&,const char*).
12684     **/
12685     template<typename t>
12686     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
12687       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
12688       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
12689       CImg<charT> item(256);
12690       for (const char *s = dimensions; *s && k<4; ++k) {
12691         if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
12692         if (*s) {
12693           unsigned int val = 0; char sep = 0;
12694           if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
12695             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
12696             else siz[k] = val;
12697             while (*s>='0' && *s<='9') ++s;
12698             if (sep=='%') ++s;
12699           } else switch (cimg::lowercase(*s)) {
12700           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
12701           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
12702           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
12703           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
12704           default :
12705             throw CImgArgumentException(_cimg_instance
12706                                         "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
12707                                         cimg_instance,
12708                                         *s,dimensions);
12709           }
12710         }
12711       }
12712       return assign(siz[0],siz[1],siz[2],siz[3]);
12713     }
12714 
12715     //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
12716     /**
12717        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
12718     **/
12719     template<typename t>
12720     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
12721       return assign(img,dimensions).fill(value);
12722     }
12723 
12724     //! Construct image from a display window \inplace.
12725     /**
12726        In-place version of the constructor CImg(const CImgDisplay&).
12727     **/
12728     CImg<T>& assign(const CImgDisplay &disp) {
12729       disp.snapshot(*this);
12730       return *this;
12731     }
12732 
12733     //! Construct empty image \inplace.
12734     /**
12735        Equivalent to assign().
12736        \note
12737        - It has been defined for compatibility with STL naming conventions.
12738     **/
12739     CImg<T>& clear() {
12740       return assign();
12741     }
12742 
12743     //! Transfer content of an image instance into another one.
12744     /**
12745        Transfer the dimensions and the pixel buffer content of an image instance into another one,
12746        and replace instance by an empty image. It avoids the copy of the pixel buffer
12747        when possible.
12748        \param img Destination image.
12749        \note
12750        - Pixel types \c T and \c t of source and destination images can be different, though the process is
12751          designed to be instantaneous when \c T and \c t are the same.
12752        \par Example
12753        \code
12754        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'
12755                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image
12756        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image
12757        \endcode
12758     **/
12759     template<typename t>
12760     CImg<t>& move_to(CImg<t>& img) {
12761       img.assign(*this);
12762       assign();
12763       return img;
12764     }
12765 
12766     //! Transfer content of an image instance into another one \specialization.
12767     CImg<T>& move_to(CImg<T>& img) {
12768       if (_is_shared || img._is_shared) img.assign(*this);
12769       else swap(img);
12770       assign();
12771       return img;
12772     }
12773 
12774     //! Transfer content of an image instance into a new image in an image list.
12775     /**
12776        Transfer the dimensions and the pixel buffer content of an image instance
12777        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
12778        \param list Destination list.
12779        \param pos Position of the newly inserted image in the list.
12780        \note
12781        - When optional parameter \c pos is omitted, the image instance is transferred as a new
12782          image at the end of the specified \c list.
12783        - It is convenient to sequentially insert new images into image lists, with no
12784          additional copies of memory buffer.
12785        \par Example
12786        \code
12787        CImgList<float> list;             // Construct an empty image list
12788        CImg<float> img("reference.jpg"); // Read image from filename
12789        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy)
12790        \endcode
12791     **/
12792     template<typename t>
12793     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
12794       const unsigned int npos = pos>list._width?list._width:pos;
12795       move_to(list.insert(1,npos)[npos]);
12796       return list;
12797     }
12798 
12799     //! Swap fields of two image instances.
12800     /**
12801       \param img Image to swap fields with.
12802       \note
12803       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
12804         with algorithms requiring two swapping buffers.
12805       \par Example
12806       \code
12807       CImg<float> img1("lena.jpg"),
12808                   img2("milla.jpg");
12809       img1.swap(img2);    // Now, 'img1' is 'milla' and 'img2' is 'lena'
12810       \endcode
12811     **/
12812     CImg<T>& swap(CImg<T>& img) {
12813       cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
12814       cimg::swap(_data,img._data);
12815       cimg::swap(_is_shared,img._is_shared);
12816       return img;
12817     }
12818 
12819     //! Return a reference to an empty image.
12820     /**
12821        \note
12822        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
12823        e.g.
12824        \code
12825        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
12826        \endcode
12827      **/
12828     static CImg<T>& empty() {
12829       static CImg<T> _empty;
12830       return _empty.assign();
12831     }
12832 
12833     //! Return a reference to an empty image \const.
12834     static const CImg<T>& const_empty() {
12835       static const CImg<T> _empty;
12836       return _empty;
12837     }
12838 
12839     //@}
12840     //------------------------------------------
12841     //
12842     //! \name Overloaded Operators
12843     //@{
12844     //------------------------------------------
12845 
12846     //! Access to a pixel value.
12847     /**
12848        Return a reference to a located pixel value of the image instance,
12849        being possibly \e const, whether the image instance is \e const or not.
12850        This is the standard method to get/set pixel values in \c CImg<T> images.
12851        \param x X-coordinate of the pixel value.
12852        \param y Y-coordinate of the pixel value.
12853        \param z Z-coordinate of the pixel value.
12854        \param c C-coordinate of the pixel value.
12855        \note
12856        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
12857          <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
12858        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
12859          corresponding dimension is equal to \c 1.
12860          For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
12861          <tt>img(x,y,0,c)</tt>.
12862        \warning
12863        - There is \e no boundary checking done in this operator, to make it as fast as possible.
12864          You \e must take care of out-of-bounds access by yourself, if necessary.
12865          For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
12866          checking operations in this operator. In that case, warning messages will be printed on the error output
12867          when accessing out-of-bounds pixels.
12868        \par Example
12869        \code
12870        CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'
12871        const float
12872           valR = img(10,10,0,0), // Read red value at coordinates (10,10)
12873           valG = img(10,10,0,1), // Read green value at coordinates (10,10)
12874           valB = img(10,10,2),   // Read blue value at coordinates (10,10) (Z-coordinate can be omitted)
12875           avg = (valR + valG + valB)/3; // Compute average pixel value
12876        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value
12877        \endcode
12878     **/
12879 #if cimg_verbosity>=3
12880     T& operator()(const unsigned int x, const unsigned int y=0,
12881                   const unsigned int z=0, const unsigned int c=0) {
12882       const ulongT off = (ulongT)offset(x,y,z,c);
12883       if (!_data || off>=size()) {
12884         cimg::warn(_cimg_instance
12885                    "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
12886                    cimg_instance,
12887                    (int)x,(int)y,(int)z,(int)c,off);
12888         return *_data;
12889       }
12890       else return _data[off];
12891     }
12892 
12893     //! Access to a pixel value \const.
12894     const T& operator()(const unsigned int x, const unsigned int y=0,
12895                         const unsigned int z=0, const unsigned int c=0) const {
12896       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
12897     }
12898 
12899     //! Access to a pixel value.
12900     /**
12901        \param x X-coordinate of the pixel value.
12902        \param y Y-coordinate of the pixel value.
12903        \param z Z-coordinate of the pixel value.
12904        \param c C-coordinate of the pixel value.
12905        \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
12906        \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
12907        \note
12908        - Similar to (but faster than) operator()().
12909          It uses precomputed offsets to optimize memory access. You may use it to optimize
12910          the reading/writing of several pixel values in the same image (e.g. in a loop).
12911      **/
12912     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12913                   const ulongT wh, const ulongT whd=0) {
12914       cimg::unused(wh,whd);
12915       return (*this)(x,y,z,c);
12916     }
12917 
12918     //! Access to a pixel value \const.
12919     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12920                         const ulongT wh, const ulongT whd=0) const {
12921       cimg::unused(wh,whd);
12922       return (*this)(x,y,z,c);
12923     }
12924 #else
12925     T& operator()(const unsigned int x) {
12926       return _data[x];
12927     }
12928 
12929     const T& operator()(const unsigned int x) const {
12930       return _data[x];
12931     }
12932 
12933     T& operator()(const unsigned int x, const unsigned int y) {
12934       return _data[x + y*_width];
12935     }
12936 
12937     const T& operator()(const unsigned int x, const unsigned int y) const {
12938       return _data[x + y*_width];
12939     }
12940 
12941     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
12942       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
12943    }
12944 
12945     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
12946       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
12947     }
12948 
12949     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
12950       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
12951     }
12952 
12953     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
12954       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
12955     }
12956 
12957     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
12958                   const ulongT wh) {
12959       return _data[x + y*_width + z*wh];
12960     }
12961 
12962     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
12963                         const ulongT wh) const {
12964       return _data[x + y*_width + z*wh];
12965     }
12966 
12967     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12968                   const ulongT wh, const ulongT whd) {
12969       return _data[x + y*_width + z*wh + c*whd];
12970     }
12971 
12972     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12973                         const ulongT wh, const ulongT whd) const {
12974       return _data[x + y*_width + z*wh + c*whd];
12975     }
12976 #endif
12977 
12978     //! Implicitly cast an image into a \c T*.
12979     /**
12980        Implicitly cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
12981        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
12982        \note
12983        - It simply returns the pointer data() to the pixel buffer.
12984        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
12985        \code
12986        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image
12987        if (img1) {                      // Test succeeds, 'img1' is not an empty image
12988          if (!img2) {                   // Test succeeds, 'img2' is an empty image
12989            std::printf("'img1' is not empty, 'img2' is empty.");
12990          }
12991        }
12992        \endcode
12993        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
12994        \code
12995        CImg<float> img(100,100);
12996        const float value = img[99]; // Access to value of the last pixel on the first row
12997        img[510] = 255;              // Set pixel value at (10,5)
12998        \endcode
12999     **/
13000     operator T*() {
13001       return _data;
13002     }
13003 
13004     //! Implicitly cast an image into a \c T* \const.
13005     operator const T*() const {
13006       return _data;
13007     }
13008 
13009     //! Assign a value to all image pixels.
13010     /**
13011        Assign specified \c value to each pixel value of the image instance.
13012        \param value Value that will be assigned to image pixels.
13013        \note
13014        - The image size is never modified.
13015        - The \c value may be casted to pixel type \c T if necessary.
13016        \par Example
13017        \code
13018        CImg<char> img(100,100); // Declare image (with garbage values)
13019        img = 0;                 // Set all pixel values to '0'
13020        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char')
13021        \endcode
13022     **/
13023     CImg<T>& operator=(const T& value) {
13024       return fill(value);
13025     }
13026 
13027     //! Assign pixels values from a specified expression.
13028     /**
13029        Initialize all pixel values from the specified string \c expression.
13030        \param expression Value string describing the way pixel values are set.
13031        \note
13032        - String parameter \c expression may describe different things:
13033          - 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"),
13034            the pixel values are set from specified \c expression and the image size is not modified.
13035          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
13036            replace the image instance. The image size is modified if necessary.
13037        \par Example
13038        \code
13039        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values
13040        img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence
13041        img2 = "10*((x*y)%25)";                       // Set pixel values of 'img2' from a formula
13042        img3 = "reference.jpg";                       // Set pixel values of 'img3' from a file (image size is modified)
13043        (img1,img2,img3).display();
13044        \endcode
13045        \image html ref_operator_eq.jpg
13046     **/
13047     CImg<T>& operator=(const char *const expression) {
13048       const unsigned int omode = cimg::exception_mode();
13049       cimg::exception_mode(0);
13050       try {
13051         _fill(expression,true,1,0,0,"operator=",0);
13052       } catch (CImgException&) {
13053         cimg::exception_mode(omode);
13054         load(expression);
13055       }
13056       cimg::exception_mode(omode);
13057       return *this;
13058     }
13059 
13060     //! Copy an image into the current image instance.
13061     /**
13062        Similar to the in-place copy constructor assign(const CImg<t>&).
13063     **/
13064     template<typename t>
13065     CImg<T>& operator=(const CImg<t>& img) {
13066       return assign(img);
13067     }
13068 
13069     //! Copy an image into the current image instance \specialization.
13070     CImg<T>& operator=(const CImg<T>& img) {
13071       return assign(img);
13072     }
13073 
13074     //! Copy the content of a display window to the current image instance.
13075     /**
13076        Similar to assign(const CImgDisplay&).
13077     **/
13078     CImg<T>& operator=(const CImgDisplay& disp) {
13079       disp.snapshot(*this);
13080       return *this;
13081     }
13082 
13083     //! In-place addition operator.
13084     /**
13085        Add specified \c value to all pixels of an image instance.
13086        \param value Value to add.
13087        \note
13088        - Resulting pixel values are casted to fit the pixel type \c T.
13089          For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
13090        - Overflow values are treated as with standard C++ numeric types. For instance,
13091        \code
13092        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'
13093        img+=1;                                   // Add '1' to each pixels -> Overflow
13094        // here all pixels of image 'img' are equal to '0'.
13095        \endcode
13096        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
13097          and use cut() after addition.
13098        \par Example
13099        \code
13100        CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255])
13101        CImg<float> img2(img1); // Construct a float-valued copy of 'img1'
13102        img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats
13103        img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint
13104        img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'
13105        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way
13106        (img1,img2,img3).display();
13107        \endcode
13108        \image html ref_operator_plus.jpg
13109      **/
13110     template<typename t>
13111     CImg<T>& operator+=(const t value) {
13112       if (is_empty()) return *this;
13113       cimg_openmp_for(*this,*ptr + value,524288);
13114       return *this;
13115     }
13116 
13117     //! In-place addition operator.
13118     /**
13119        Add values to image pixels, according to the specified string \c expression.
13120        \param expression Value string describing the way pixel values are added.
13121        \note
13122        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
13123          instead of assigning them.
13124     **/
13125     CImg<T>& operator+=(const char *const expression) {
13126       return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this);
13127     }
13128 
13129     //! In-place addition operator.
13130     /**
13131        Add values to image pixels, according to the values of the input image \c img.
13132        \param img Input image to add.
13133        \note
13134        - The size of the image instance is never modified.
13135        - It is not mandatory that input image \c img has the same size as the image instance.
13136          If less values are available in \c img, then the values are added periodically. For instance, adding one
13137          WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
13138          means each color channel will be incremented with the same values at the same locations.
13139        \par Example
13140        \code
13141        CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
13142        // Construct a scalar shading (img2.spectrum()==1).
13143        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
13144        img1+=img2; // Add shading to each channel of 'img1'
13145        img1.cut(0,255); // Prevent [0,255] overflow
13146        (img2,img1).display();
13147        \endcode
13148        \image html ref_operator_plus1.jpg
13149     **/
13150     template<typename t>
13151     CImg<T>& operator+=(const CImg<t>& img) {
13152       const ulongT siz = size(), isiz = img.size();
13153       if (siz && isiz) {
13154         if (is_overlapped(img)) return *this+=+img;
13155         T *ptrd = _data, *const ptre = _data + siz;
13156         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13157           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13158             *ptrd = (T)(*ptrd + *(ptrs++));
13159         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
13160       }
13161       return *this;
13162     }
13163 
13164     //! In-place increment operator (prefix).
13165     /**
13166        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
13167        \note
13168        - Writing \c ++img is equivalent to \c img+=1.
13169      **/
13170     CImg<T>& operator++() {
13171       if (is_empty()) return *this;
13172       cimg_openmp_for(*this,*ptr + 1,524288);
13173       return *this;
13174     }
13175 
13176     //! In-place increment operator (postfix).
13177     /**
13178        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
13179        \note
13180        - Use the prefixed version operator++() if you don't need a copy of the initial
13181          (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
13182      **/
13183     CImg<T> operator++(int) {
13184       const CImg<T> copy(*this,false);
13185       ++*this;
13186       return copy;
13187     }
13188 
13189     //! Return a non-shared copy of the image instance.
13190     /**
13191        \note
13192        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
13193          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
13194          and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
13195          information about the shared state of the input image.
13196        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
13197     **/
13198     CImg<T> operator+() const {
13199       return CImg<T>(*this,false);
13200     }
13201 
13202     //! Addition operator.
13203     /**
13204        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
13205        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13206      **/
13207     template<typename t>
13208     CImg<_cimg_Tt> operator+(const t value) const {
13209       return CImg<_cimg_Tt>(*this,false)+=value;
13210     }
13211 
13212     //! Addition operator.
13213     /**
13214        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
13215        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13216      **/
13217     CImg<Tfloat> operator+(const char *const expression) const {
13218       return CImg<Tfloat>(*this,false)+=expression;
13219     }
13220 
13221     //! Addition operator.
13222     /**
13223        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13224        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13225      **/
13226     template<typename t>
13227     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
13228       return CImg<_cimg_Tt>(*this,false)+=img;
13229     }
13230 
13231     //! In-place subtraction operator.
13232     /**
13233        Similar to operator+=(const t), except that it performs a subtraction instead of an addition.
13234      **/
13235     template<typename t>
13236     CImg<T>& operator-=(const t value) {
13237       if (is_empty()) return *this;
13238       cimg_openmp_for(*this,*ptr - value,524288);
13239       return *this;
13240     }
13241 
13242     //! In-place subtraction operator.
13243     /**
13244        Similar to operator+=(const char*), except that it performs a subtraction instead of an addition.
13245      **/
13246     CImg<T>& operator-=(const char *const expression) {
13247       return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this);
13248     }
13249 
13250     //! In-place subtraction operator.
13251     /**
13252        Similar to operator+=(const CImg<t>&), except that it performs a subtraction instead of an addition.
13253      **/
13254     template<typename t>
13255     CImg<T>& operator-=(const CImg<t>& img) {
13256       const ulongT siz = size(), isiz = img.size();
13257       if (siz && isiz) {
13258         if (is_overlapped(img)) return *this-=+img;
13259         T *ptrd = _data, *const ptre = _data + siz;
13260         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13261           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13262             *ptrd = (T)(*ptrd - *(ptrs++));
13263         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
13264       }
13265       return *this;
13266     }
13267 
13268     //! In-place decrement operator (prefix).
13269     /**
13270        Similar to operator++(), except that it performs a decrement instead of an increment.
13271     **/
13272     CImg<T>& operator--() {
13273       if (is_empty()) return *this;
13274       cimg_openmp_for(*this,*ptr - 1,524288);
13275       return *this;
13276     }
13277 
13278     //! In-place decrement operator (postfix).
13279     /**
13280        Similar to operator++(int), except that it performs a decrement instead of an increment.
13281     **/
13282     CImg<T> operator--(int) {
13283       const CImg<T> copy(*this,false);
13284       --*this;
13285       return copy;
13286     }
13287 
13288     //! Replace each pixel by its opposite value.
13289     /**
13290        \note
13291        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
13292          For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
13293        \par Example
13294        \code
13295        const CImg<unsigned char>
13296          img1("reference.jpg"),   // Load a RGB color image
13297          img2 = -img1;            // Compute its opposite (in 'unsigned char')
13298        (img1,img2).display();
13299        \endcode
13300        \image html ref_operator_minus.jpg
13301      **/
13302     CImg<T> operator-() const {
13303       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
13304     }
13305 
13306     //! Subtraction operator.
13307     /**
13308        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
13309        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13310     **/
13311     template<typename t>
13312     CImg<_cimg_Tt> operator-(const t value) const {
13313       return CImg<_cimg_Tt>(*this,false)-=value;
13314     }
13315 
13316     //! Subtraction operator.
13317     /**
13318        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
13319        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13320     **/
13321     CImg<Tfloat> operator-(const char *const expression) const {
13322       return CImg<Tfloat>(*this,false)-=expression;
13323     }
13324 
13325     //! Subtraction operator.
13326     /**
13327        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13328        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13329     **/
13330     template<typename t>
13331     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
13332       return CImg<_cimg_Tt>(*this,false)-=img;
13333     }
13334 
13335     //! In-place multiplication operator.
13336     /**
13337        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
13338      **/
13339     template<typename t>
13340     CImg<T>& operator*=(const t value) {
13341       if (is_empty()) return *this;
13342       cimg_openmp_for(*this,*ptr * value,262144);
13343       return *this;
13344     }
13345 
13346     //! In-place multiplication operator.
13347     /**
13348        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
13349      **/
13350     CImg<T>& operator*=(const char *const expression) {
13351       return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this));
13352     }
13353 
13354     //! In-place multiplication operator.
13355     /**
13356        Replace the image instance by the matrix multiplication between the image instance and the specified matrix
13357        \c img.
13358        \param img Second operand of the matrix multiplication.
13359        \note
13360        - It does \e not compute a pointwise multiplication between two images. For this purpose, use
13361          mul(const CImg<t>&) instead.
13362        - The size of the image instance can be modified by this operator.
13363        \par Example
13364        \code
13365        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4]
13366        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2]
13367        A*=X;                              // Assign matrix multiplication A*X to 'A'
13368        // 'A' is now a 1x2 vector whose values are [5;11].
13369        \endcode
13370     **/
13371     template<typename t>
13372     CImg<T>& operator*=(const CImg<t>& img) {
13373       return ((*this)*img).move_to(*this);
13374     }
13375 
13376     //! Multiplication operator.
13377     /**
13378        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
13379        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13380     **/
13381     template<typename t>
13382     CImg<_cimg_Tt> operator*(const t value) const {
13383       return CImg<_cimg_Tt>(*this,false)*=value;
13384     }
13385 
13386     //! Multiplication operator.
13387     /**
13388        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
13389        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13390     **/
13391     CImg<Tfloat> operator*(const char *const expression) const {
13392       return CImg<Tfloat>(*this,false)*=expression;
13393     }
13394 
13395     //! Multiplication operator.
13396     /**
13397        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13398        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13399     **/
13400     template<typename t>
13401     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
13402       typedef _cimg_Ttdouble Ttdouble;
13403       typedef _cimg_Tt Tt;
13404       if (_width!=img._height || _depth!=1 || _spectrum!=1)
13405         throw CImgArgumentException(_cimg_instance
13406                                     "operator*(): Invalid multiplication of instance by specified "
13407                                     "matrix (%u,%u,%u,%u,%p).",
13408                                     cimg_instance,
13409                                     img._width,img._height,img._depth,img._spectrum,img._data);
13410       CImg<Tt> res(img._width,_height);
13411 
13412       // Check for common cases to optimize.
13413       if (img._width==1) { // Matrix * Vector
13414         if (_height==1) switch (_width) { // Vector^T * Vector
13415           case 1 :
13416             res[0] = (Tt)((Ttdouble)_data[0]*img[0]);
13417             return res;
13418           case 2 :
13419             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
13420             return res;
13421           case 3 :
13422             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13423                           (Ttdouble)_data[2]*img[2]);
13424             return res;
13425           case 4 :
13426             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13427                           (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
13428             return res;
13429           default : {
13430             Ttdouble val = 0;
13431             cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096))
13432             cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i];
13433             res[0] = val;
13434             return res;
13435           }
13436           } else if (_height==_width) switch (_width) { // Square_matrix * Vector
13437           case 2 : // 2x2_matrix * Vector
13438             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
13439             res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]);
13440             return res;
13441           case 3 : // 3x3_matrix * Vector
13442             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13443                           (Ttdouble)_data[2]*img[2]);
13444             res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] +
13445                           (Ttdouble)_data[5]*img[2]);
13446             res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] +
13447                           (Ttdouble)_data[8]*img[2]);
13448             return res;
13449           case 4 : // 4x4_matrix * Vector
13450             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13451                           (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
13452             res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] +
13453                           (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]);
13454             res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] +
13455                           (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]);
13456             res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] +
13457                           (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]);
13458             return res;
13459           }
13460       } else if (_height==_width) {
13461         if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix
13462           case 2 : // 2x2_matrix * 2x2_matrix
13463             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]);
13464             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]);
13465             res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]);
13466             res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]);
13467             return res;
13468           case 3 : // 3x3_matrix * 3x3_matrix
13469             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] +
13470                           (Ttdouble)_data[2]*img[6]);
13471             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] +
13472                           (Ttdouble)_data[2]*img[7]);
13473             res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] +
13474                           (Ttdouble)_data[2]*img[8]);
13475             res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] +
13476                           (Ttdouble)_data[5]*img[6]);
13477             res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] +
13478                           (Ttdouble)_data[5]*img[7]);
13479             res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] +
13480                           (Ttdouble)_data[5]*img[8]);
13481             res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] +
13482                           (Ttdouble)_data[8]*img[6]);
13483             res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] +
13484                           (Ttdouble)_data[8]*img[7]);
13485             res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] +
13486                           (Ttdouble)_data[8]*img[8]);
13487             return res;
13488           case 4 : // 4x4_matrix * 4x4_matrix
13489             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] +
13490                           (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]);
13491             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] +
13492                           (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]);
13493             res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] +
13494                           (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]);
13495             res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] +
13496                           (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]);
13497             res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] +
13498                           (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]);
13499             res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] +
13500                           (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]);
13501             res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] +
13502                           (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]);
13503             res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] +
13504                           (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]);
13505             res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] +
13506                           (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]);
13507             res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] +
13508                           (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]);
13509             res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] +
13510                            (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]);
13511             res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] +
13512                            (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]);
13513             res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] +
13514                            (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]);
13515             res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] +
13516                            (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]);
13517             res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] +
13518                            (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]);
13519             res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] +
13520                            (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]);
13521             return res;
13522           } else switch (_width) { // Square_matrix * Matrix
13523           case 2 : { // 2x2_matrix * Matrix
13524             const t *const ps0 = img.data(), *const ps1 = img.data(0,1);
13525             Tt *const pd0 = res.data(), *const pd1 = res.data(0,1);
13526             const Ttdouble
13527               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1],
13528               a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3];
13529             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096))
13530             cimg_forX(img,i) {
13531               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i];
13532               pd0[i] = (Tt)(a0*x + a1*y);
13533               pd1[i] = (Tt)(a2*x + a3*y);
13534             }
13535             return res;
13536           }
13537           case 3 : { // 3x3_matrix * Matrix
13538             const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2);
13539             Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2);
13540             const Ttdouble
13541               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2],
13542               a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5],
13543               a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8];
13544             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024))
13545             cimg_forX(img,i) {
13546               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i];
13547               pd0[i] = (Tt)(a0*x + a1*y + a2*z);
13548               pd1[i] = (Tt)(a3*x + a4*y + a5*z);
13549               pd2[i] = (Tt)(a6*x + a7*y + a8*z);
13550             }
13551             return res;
13552           }
13553           case 4 : { // 4x4_matrix * Matrix
13554             const t
13555               *const ps0 = img.data(), *const ps1 = img.data(0,1),
13556               *const ps2 = img.data(0,2), *const ps3 = img.data(0,3);
13557             Tt
13558               *const pd0 = res.data(), *const pd1 = res.data(0,1),
13559               *const pd2 = res.data(0,2), *const pd3 = res.data(0,3);
13560             const Ttdouble
13561               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3],
13562               a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7],
13563               a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11],
13564               a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14],
13565               a15 = (Ttdouble)_data[15];
13566             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512))
13567             cimg_forX(img,i) {
13568               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i];
13569               pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c);
13570               pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c);
13571               pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c);
13572               pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c);
13573             }
13574             return res;
13575           }
13576           }
13577       }
13578 
13579       // Fallback to generic version.
13580 #if cimg_use_openmp!=0
13581       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
13582                          cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 &&
13583                                         img.size()>(cimg_openmp_sizefactor)*1024))
13584         cimg_forXY(res,i,j) {
13585           Ttdouble value = 0;
13586           cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
13587           res(i,j) = (Tt)value;
13588       }
13589 #else
13590       Tt *ptrd = res._data;
13591       cimg_forXY(res,i,j) {
13592         Ttdouble value = 0;
13593         cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
13594         *(ptrd++) = (Tt)value;
13595       }
13596 #endif
13597       return res;
13598     }
13599 
13600     //! In-place division operator.
13601     /**
13602        Similar to operator+=(const t), except that it performs a division instead of an addition.
13603      **/
13604     template<typename t>
13605     CImg<T>& operator/=(const t value) {
13606       if (is_empty()) return *this;
13607       cimg_openmp_for(*this,*ptr / value,32768);
13608       return *this;
13609     }
13610 
13611     //! In-place division operator.
13612     /**
13613        Similar to operator+=(const char*), except that it performs a division instead of an addition.
13614      **/
13615     CImg<T>& operator/=(const char *const expression) {
13616       return div((+*this)._fill(expression,true,1,0,0,"operator/=",this));
13617     }
13618 
13619     //! In-place division operator.
13620     /**
13621        Replace the image instance by the (right) matrix division between the image instance and the specified
13622        matrix \c img.
13623        \param img Second operand of the matrix division.
13624        \note
13625        - It does \e not compute a pointwise division between two images. For this purpose, use
13626          div(const CImg<t>&) instead.
13627        - It returns the matrix operation \c A*inverse(img).
13628        - The size of the image instance can be modified by this operator.
13629      **/
13630     template<typename t>
13631     CImg<T>& operator/=(const CImg<t>& img) {
13632       return (*this*img.get_invert()).move_to(*this);
13633     }
13634 
13635     //! Division operator.
13636     /**
13637        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
13638        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13639     **/
13640     template<typename t>
13641     CImg<_cimg_Tt> operator/(const t value) const {
13642       return CImg<_cimg_Tt>(*this,false)/=value;
13643     }
13644 
13645     //! Division operator.
13646     /**
13647        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
13648        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13649     **/
13650     CImg<Tfloat> operator/(const char *const expression) const {
13651       return CImg<Tfloat>(*this,false)/=expression;
13652     }
13653 
13654     //! Division operator.
13655     /**
13656        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13657        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13658     **/
13659     template<typename t>
13660     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
13661       return (*this)*img.get_invert();
13662     }
13663 
13664     //! In-place modulo operator.
13665     /**
13666        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
13667     **/
13668     template<typename t>
13669     CImg<T>& operator%=(const t value) {
13670       if (is_empty()) return *this;
13671       cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384);
13672       return *this;
13673     }
13674 
13675     //! In-place modulo operator.
13676     /**
13677        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
13678     **/
13679     CImg<T>& operator%=(const char *const expression) {
13680       return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this);
13681     }
13682 
13683     //! In-place modulo operator.
13684     /**
13685        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
13686     **/
13687     template<typename t>
13688     CImg<T>& operator%=(const CImg<t>& img) {
13689       const ulongT siz = size(), isiz = img.size();
13690       if (siz && isiz) {
13691         if (is_overlapped(img)) return *this%=+img;
13692         T *ptrd = _data, *const ptre = _data + siz;
13693         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13694           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13695             *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
13696         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
13697       }
13698       return *this;
13699     }
13700 
13701     //! Modulo operator.
13702     /**
13703        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
13704        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13705     **/
13706     template<typename t>
13707     CImg<_cimg_Tt> operator%(const t value) const {
13708       return CImg<_cimg_Tt>(*this,false)%=value;
13709     }
13710 
13711     //! Modulo operator.
13712     /**
13713        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
13714        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13715     **/
13716     CImg<Tfloat> operator%(const char *const expression) const {
13717       return CImg<Tfloat>(*this,false)%=expression;
13718     }
13719 
13720     //! Modulo operator.
13721     /**
13722        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13723        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13724     **/
13725     template<typename t>
13726     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
13727       return CImg<_cimg_Tt>(*this,false)%=img;
13728     }
13729 
13730     //! In-place bitwise AND operator.
13731     /**
13732        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
13733     **/
13734     template<typename t>
13735     CImg<T>& operator&=(const t value) {
13736       if (is_empty()) return *this;
13737       cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768);
13738       return *this;
13739     }
13740 
13741     //! In-place bitwise AND operator.
13742     /**
13743        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
13744     **/
13745     CImg<T>& operator&=(const char *const expression) {
13746       return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this);
13747     }
13748 
13749     //! In-place bitwise AND operator.
13750     /**
13751        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
13752     **/
13753     template<typename t>
13754     CImg<T>& operator&=(const CImg<t>& img) {
13755       const ulongT siz = size(), isiz = img.size();
13756       if (siz && isiz) {
13757         if (is_overlapped(img)) return *this&=+img;
13758         T *ptrd = _data, *const ptre = _data + siz;
13759         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13760           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13761             *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
13762         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
13763       }
13764       return *this;
13765     }
13766 
13767     //! Bitwise AND operator.
13768     /**
13769        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
13770        The pixel type of the returned image is \c T.
13771     **/
13772     template<typename t>
13773     CImg<T> operator&(const t value) const {
13774       return (+*this)&=value;
13775     }
13776 
13777     //! Bitwise AND operator.
13778     /**
13779        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
13780        The pixel type of the returned image is \c T.
13781     **/
13782     CImg<T> operator&(const char *const expression) const {
13783       return (+*this)&=expression;
13784     }
13785 
13786     //! Bitwise AND operator.
13787     /**
13788        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13789        The pixel type of the returned image is \c T.
13790     **/
13791     template<typename t>
13792     CImg<T> operator&(const CImg<t>& img) const {
13793       return (+*this)&=img;
13794     }
13795 
13796     //! In-place bitwise OR operator.
13797     /**
13798        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
13799     **/
13800     template<typename t>
13801     CImg<T>& operator|=(const t value) {
13802       if (is_empty()) return *this;
13803       cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768);
13804       return *this;
13805     }
13806 
13807     //! In-place bitwise OR operator.
13808     /**
13809        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
13810     **/
13811     CImg<T>& operator|=(const char *const expression) {
13812       return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this);
13813     }
13814 
13815     //! In-place bitwise OR operator.
13816     /**
13817        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
13818     **/
13819     template<typename t>
13820     CImg<T>& operator|=(const CImg<t>& img) {
13821       const ulongT siz = size(), isiz = img.size();
13822       if (siz && isiz) {
13823         if (is_overlapped(img)) return *this|=+img;
13824         T *ptrd = _data, *const ptre = _data + siz;
13825         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13826           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13827             *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
13828         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
13829       }
13830       return *this;
13831     }
13832 
13833     //! Bitwise OR operator.
13834     /**
13835        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
13836        The pixel type of the returned image is \c T.
13837     **/
13838     template<typename t>
13839     CImg<T> operator|(const t value) const {
13840       return (+*this)|=value;
13841     }
13842 
13843     //! Bitwise OR operator.
13844     /**
13845        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
13846        The pixel type of the returned image is \c T.
13847     **/
13848     CImg<T> operator|(const char *const expression) const {
13849       return (+*this)|=expression;
13850     }
13851 
13852     //! Bitwise OR operator.
13853     /**
13854        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13855        The pixel type of the returned image is \c T.
13856     **/
13857     template<typename t>
13858     CImg<T> operator|(const CImg<t>& img) const {
13859       return (+*this)|=img;
13860     }
13861 
13862     //! In-place bitwise XOR operator.
13863     /**
13864        Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
13865        \warning
13866        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
13867     **/
13868     template<typename t>
13869     CImg<T>& operator^=(const t value) {
13870       if (is_empty()) return *this;
13871       cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768);
13872       return *this;
13873     }
13874 
13875     //! In-place bitwise XOR operator.
13876     /**
13877        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
13878        \warning
13879        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
13880     **/
13881     CImg<T>& operator^=(const char *const expression) {
13882       return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this);
13883     }
13884 
13885     //! In-place bitwise XOR operator.
13886     /**
13887        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
13888        \warning
13889        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
13890     **/
13891     template<typename t>
13892     CImg<T>& operator^=(const CImg<t>& img) {
13893       const ulongT siz = size(), isiz = img.size();
13894       if (siz && isiz) {
13895         if (is_overlapped(img)) return *this^=+img;
13896         T *ptrd = _data, *const ptre = _data + siz;
13897         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13898           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13899             *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
13900         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
13901       }
13902       return *this;
13903     }
13904 
13905     //! Bitwise XOR operator.
13906     /**
13907        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
13908        The pixel type of the returned image is \c T.
13909     **/
13910     template<typename t>
13911     CImg<T> operator^(const t value) const {
13912       return (+*this)^=value;
13913     }
13914 
13915     //! Bitwise XOR operator.
13916     /**
13917        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
13918        The pixel type of the returned image is \c T.
13919     **/
13920     CImg<T> operator^(const char *const expression) const {
13921       return (+*this)^=expression;
13922     }
13923 
13924     //! Bitwise XOR operator.
13925     /**
13926        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13927        The pixel type of the returned image is \c T.
13928     **/
13929     template<typename t>
13930     CImg<T> operator^(const CImg<t>& img) const {
13931       return (+*this)^=img;
13932     }
13933 
13934     //! In-place bitwise left shift operator.
13935     /**
13936        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
13937     **/
13938     template<typename t>
13939     CImg<T>& operator<<=(const t value) {
13940       if (is_empty()) return *this;
13941       cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536);
13942       return *this;
13943     }
13944 
13945     //! In-place bitwise left shift operator.
13946     /**
13947        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
13948     **/
13949     CImg<T>& operator<<=(const char *const expression) {
13950       return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this);
13951     }
13952 
13953     //! In-place bitwise left shift operator.
13954     /**
13955        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
13956     **/
13957     template<typename t>
13958     CImg<T>& operator<<=(const CImg<t>& img) {
13959       const ulongT siz = size(), isiz = img.size();
13960       if (siz && isiz) {
13961         if (is_overlapped(img)) return *this^=+img;
13962         T *ptrd = _data, *const ptre = _data + siz;
13963         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13964           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13965             *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
13966         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
13967       }
13968       return *this;
13969     }
13970 
13971     //! Bitwise left shift operator.
13972     /**
13973        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
13974        The pixel type of the returned image is \c T.
13975     **/
13976     template<typename t>
13977     CImg<T> operator<<(const t value) const {
13978       return (+*this)<<=value;
13979     }
13980 
13981     //! Bitwise left shift operator.
13982     /**
13983        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
13984        The pixel type of the returned image is \c T.
13985     **/
13986     CImg<T> operator<<(const char *const expression) const {
13987       return (+*this)<<=expression;
13988     }
13989 
13990     //! Bitwise left shift operator.
13991     /**
13992        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
13993        operating in-place.
13994        The pixel type of the returned image is \c T.
13995     **/
13996     template<typename t>
13997     CImg<T> operator<<(const CImg<t>& img) const {
13998       return (+*this)<<=img;
13999     }
14000 
14001     //! In-place bitwise right shift operator.
14002     /**
14003        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
14004     **/
14005     template<typename t>
14006     CImg<T>& operator>>=(const t value) {
14007       if (is_empty()) return *this;
14008       cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536);
14009       return *this;
14010     }
14011 
14012     //! In-place bitwise right shift operator.
14013     /**
14014        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
14015     **/
14016     CImg<T>& operator>>=(const char *const expression) {
14017       return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this);
14018     }
14019 
14020     //! In-place bitwise right shift operator.
14021     /**
14022        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
14023     **/
14024     template<typename t>
14025     CImg<T>& operator>>=(const CImg<t>& img) {
14026       const ulongT siz = size(), isiz = img.size();
14027       if (siz && isiz) {
14028         if (is_overlapped(img)) return *this^=+img;
14029         T *ptrd = _data, *const ptre = _data + siz;
14030         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
14031           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
14032             *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
14033         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
14034       }
14035       return *this;
14036     }
14037 
14038     //! Bitwise right shift operator.
14039     /**
14040        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
14041        The pixel type of the returned image is \c T.
14042     **/
14043     template<typename t>
14044     CImg<T> operator>>(const t value) const {
14045       return (+*this)>>=value;
14046     }
14047 
14048     //! Bitwise right shift operator.
14049     /**
14050        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
14051        The pixel type of the returned image is \c T.
14052     **/
14053     CImg<T> operator>>(const char *const expression) const {
14054       return (+*this)>>=expression;
14055     }
14056 
14057     //! Bitwise right shift operator.
14058     /**
14059        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
14060        operating in-place.
14061        The pixel type of the returned image is \c T.
14062     **/
14063     template<typename t>
14064     CImg<T> operator>>(const CImg<t>& img) const {
14065       return (+*this)>>=img;
14066     }
14067 
14068     //! Bitwise inversion operator.
14069     /**
14070        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
14071     **/
14072     CImg<T> operator~() const {
14073       CImg<T> res(_width,_height,_depth,_spectrum);
14074       const T *ptrs = _data;
14075       cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
14076       return res;
14077     }
14078 
14079     //! Test if all pixels of an image have the same value.
14080     /**
14081        Return \c true is all pixels of the image instance are equal to the specified \c value.
14082        \param value Reference value to compare with.
14083     **/
14084     template<typename t>
14085     bool operator==(const t value) const {
14086       if (is_empty()) return false;
14087       typedef _cimg_Tt Tt;
14088       bool is_equal = true;
14089       for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
14090       return is_equal;
14091     }
14092 
14093     //! Test if all pixel values of an image follow a specified expression.
14094     /**
14095        Return \c true is all pixels of the image instance are equal to the specified \c expression.
14096        \param expression Value string describing the way pixel values are compared.
14097     **/
14098     bool operator==(const char *const expression) const {
14099       return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this);
14100     }
14101 
14102     //! Test if two images have the same size and values.
14103     /**
14104        Return \c true if the image instance and the input image \c img have the same pixel values,
14105        even if the dimensions of the two images do not match. It returns \c false otherwise.
14106        \param img Input image to compare with.
14107        \note
14108        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
14109          to return \c true.
14110          Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
14111          pixel types \c T and \c t.
14112        \par Example
14113        \code
14114        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values)
14115        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values)
14116        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same
14117          std::printf("'img1' and 'img2' have same dimensions and values.");
14118        }
14119        \endcode
14120     **/
14121     template<typename t>
14122     bool operator==(const CImg<t>& img) const {
14123       typedef _cimg_Tt Tt;
14124       const ulongT siz = size();
14125       bool is_equal = true;
14126       if (siz!=img.size()) return false;
14127       t *ptrs = img._data + siz;
14128       for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
14129       return is_equal;
14130     }
14131 
14132     //! Test if pixels of an image are all different from a value.
14133     /**
14134        Return \c true is all pixels of the image instance are different than the specified \c value.
14135        \param value Reference value to compare with.
14136     **/
14137     template<typename t>
14138     bool operator!=(const t value) const {
14139       return !((*this)==value);
14140     }
14141 
14142     //! Test if all pixel values of an image are different from a specified expression.
14143     /**
14144        Return \c true is all pixels of the image instance are different to the specified \c expression.
14145        \param expression Value string describing the way pixel values are compared.
14146     **/
14147     bool operator!=(const char *const expression) const {
14148       return !((*this)==expression);
14149     }
14150 
14151     //! Test if two images have different sizes or values.
14152     /**
14153        Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
14154        and \c false otherwise.
14155        \param img Input image to compare with.
14156        \note
14157        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
14158     **/
14159     template<typename t>
14160     bool operator!=(const CImg<t>& img) const {
14161       return !((*this)==img);
14162     }
14163 
14164     //! Construct an image list from two images.
14165     /**
14166        Return a new list of image (\c CImgList instance) containing exactly two elements:
14167          - A copy of the image instance, at position [\c 0].
14168          - A copy of the specified image \c img, at position [\c 1].
14169 
14170        \param img Input image that will be the second image of the resulting list.
14171        \note
14172        - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
14173          in practice (see warning below).
14174        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
14175          inserted as new non-shared copies in the resulting list.
14176        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
14177        \warning
14178        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
14179          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
14180          build a new CImgList instance from several images, if you are seeking for performance.
14181          Fast insertions of images in an image list are possible with
14182          CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
14183        \par Example
14184        \code
14185        const CImg<float>
14186           img1("reference.jpg"),
14187           img2 = img1.get_mirror('x'),
14188           img3 = img2.get_blur(5);
14189        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'
14190        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'
14191        \endcode
14192        \image html ref_operator_comma.jpg
14193     **/
14194     template<typename t>
14195     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
14196       return CImgList<_cimg_Tt>(*this,img);
14197     }
14198 
14199     //! Construct an image list from image instance and an input image list.
14200     /**
14201        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
14202          - A copy of the image instance, at position [\c 0].
14203          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
14204 
14205        \param list Input image list that will be appended to the image instance.
14206        \note
14207        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
14208     **/
14209     template<typename t>
14210     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
14211       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
14212     }
14213 
14214     //! Split image along specified axis.
14215     /**
14216        Return a new list of images (\c CImgList instance) containing the split components
14217        of the instance image along the specified axis.
14218        \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
14219        \note
14220        - Similar to get_split(char,int) const, with default second argument.
14221        \par Example
14222        \code
14223        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image
14224        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels
14225        (img,list).display();
14226        \endcode
14227        \image html ref_operator_less.jpg
14228     **/
14229     CImgList<T> operator<(const char axis) const {
14230       return get_split(axis);
14231     }
14232 
14233     //@}
14234     //-------------------------------------
14235     //
14236     //! \name Instance Characteristics
14237     //@{
14238     //-------------------------------------
14239 
14240     //! Return the type of image pixel values as a C string.
14241     /**
14242        Return a \c char* string containing the usual type name of the image pixel values
14243        (i.e. a stringified version of the template parameter \c T).
14244        \note
14245        - The returned string may contain spaces (as in \c "unsigned char").
14246        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
14247     **/
14248     static const char* pixel_type() {
14249       return cimg::type<T>::string();
14250     }
14251 
14252     //! Return the number of image columns.
14253     /**
14254        Return the image width, i.e. the image dimension along the X-axis.
14255        \note
14256        - The width() of an empty image is equal to \c 0.
14257        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
14258        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
14259          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14260          \c unsigned \c int variables.
14261          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14262          <tt>(*this)._width</tt>.
14263     **/
14264     int width() const {
14265       return (int)_width;
14266     }
14267 
14268     //! Return the number of image rows.
14269     /**
14270        Return the image height, i.e. the image dimension along the Y-axis.
14271        \note
14272        - The height() of an empty image is equal to \c 0.
14273        - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
14274          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14275          \c unsigned \c int variables.
14276          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14277          <tt>(*this)._height</tt>.
14278     **/
14279     int height() const {
14280       return (int)_height;
14281     }
14282 
14283     //! Return the number of image slices.
14284     /**
14285        Return the image depth, i.e. the image dimension along the Z-axis.
14286        \note
14287        - The depth() of an empty image is equal to \c 0.
14288        - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image
14289          is said to be \e volumetric.
14290        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
14291          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14292          \c unsigned \c int variables.
14293          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14294          <tt>(*this)._depth</tt>.
14295     **/
14296     int depth() const {
14297       return (int)_depth;
14298     }
14299 
14300     //! Return the number of image channels.
14301     /**
14302        Return the number of image channels, i.e. the image dimension along the C-axis.
14303        \note
14304        - The spectrum() of an empty image is equal to \c 0.
14305        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
14306          for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
14307          The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
14308          up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
14309        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
14310          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14311          \c unsigned \c int variables.
14312          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14313          <tt>(*this)._spectrum</tt>.
14314     **/
14315     int spectrum() const {
14316       return (int)_spectrum;
14317     }
14318 
14319     //! Return the total number of pixel values.
14320     /**
14321        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
14322        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
14323        \note
14324        - The size() of an empty image is equal to \c 0.
14325        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
14326          <tt>size()*sizeof(T)</tt>.
14327        \par Example
14328        \code
14329        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image
14330        if (img.size()==30000)                            // Test succeeds
14331          std::printf("Pixel buffer uses %lu bytes",
14332                      img.size()*sizeof(float));
14333        \endcode
14334     **/
14335     ulongT size() const {
14336       return (ulongT)_width*_height*_depth*_spectrum;
14337     }
14338 
14339     //! Return a pointer to the first pixel value.
14340     /**
14341        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
14342        whether the instance is \c const or not.
14343        \note
14344        - The data() of an empty image is equal to \c 0 (null pointer).
14345        - The allocated pixel buffer for the image instance starts from \c data()
14346          and goes to <tt>data()+\ref size() - 1</tt> (included).
14347        - To get the pointer to one particular location of the pixel buffer, use
14348          data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
14349     **/
14350     T* data() {
14351       return _data;
14352     }
14353 
14354     //! Return a pointer to the first pixel value \const.
14355     const T* data() const {
14356       return _data;
14357     }
14358 
14359     //! Return a pointer to a located pixel value.
14360     /**
14361        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
14362        of the image instance,
14363        whether the instance is \c const or not.
14364        \param x X-coordinate of the pixel value.
14365        \param y Y-coordinate of the pixel value.
14366        \param z Z-coordinate of the pixel value.
14367        \param c C-coordinate of the pixel value.
14368        \note
14369        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
14370          properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
14371      **/
14372 #if cimg_verbosity>=3
14373     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
14374       const ulongT off = (ulongT)offset(x,y,z,c);
14375       if (off>=size())
14376         cimg::warn(_cimg_instance
14377                    "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
14378                    cimg_instance,
14379                    x,y,z,c,off);
14380       return _data + off;
14381     }
14382 
14383     //! Return a pointer to a located pixel value \const.
14384     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
14385       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
14386     }
14387 #else
14388     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
14389       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
14390     }
14391 
14392     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
14393       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
14394     }
14395 #endif
14396 
14397     //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
14398     /**
14399        \param x X-coordinate of the pixel value.
14400        \param y Y-coordinate of the pixel value.
14401        \param z Z-coordinate of the pixel value.
14402        \param c C-coordinate of the pixel value.
14403        \note
14404        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
14405          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
14406        \par Example
14407        \code
14408        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image
14409        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10)
14410        const float val = img[off];              // Get the blue value of this pixel
14411        \endcode
14412     **/
14413     longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
14414       return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
14415     }
14416 
14417     //! Return a CImg<T>::iterator pointing to the first pixel value.
14418     /**
14419        \note
14420        - Equivalent to data().
14421        - It has been mainly defined for compatibility with STL naming conventions.
14422      **/
14423     iterator begin() {
14424       return _data;
14425     }
14426 
14427     //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
14428     const_iterator begin() const {
14429       return _data;
14430     }
14431 
14432     //! Return a CImg<T>::iterator pointing next to the last pixel value.
14433     /**
14434        \note
14435        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
14436        - It has been mainly defined for compatibility with STL naming conventions.
14437        \warning
14438        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
14439          Trying to read or write the content of the returned iterator will probably result in a crash.
14440          Use it mainly as a strict upper bound for a CImg<T>::iterator.
14441        \par Example
14442        \code
14443        CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image
14444        // 'img.end()' used below as an upper bound for the iterator.
14445        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
14446          *it = 0;
14447        \endcode
14448     **/
14449     iterator end() {
14450       return _data + size();
14451     }
14452 
14453     //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
14454     const_iterator end() const {
14455       return _data + size();
14456     }
14457 
14458     //! Return a reference to the first pixel value.
14459     /**
14460        \note
14461        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
14462        - It has been mainly defined for compatibility with STL naming conventions.
14463     **/
14464     T& front() {
14465       return *_data;
14466     }
14467 
14468     //! Return a reference to the first pixel value \const.
14469     const T& front() const {
14470       return *_data;
14471     }
14472 
14473     //! Return a reference to the last pixel value.
14474     /**
14475        \note
14476        - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
14477          <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
14478        - It has been mainly defined for compatibility with STL naming conventions.
14479     **/
14480     T& back() {
14481       return *(_data + size() - 1);
14482     }
14483 
14484     //! Return a reference to the last pixel value \const.
14485     const T& back() const {
14486       return *(_data + size() - 1);
14487     }
14488 
14489     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
14490     /**
14491        Return a reference to the pixel value of the image instance located at a specified \c offset,
14492        or to a specified default value in case of out-of-bounds access.
14493        \param offset Offset to the desired pixel value.
14494        \param out_value Default value returned if \c offset is outside image bounds.
14495        \note
14496        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
14497          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
14498          is safely returned instead.
14499        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14500          you are \e not sure about the validity of the specified pixel offset.
14501     **/
14502     T& at(const int offset, const T& out_value) {
14503       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
14504     }
14505 
14506     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
14507     T at(const int offset, const T& out_value) const {
14508       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
14509     }
14510 
14511     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
14512     /**
14513        Return a reference to the pixel value of the image instance located at a specified \c offset,
14514        or to the nearest pixel location in the image instance in case of out-of-bounds access.
14515        \param offset Offset to the desired pixel value.
14516        \note
14517        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
14518          nearest pixel in the image instance, regarding the specified offset, i.e.
14519          - If \c offset<0, then \c img[0] is returned.
14520          - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
14521        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14522          you are \e not sure about the validity of the specified pixel offset.
14523        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
14524      **/
14525     T& at(const int offset) {
14526       if (is_empty())
14527         throw CImgInstanceException(_cimg_instance
14528                                     "at(): Empty instance.",
14529                                     cimg_instance);
14530       return _at(offset);
14531     }
14532 
14533     T& _at(const int offset) {
14534       const unsigned int siz = (unsigned int)size();
14535       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
14536     }
14537 
14538     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
14539     const T& at(const int offset) const {
14540       if (is_empty())
14541         throw CImgInstanceException(_cimg_instance
14542                                     "at(): Empty instance.",
14543                                     cimg_instance);
14544       return _at(offset);
14545     }
14546 
14547     const T& _at(const int offset) const {
14548       const unsigned int siz = (unsigned int)size();
14549       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
14550     }
14551 
14552     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
14553     /**
14554        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
14555        or to a specified default value in case of out-of-bounds access along the X-axis.
14556        \param x X-coordinate of the pixel value.
14557        \param y Y-coordinate of the pixel value.
14558        \param z Z-coordinate of the pixel value.
14559        \param c C-coordinate of the pixel value.
14560        \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
14561        \note
14562        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
14563          \c out_value.
14564        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14565          you are \e not sure about the validity of the specified pixel coordinates.
14566        \warning
14567        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14568     **/
14569     T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
14570       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14571     }
14572 
14573     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
14574     T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
14575       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
14576     }
14577 
14578     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
14579     /**
14580        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
14581        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
14582        \param x X-coordinate of the pixel value.
14583        \param y Y-coordinate of the pixel value.
14584        \param z Z-coordinate of the pixel value.
14585        \param c C-coordinate of the pixel value.
14586        \note
14587        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
14588          nearest pixel in the image instance, regarding the specified X-coordinate.
14589        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14590          you are \e not sure about the validity of the specified pixel coordinates.
14591        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14592          \c _at(int,int,int,int).
14593        \warning
14594        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14595      **/
14596     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
14597       if (is_empty())
14598         throw CImgInstanceException(_cimg_instance
14599                                     "atX(): Empty instance.",
14600                                     cimg_instance);
14601       return _atX(x,y,z,c);
14602     }
14603 
14604     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
14605       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
14606     }
14607 
14608     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
14609     const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
14610       if (is_empty())
14611         throw CImgInstanceException(_cimg_instance
14612                                     "atX(): Empty instance.",
14613                                     cimg_instance);
14614       return _atX(x,y,z,c);
14615     }
14616 
14617     const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
14618       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
14619     }
14620 
14621     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
14622     /**
14623        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
14624     **/
14625     T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
14626       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14627     }
14628 
14629     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
14630     T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
14631       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
14632     }
14633 
14634     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
14635     /**
14636        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
14637        \note
14638        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14639          \c _atXY(int,int,int,int).
14640      **/
14641     T& atXY(const int x, const int y, const int z=0, const int c=0) {
14642       if (is_empty())
14643         throw CImgInstanceException(_cimg_instance
14644                                     "atXY(): Empty instance.",
14645                                     cimg_instance);
14646       return _atXY(x,y,z,c);
14647     }
14648 
14649     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
14650       return (*this)(cimg::cut(x,0,width() - 1),
14651                      cimg::cut(y,0,height() - 1),z,c);
14652     }
14653 
14654     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
14655     const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
14656       if (is_empty())
14657         throw CImgInstanceException(_cimg_instance
14658                                     "atXY(): Empty instance.",
14659                                     cimg_instance);
14660       return _atXY(x,y,z,c);
14661     }
14662 
14663     const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
14664       return (*this)(cimg::cut(x,0,width() - 1),
14665                      cimg::cut(y,0,height() - 1),z,c);
14666     }
14667 
14668     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
14669     /**
14670        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
14671        X,Y and Z-coordinates.
14672     **/
14673     T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
14674       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
14675         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14676     }
14677 
14678     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
14679     T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
14680       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
14681     }
14682 
14683     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
14684     /**
14685        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
14686        \note
14687        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14688          \c _atXYZ(int,int,int,int).
14689     **/
14690     T& atXYZ(const int x, const int y, const int z, const int c=0) {
14691       if (is_empty())
14692         throw CImgInstanceException(_cimg_instance
14693                                     "atXYZ(): Empty instance.",
14694                                     cimg_instance);
14695       return _atXYZ(x,y,z,c);
14696     }
14697 
14698     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
14699       return (*this)(cimg::cut(x,0,width() - 1),
14700                      cimg::cut(y,0,height() - 1),
14701                      cimg::cut(z,0,depth() - 1),c);
14702     }
14703 
14704     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
14705     const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
14706       if (is_empty())
14707         throw CImgInstanceException(_cimg_instance
14708                                     "atXYZ(): Empty instance.",
14709                                     cimg_instance);
14710       return _atXYZ(x,y,z,c);
14711     }
14712 
14713     const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
14714       return (*this)(cimg::cut(x,0,width() - 1),
14715                      cimg::cut(y,0,height() - 1),
14716                      cimg::cut(z,0,depth() - 1),c);
14717     }
14718 
14719     //! Access to a pixel value, using Dirichlet boundary conditions.
14720     /**
14721        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
14722        X,Y,Z and C-coordinates.
14723     **/
14724     T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
14725       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
14726         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14727     }
14728 
14729     //! Access to a pixel value, using Dirichlet boundary conditions \const.
14730     T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
14731       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
14732         (*this)(x,y,z,c);
14733     }
14734 
14735     //! Access to a pixel value, using Neumann boundary conditions.
14736     /**
14737        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
14738        \note
14739        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14740          \c _atXYZC(int,int,int,int).
14741     **/
14742     T& atXYZC(const int x, const int y, const int z, const int c) {
14743       if (is_empty())
14744         throw CImgInstanceException(_cimg_instance
14745                                     "atXYZC(): Empty instance.",
14746                                     cimg_instance);
14747       return _atXYZC(x,y,z,c);
14748     }
14749 
14750     T& _atXYZC(const int x, const int y, const int z, const int c) {
14751       return (*this)(cimg::cut(x,0,width() - 1),
14752                      cimg::cut(y,0,height() - 1),
14753                      cimg::cut(z,0,depth() - 1),
14754                      cimg::cut(c,0,spectrum() - 1));
14755     }
14756 
14757     //! Access to a pixel value, using Neumann boundary conditions \const.
14758     const T& atXYZC(const int x, const int y, const int z, const int c) const {
14759       if (is_empty())
14760         throw CImgInstanceException(_cimg_instance
14761                                     "atXYZC(): Empty instance.",
14762                                     cimg_instance);
14763       return _atXYZC(x,y,z,c);
14764     }
14765 
14766     const T& _atXYZC(const int x, const int y, const int z, const int c) const {
14767       return (*this)(cimg::cut(x,0,width() - 1),
14768                      cimg::cut(y,0,height() - 1),
14769                      cimg::cut(z,0,depth() - 1),
14770                      cimg::cut(c,0,spectrum() - 1));
14771     }
14772 
14773     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
14774     /**
14775        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
14776        or a specified default value in case of out-of-bounds access along the X-axis.
14777        \param fx X-coordinate of the pixel value (float-valued).
14778        \param y Y-coordinate of the pixel value.
14779        \param z Z-coordinate of the pixel value.
14780        \param c C-coordinate of the pixel value.
14781        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
14782        \note
14783        - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
14784          a linear interpolation along the X-axis, if corresponding coordinates are not integers.
14785        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
14786        \warning
14787        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14788     **/
14789     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
14790       const int
14791         x = (int)fx - (fx>=0?0:1), nx = x + 1;
14792       const float
14793         dx = fx - x;
14794       const Tfloat
14795         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
14796       return Ic + dx*(In - Ic);
14797     }
14798 
14799     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
14800     /**
14801        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
14802        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
14803        the X-axis.
14804        \param fx X-coordinate of the pixel value (float-valued).
14805        \param y Y-coordinate of the pixel value.
14806        \param z Z-coordinate of the pixel value.
14807        \param c C-coordinate of the pixel value.
14808        \note
14809        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
14810          the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
14811        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14812          \c _linear_atX(float,int,int,int).
14813        \warning
14814        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14815     **/
14816     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
14817       if (is_empty())
14818         throw CImgInstanceException(_cimg_instance
14819                                     "linear_atX(): Empty instance.",
14820                                     cimg_instance);
14821 
14822       return _linear_atX(fx,y,z,c);
14823     }
14824 
14825     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
14826       const float
14827         nfx = cimg::cut(fx,0,width() - 1);
14828       const unsigned int
14829         x = (unsigned int)nfx;
14830       const float
14831         dx = nfx - x;
14832       const unsigned int
14833         nx = dx>0?x + 1:x;
14834       const Tfloat
14835         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
14836       return Ic + dx*(In - Ic);
14837     }
14838 
14839     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate.
14840     Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
14841       if (is_empty())
14842         throw CImgInstanceException(_cimg_instance
14843                                     "linear_atX_p(): Empty instance.",
14844                                     cimg_instance);
14845 
14846       return _linear_atX_p(fx,y,z,c);
14847     }
14848 
14849     Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
14850       const float
14851         nfx = cimg::mod(fx,_width - 0.5f);
14852       const unsigned int
14853         x = (unsigned int)nfx;
14854       const float
14855         dx = nfx - x;
14856       const unsigned int
14857         nx = cimg::mod(x + 1,_width);
14858       const Tfloat
14859         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
14860       return Ic + dx*(In - Ic);
14861     }
14862 
14863     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
14864     /**
14865        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
14866        boundary checking are achieved both for X and Y-coordinates.
14867     **/
14868     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
14869       const int
14870         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14871         y = (int)fy - (fy>=0?0:1), ny = y + 1;
14872       const float
14873         dx = fx - x,
14874         dy = fy - y;
14875       const Tfloat
14876         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
14877         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
14878       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14879     }
14880 
14881     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
14882     /**
14883        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
14884        are achieved both for X and Y-coordinates.
14885        \note
14886        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14887          \c _linear_atXY(float,float,int,int).
14888     **/
14889     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
14890       if (is_empty())
14891         throw CImgInstanceException(_cimg_instance
14892                                     "linear_atXY(): Empty instance.",
14893                                     cimg_instance);
14894 
14895       return _linear_atXY(fx,fy,z,c);
14896     }
14897 
14898     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
14899       const float
14900         nfx = cimg::cut(fx,0,width() - 1),
14901         nfy = cimg::cut(fy,0,height() - 1);
14902       const unsigned int
14903         x = (unsigned int)nfx,
14904         y = (unsigned int)nfy;
14905       const float
14906         dx = nfx - x,
14907         dy = nfy - y;
14908       const unsigned int
14909         nx = dx>0?x + 1:x,
14910         ny = dy>0?y + 1:y;
14911       const Tfloat
14912         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
14913         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
14914       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14915     }
14916 
14917     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates.
14918     Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
14919       if (is_empty())
14920         throw CImgInstanceException(_cimg_instance
14921                                     "linear_atXY_p(): Empty instance.",
14922                                     cimg_instance);
14923 
14924       return _linear_atXY_p(fx,fy,z,c);
14925     }
14926 
14927     Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
14928       const float
14929         nfx = cimg::mod(fx,_width - 0.5f),
14930         nfy = cimg::mod(fy,_height - 0.5f);
14931       const unsigned int
14932         x = (unsigned int)nfx,
14933         y = (unsigned int)nfy;
14934       const float
14935         dx = nfx - x,
14936         dy = nfy - y;
14937       const unsigned int
14938         nx = cimg::mod(x + 1,_width),
14939         ny = cimg::mod(y + 1,_height);
14940       const Tfloat
14941         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
14942         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
14943       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14944     }
14945 
14946     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
14947     /**
14948        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
14949        boundary checking are achieved both for X,Y and Z-coordinates.
14950     **/
14951     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
14952       const int
14953         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14954         y = (int)fy - (fy>=0?0:1), ny = y + 1,
14955         z = (int)fz - (fz>=0?0:1), nz = z + 1;
14956       const float
14957         dx = fx - x,
14958         dy = fy - y,
14959         dz = fz - z;
14960       const Tfloat
14961         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
14962         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
14963         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
14964         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
14965       return Iccc +
14966         (Incc - Iccc +
14967          (Iccc + Innc - Icnc - Incc +
14968           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
14969          (Iccc + Incn - Iccn - Incc)*dz)*dx +
14970         (Icnc - Iccc +
14971          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
14972         (Iccn - Iccc)*dz;
14973     }
14974 
14975     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
14976     /**
14977        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
14978        are achieved both for X,Y and Z-coordinates.
14979        \note
14980        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14981          \c _linear_atXYZ(float,float,float,int).
14982     **/
14983     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
14984       if (is_empty())
14985         throw CImgInstanceException(_cimg_instance
14986                                     "linear_atXYZ(): Empty instance.",
14987                                     cimg_instance);
14988 
14989       return _linear_atXYZ(fx,fy,fz,c);
14990     }
14991 
14992     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
14993       const float
14994         nfx = cimg::cut(fx,0,width() - 1),
14995         nfy = cimg::cut(fy,0,height() - 1),
14996         nfz = cimg::cut(fz,0,depth() - 1);
14997       const unsigned int
14998         x = (unsigned int)nfx,
14999         y = (unsigned int)nfy,
15000         z = (unsigned int)nfz;
15001       const float
15002         dx = nfx - x,
15003         dy = nfy - y,
15004         dz = nfz - z;
15005       const unsigned int
15006         nx = dx>0?x + 1:x,
15007         ny = dy>0?y + 1:y,
15008         nz = dz>0?z + 1:z;
15009       const Tfloat
15010         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
15011         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
15012         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
15013         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
15014       return Iccc +
15015         (Incc - Iccc +
15016          (Iccc + Innc - Icnc - Incc +
15017           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
15018          (Iccc + Incn - Iccn - Incc)*dz)*dx +
15019         (Icnc - Iccc +
15020          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
15021         (Iccn - Iccc)*dz;
15022     }
15023 
15024     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates.
15025     Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
15026       if (is_empty())
15027         throw CImgInstanceException(_cimg_instance
15028                                     "linear_atXYZ_p(): Empty instance.",
15029                                     cimg_instance);
15030 
15031       return _linear_atXYZ_p(fx,fy,fz,c);
15032     }
15033 
15034     Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
15035       const float
15036         nfx = cimg::mod(fx,_width - 0.5f),
15037         nfy = cimg::mod(fy,_height - 0.5f),
15038         nfz = cimg::mod(fz,_depth - 0.5f);
15039       const unsigned int
15040         x = (unsigned int)nfx,
15041         y = (unsigned int)nfy,
15042         z = (unsigned int)nfz;
15043       const float
15044         dx = nfx - x,
15045         dy = nfy - y,
15046         dz = nfz - z;
15047       const unsigned int
15048         nx = cimg::mod(x + 1,_width),
15049         ny = cimg::mod(y + 1,_height),
15050         nz = cimg::mod(z + 1,_depth);
15051       const Tfloat
15052         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
15053         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
15054         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
15055         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
15056       return Iccc +
15057         (Incc - Iccc +
15058          (Iccc + Innc - Icnc - Incc +
15059           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
15060          (Iccc + Incn - Iccn - Incc)*dz)*dx +
15061         (Icnc - Iccc +
15062          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
15063         (Iccn - Iccc)*dz;
15064     }
15065 
15066     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
15067     /**
15068        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
15069        boundary checking are achieved for all X,Y,Z and C-coordinates.
15070     **/
15071     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
15072       const int
15073         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15074         y = (int)fy - (fy>=0?0:1), ny = y + 1,
15075         z = (int)fz - (fz>=0?0:1), nz = z + 1,
15076         c = (int)fc - (fc>=0?0:1), nc = c + 1;
15077       const float
15078         dx = fx - x,
15079         dy = fy - y,
15080         dz = fz - z,
15081         dc = fc - c;
15082       const Tfloat
15083         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
15084         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
15085         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
15086         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
15087         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
15088         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
15089         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
15090         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
15091       return Icccc +
15092         dx*(Inccc - Icccc +
15093             dy*(Icccc + Inncc - Icncc - Inccc +
15094                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15095                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15096                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15097                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15098             dz*(Icccc + Incnc - Iccnc - Inccc +
15099                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15100             dc*(Icccc + Inccn - Inccc - Icccn)) +
15101         dy*(Icncc - Icccc +
15102             dz*(Icccc + Icnnc - Iccnc - Icncc +
15103                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15104             dc*(Icccc + Icncn - Icncc - Icccn)) +
15105         dz*(Iccnc - Icccc +
15106             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15107         dc*(Icccn  -Icccc);
15108     }
15109 
15110     //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
15111     /**
15112        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
15113        are achieved for all X,Y,Z and C-coordinates.
15114        \note
15115        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15116          \c _linear_atXYZC(float,float,float,float).
15117     **/
15118     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15119       if (is_empty())
15120         throw CImgInstanceException(_cimg_instance
15121                                     "linear_atXYZC(): Empty instance.",
15122                                     cimg_instance);
15123 
15124       return _linear_atXYZC(fx,fy,fz,fc);
15125     }
15126 
15127     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15128       const float
15129         nfx = cimg::cut(fx,0,width() - 1),
15130         nfy = cimg::cut(fy,0,height() - 1),
15131         nfz = cimg::cut(fz,0,depth() - 1),
15132         nfc = cimg::cut(fc,0,spectrum() - 1);
15133       const unsigned int
15134         x = (unsigned int)nfx,
15135         y = (unsigned int)nfy,
15136         z = (unsigned int)nfz,
15137         c = (unsigned int)nfc;
15138       const float
15139         dx = nfx - x,
15140         dy = nfy - y,
15141         dz = nfz - z,
15142         dc = nfc - c;
15143       const unsigned int
15144         nx = dx>0?x + 1:x,
15145         ny = dy>0?y + 1:y,
15146         nz = dz>0?z + 1:z,
15147         nc = dc>0?c + 1:c;
15148       const Tfloat
15149         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
15150         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
15151         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
15152         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
15153         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
15154         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
15155         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
15156         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
15157       return Icccc +
15158         dx*(Inccc - Icccc +
15159             dy*(Icccc + Inncc - Icncc - Inccc +
15160                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15161                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15162                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15163                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15164             dz*(Icccc + Incnc - Iccnc - Inccc +
15165                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15166             dc*(Icccc + Inccn - Inccc - Icccn)) +
15167         dy*(Icncc - Icccc +
15168             dz*(Icccc + Icnnc - Iccnc - Icncc +
15169                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15170             dc*(Icccc + Icncn - Icncc - Icccn)) +
15171         dz*(Iccnc - Icccc +
15172             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15173         dc*(Icccn - Icccc);
15174     }
15175 
15176     //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates.
15177     Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15178       if (is_empty())
15179         throw CImgInstanceException(_cimg_instance
15180                                     "linear_atXYZC_p(): Empty instance.",
15181                                     cimg_instance);
15182 
15183       return _linear_atXYZC_p(fx,fy,fz,fc);
15184     }
15185 
15186     Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15187       const float
15188         nfx = cimg::mod(fx,_width - 0.5f),
15189         nfy = cimg::mod(fy,_height - 0.5f),
15190         nfz = cimg::mod(fz,_depth - 0.5f),
15191         nfc = cimg::mod(fc,_spectrum - 0.5f);
15192       const unsigned int
15193         x = (unsigned int)nfx,
15194         y = (unsigned int)nfy,
15195         z = (unsigned int)nfz,
15196         c = (unsigned int)nfc;
15197       const float
15198         dx = nfx - x,
15199         dy = nfy - y,
15200         dz = nfz - z,
15201         dc = nfc - c;
15202       const unsigned int
15203         nx = cimg::mod(x + 1,_width),
15204         ny = cimg::mod(y + 1,_height),
15205         nz = cimg::mod(z + 1,_depth),
15206         nc = cimg::mod(c + 1,_spectrum);
15207       const Tfloat
15208         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
15209         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
15210         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
15211         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
15212         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
15213         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
15214         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
15215         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
15216       return Icccc +
15217         dx*(Inccc - Icccc +
15218             dy*(Icccc + Inncc - Icncc - Inccc +
15219                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15220                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15221                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15222                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15223             dz*(Icccc + Incnc - Iccnc - Inccc +
15224                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15225             dc*(Icccc + Inccn - Inccc - Icccn)) +
15226         dy*(Icncc - Icccc +
15227             dz*(Icccc + Icnnc - Iccnc - Icncc +
15228                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15229             dc*(Icccc + Icncn - Icncc - Icccn)) +
15230         dz*(Iccnc - Icccc +
15231             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15232         dc*(Icccn - Icccc);
15233     }
15234 
15235     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
15236     /**
15237        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
15238        or a specified default value in case of out-of-bounds access along the X-axis.
15239        The cubic interpolation uses Hermite splines.
15240        \param fx d X-coordinate of the pixel value (float-valued).
15241        \param y Y-coordinate of the pixel value.
15242        \param z Z-coordinate of the pixel value.
15243        \param c C-coordinate of the pixel value.
15244        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
15245        \note
15246        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
15247          approximated by a \e cubic interpolation along the X-axis.
15248        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
15249        \warning
15250        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
15251     **/
15252     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
15253       const int
15254         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
15255       const float
15256         dx = fx - x;
15257       const Tfloat
15258         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
15259         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
15260       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));
15261     }
15262 
15263     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
15264     /**
15265        Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
15266        min/max range of the datatype \c T.
15267     **/
15268     T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const {
15269       return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
15270     }
15271 
15272     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
15273     /**
15274        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
15275        or the value of the nearest pixel location in the image instance in case of out-of-bounds access
15276        along the X-axis. The cubic interpolation uses Hermite splines.
15277        \param fx X-coordinate of the pixel value (float-valued).
15278        \param y Y-coordinate of the pixel value.
15279        \param z Z-coordinate of the pixel value.
15280        \param c C-coordinate of the pixel value.
15281        \note
15282        - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
15283          approximated by a cubic interpolation along the X-axis.
15284        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15285          \c _cubic_atX(float,int,int,int).
15286        \warning
15287        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
15288     **/
15289     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
15290       if (is_empty())
15291         throw CImgInstanceException(_cimg_instance
15292                                     "cubic_atX(): Empty instance.",
15293                                     cimg_instance);
15294       return _cubic_atX(fx,y,z,c);
15295     }
15296 
15297     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
15298       const float
15299         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1);
15300       const int
15301         x = (int)nfx;
15302       const float
15303         dx = nfx - x;
15304       const int
15305         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
15306       const Tfloat
15307         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
15308         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
15309       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));
15310     }
15311 
15312     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
15313     /**
15314        Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
15315        min/max range of the datatype \c T.
15316     **/
15317     T cubic_atX_c(const float fx, const int y, const int z, const int c) const {
15318       return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
15319     }
15320 
15321     T _cubic_atX_c(const float fx, const int y, const int z, const int c) const {
15322       return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
15323     }
15324 
15325     //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate.
15326     Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
15327       if (is_empty())
15328         throw CImgInstanceException(_cimg_instance
15329                                     "cubic_atX_p(): Empty instance.",
15330                                     cimg_instance);
15331       return _cubic_atX_p(fx,y,z,c);
15332     }
15333 
15334     Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
15335       const float
15336         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f);
15337       const int
15338         x = (int)nfx;
15339       const float
15340         dx = nfx - x;
15341       const int
15342         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width());
15343       const Tfloat
15344         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
15345         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
15346       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));
15347     }
15348 
15349     T cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
15350       return cimg::type<T>::cut(cubic_atX_p(fx,y,z,c));
15351     }
15352 
15353     T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
15354       return cimg::type<T>::cut(_cubic_atX_p(fx,y,z,c));
15355     }
15356 
15357     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
15358     /**
15359        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
15360        are achieved both for X and Y-coordinates.
15361     **/
15362     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
15363       const int
15364         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
15365         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
15366       const float dx = fx - x, dy = fy - y;
15367       const Tfloat
15368         Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
15369         Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
15370         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)),
15371         Ipc = (Tfloat)atXY(px,y,z,c,out_value),  Icc = (Tfloat)atXY(x, y,z,c,out_value),
15372         Inc = (Tfloat)atXY(nx,y,z,c,out_value),  Iac = (Tfloat)atXY(ax,y,z,c,out_value),
15373         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)),
15374         Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
15375         Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
15376         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)),
15377         Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
15378         Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
15379         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));
15380       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));
15381     }
15382 
15383     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
15384     /**
15385        Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
15386        min/max range of the datatype \c T.
15387     **/
15388     T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const {
15389       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
15390     }
15391 
15392     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
15393     /**
15394        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15395        are achieved for both X and Y-coordinates.
15396        \note
15397        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15398        \c _cubic_atXY(float,float,int,int).
15399     **/
15400     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
15401       if (is_empty())
15402         throw CImgInstanceException(_cimg_instance
15403                                     "cubic_atXY(): Empty instance.",
15404                                     cimg_instance);
15405       return _cubic_atXY(fx,fy,z,c);
15406     }
15407 
15408     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
15409       const float
15410         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
15411         nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1);
15412       const int x = (int)nfx, y = (int)nfy;
15413       const float dx = nfx - x, dy = nfy - y;
15414       const int
15415         px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2,
15416         py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2;
15417       const Tfloat
15418         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
15419         Iap = (Tfloat)(*this)(ax,py,z,c),
15420         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)),
15421         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
15422         Iac = (Tfloat)(*this)(ax,y,z,c),
15423         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)),
15424         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
15425         Ian = (Tfloat)(*this)(ax,ny,z,c),
15426         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)),
15427         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
15428         Iaa = (Tfloat)(*this)(ax,ay,z,c),
15429         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));
15430       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));
15431     }
15432 
15433     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
15434     /**
15435        Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
15436        min/max range of the datatype \c T.
15437     **/
15438     T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
15439       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
15440     }
15441 
15442     T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
15443       return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
15444     }
15445 
15446     //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates.
15447     Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
15448       if (is_empty())
15449         throw CImgInstanceException(_cimg_instance
15450                                     "cubic_atXY_p(): Empty instance.",
15451                                     cimg_instance);
15452       return _cubic_atXY_p(fx,fy,z,c);
15453     }
15454 
15455     Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
15456       const float
15457         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
15458         nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f);
15459       const int x = (int)nfx, y = (int)nfy;
15460       const float dx = nfx - x, dy = nfy - y;
15461       const int
15462         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
15463         py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height());
15464       const Tfloat
15465         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
15466         Iap = (Tfloat)(*this)(ax,py,z,c),
15467         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)),
15468         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
15469         Iac = (Tfloat)(*this)(ax,y,z,c),
15470         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)),
15471         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
15472         Ian = (Tfloat)(*this)(ax,ny,z,c),
15473         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)),
15474         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
15475         Iaa = (Tfloat)(*this)(ax,ay,z,c),
15476         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));
15477       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));
15478     }
15479 
15480     T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
15481       return cimg::type<T>::cut(cubic_atXY_p(fx,fy,z,c));
15482     }
15483 
15484     T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
15485       return cimg::type<T>::cut(_cubic_atXY_p(fx,fy,z,c));
15486     }
15487 
15488     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
15489     /**
15490        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
15491        are achieved both for X,Y and Z-coordinates.
15492     **/
15493     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
15494       const int
15495         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
15496         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
15497         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
15498       const float dx = fx - x, dy = fy - y, dz = fz - z;
15499       const Tfloat
15500         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
15501         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
15502         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15503                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15504         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
15505         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
15506         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15507                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15508         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
15509         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
15510         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15511                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15512         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
15513         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
15514         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15515                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15516         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15517                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15518         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
15519         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
15520         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15521                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15522         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
15523         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
15524         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15525                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15526         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
15527         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
15528         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15529                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15530         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
15531         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
15532         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15533                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15534         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15535                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15536         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
15537         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
15538         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15539                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15540         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
15541         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
15542         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15543                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15544         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
15545         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
15546         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15547                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15548         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
15549         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
15550         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15551                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15552         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15553                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15554         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
15555         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
15556         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15557                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15558         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
15559         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
15560         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15561                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15562         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
15563         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
15564         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15565                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15566         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
15567         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
15568         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15569                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15570         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15571                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15572       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));
15573     }
15574 
15575     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
15576     /**
15577        Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
15578        in the min/max range of the datatype \c T.
15579     **/
15580     T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
15581       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
15582     }
15583 
15584     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
15585     /**
15586        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15587        are achieved both for X,Y and Z-coordinates.
15588        \note
15589        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15590          \c _cubic_atXYZ(float,float,float,int).
15591     **/
15592     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
15593       if (is_empty())
15594         throw CImgInstanceException(_cimg_instance
15595                                     "cubic_atXYZ(): Empty instance.",
15596                                     cimg_instance);
15597       return _cubic_atXYZ(fx,fy,fz,c);
15598     }
15599 
15600     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
15601       const float
15602         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
15603         nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1),
15604         nfz = cimg::type<float>::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1);
15605       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
15606       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
15607       const int
15608         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
15609         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
15610         pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
15611       const Tfloat
15612         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
15613         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
15614         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15615                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15616         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
15617         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
15618         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15619                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15620         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
15621         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
15622         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15623                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15624         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
15625         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
15626         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15627                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15628         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15629                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15630         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
15631         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
15632         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15633                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15634         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
15635         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
15636         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15637                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15638         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
15639         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
15640         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15641                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15642         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
15643         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
15644         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15645                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15646         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15647                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15648         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
15649         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
15650         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15651                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15652         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
15653         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
15654         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15655                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15656         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
15657         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
15658         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15659                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15660         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
15661         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
15662         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15663                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15664         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15665                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15666         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
15667         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
15668         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15669                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15670         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
15671         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
15672         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15673                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15674         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
15675         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
15676         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15677                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15678         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
15679         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
15680         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15681                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15682         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15683                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15684       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));
15685     }
15686 
15687     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
15688     /**
15689        Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
15690        min/max range of the datatype \c T.
15691     **/
15692     T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
15693       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
15694     }
15695 
15696     T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
15697       return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
15698     }
15699 
15700     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
15701     /**
15702        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15703        are achieved both for X,Y and Z-coordinates.
15704        \note
15705        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15706          \c _cubic_atXYZ(float,float,float,int).
15707     **/
15708     Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
15709       if (is_empty())
15710         throw CImgInstanceException(_cimg_instance
15711                                     "cubic_atXYZ_p(): Empty instance.",
15712                                     cimg_instance);
15713       return _cubic_atXYZ_p(fx,fy,fz,c);
15714     }
15715 
15716     Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
15717       const float
15718         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
15719         nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f),
15720         nfz = cimg::type<float>::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f);
15721       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
15722       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
15723       const int
15724         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
15725         py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()),
15726         pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth());
15727       const Tfloat
15728         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
15729         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
15730         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15731                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15732         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
15733         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
15734         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15735                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15736         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
15737         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
15738         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15739                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15740         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
15741         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
15742         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15743                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15744         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15745                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15746         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
15747         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
15748         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15749                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15750         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
15751         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
15752         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15753                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15754         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
15755         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
15756         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15757                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15758         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
15759         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
15760         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15761                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15762         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15763                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15764         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
15765         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
15766         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15767                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15768         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
15769         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
15770         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15771                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15772         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
15773         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
15774         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15775                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15776         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
15777         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
15778         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15779                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15780         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15781                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15782         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
15783         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
15784         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15785                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15786         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
15787         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
15788         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15789                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15790         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
15791         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
15792         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15793                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15794         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
15795         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
15796         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15797                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15798         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15799                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15800       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));
15801     }
15802 
15803     T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
15804       return cimg::type<T>::cut(cubic_atXYZ_p(fx,fy,fz,c));
15805     }
15806 
15807     T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
15808       return cimg::type<T>::cut(_cubic_atXYZ_p(fx,fy,fz,c));
15809     }
15810 
15811     //! Set pixel value, using linear interpolation for the X-coordinates.
15812     /**
15813        Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
15814        the value is spread amongst several neighbors if the pixel coordinates are float-valued.
15815        \param value Pixel value to set.
15816        \param fx X-coordinate of the pixel value (float-valued).
15817        \param y Y-coordinate of the pixel value.
15818        \param z Z-coordinate of the pixel value.
15819        \param c C-coordinate of the pixel value.
15820        \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
15821          pixel(s).
15822        \return A reference to the current image instance.
15823        \note
15824        - Calling this method with out-of-bounds coordinates does nothing.
15825     **/
15826     CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
15827                             const bool is_added=false) {
15828       const int
15829         x = (int)fx - (fx>=0?0:1), nx = x + 1;
15830       const float
15831         dx = fx - x;
15832       if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
15833         if (x>=0 && x<width()) {
15834           const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
15835           (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15836         }
15837         if (nx>=0 && nx<width()) {
15838           const float w1 = dx, w2 = is_added?1:(1 - w1);
15839           (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15840         }
15841       }
15842       return *this;
15843     }
15844 
15845     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
15846     /**
15847        Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
15848        is achieved both for X and Y-coordinates.
15849     **/
15850     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
15851                              const bool is_added=false) {
15852       const int
15853         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15854         y = (int)fy - (fy>=0?0:1), ny = y + 1;
15855       const float
15856         dx = fx - x,
15857         dy = fy - y;
15858       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
15859         if (y>=0 && y<height()) {
15860           if (x>=0 && x<width()) {
15861             const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
15862             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15863           }
15864           if (nx>=0 && nx<width()) {
15865             const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
15866             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15867           }
15868         }
15869         if (ny>=0 && ny<height()) {
15870           if (x>=0 && x<width()) {
15871             const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
15872             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
15873           }
15874           if (nx>=0 && nx<width()) {
15875             const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
15876             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
15877           }
15878         }
15879       }
15880       return *this;
15881     }
15882 
15883     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
15884     /**
15885        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
15886        is achieved both for X,Y and Z-coordinates.
15887     **/
15888     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
15889                               const bool is_added=false) {
15890       const int
15891         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15892         y = (int)fy - (fy>=0?0:1), ny = y + 1,
15893         z = (int)fz - (fz>=0?0:1), nz = z + 1;
15894       const float
15895         dx = fx - x,
15896         dy = fy - y,
15897         dz = fz - z;
15898       if (c>=0 && c<spectrum()) {
15899         if (z>=0 && z<depth()) {
15900           if (y>=0 && y<height()) {
15901             if (x>=0 && x<width()) {
15902               const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
15903               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15904             }
15905             if (nx>=0 && nx<width()) {
15906               const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
15907               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15908             }
15909           }
15910           if (ny>=0 && ny<height()) {
15911             if (x>=0 && x<width()) {
15912               const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
15913               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
15914             }
15915             if (nx>=0 && nx<width()) {
15916               const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
15917               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
15918             }
15919           }
15920         }
15921         if (nz>=0 && nz<depth()) {
15922           if (y>=0 && y<height()) {
15923             if (x>=0 && x<width()) {
15924               const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
15925               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
15926             }
15927             if (nx>=0 && nx<width()) {
15928               const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
15929               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
15930             }
15931           }
15932           if (ny>=0 && ny<height()) {
15933             if (x>=0 && x<width()) {
15934               const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
15935               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
15936             }
15937             if (nx>=0 && nx<width()) {
15938               const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
15939               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
15940             }
15941           }
15942         }
15943       }
15944       return *this;
15945     }
15946 
15947     //! Return a C-string containing a list of all values of the image instance.
15948     /**
15949        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
15950        of the image instance (written in base 10), separated by specified \c separator character.
15951        \param separator A \c char character which specifies the separator between values in the returned C-string.
15952        \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
15953        \param format For float/double-values, tell the printf format used to generate the text representation
15954          of the numbers (or \c 0 for default representation).
15955        \note
15956        - The returned image is never empty.
15957        - For an empty image instance, the returned string is <tt>""</tt>.
15958        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
15959        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
15960          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
15961     **/
15962     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
15963                              const char *const format=0) const {
15964       if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
15965       CImgList<charT> items;
15966       CImg<charT> s_item(256); *s_item = 0;
15967       const T *ptrs = _data;
15968       unsigned int string_size = 0;
15969       const char *const _format = format?format:cimg::type<T>::format();
15970       for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
15971         const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
15972                                                              cimg::type<T>::format(*(ptrs++)));
15973         CImg<charT> item(s_item._data,printed_size);
15974         item[printed_size - 1] = separator;
15975         item.move_to(items);
15976         if (max_size) string_size+=printed_size;
15977       }
15978       CImg<charT> res;
15979       (items>'x').move_to(res);
15980       if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
15981       res.back() = 0;
15982       return res;
15983     }
15984 
15985     //@}
15986     //-------------------------------------
15987     //
15988     //! \name Instance Checking
15989     //@{
15990     //-------------------------------------
15991 
15992     //! Test shared state of the pixel buffer.
15993     /**
15994        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
15995        \note
15996        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
15997        - Most of the time, a \c CImg<T> image instance will \e not be shared.
15998        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
15999     **/
16000     bool is_shared() const {
16001       return _is_shared;
16002     }
16003 
16004     //! Test if image instance is empty.
16005     /**
16006        Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
16007        \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.
16008     **/
16009     bool is_empty() const {
16010       return !(_data && _width && _height && _depth && _spectrum);
16011     }
16012 
16013     //! Test if image instance contains a 'inf' value.
16014     /**
16015        Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
16016     **/
16017     bool is_inf() const {
16018       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
16019       return false;
16020     }
16021 
16022     //! Test if image instance contains a NaN value.
16023     /**
16024        Return \c true, if image instance contains a NaN value, and \c false otherwise.
16025     **/
16026     bool is_nan() const {
16027       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
16028       return false;
16029     }
16030 
16031     //! Test if image width is equal to specified value.
16032     bool is_sameX(const unsigned int size_x) const {
16033       return _width==size_x;
16034     }
16035 
16036     //! Test if image width is equal to specified value.
16037     template<typename t>
16038     bool is_sameX(const CImg<t>& img) const {
16039       return is_sameX(img._width);
16040     }
16041 
16042     //! Test if image width is equal to specified value.
16043     bool is_sameX(const CImgDisplay& disp) const {
16044       return is_sameX(disp._width);
16045     }
16046 
16047     //! Test if image height is equal to specified value.
16048     bool is_sameY(const unsigned int size_y) const {
16049       return _height==size_y;
16050     }
16051 
16052     //! Test if image height is equal to specified value.
16053     template<typename t>
16054     bool is_sameY(const CImg<t>& img) const {
16055       return is_sameY(img._height);
16056     }
16057 
16058     //! Test if image height is equal to specified value.
16059     bool is_sameY(const CImgDisplay& disp) const {
16060       return is_sameY(disp._height);
16061     }
16062 
16063     //! Test if image depth is equal to specified value.
16064     bool is_sameZ(const unsigned int size_z) const {
16065       return _depth==size_z;
16066     }
16067 
16068     //! Test if image depth is equal to specified value.
16069     template<typename t>
16070     bool is_sameZ(const CImg<t>& img) const {
16071       return is_sameZ(img._depth);
16072     }
16073 
16074     //! Test if image spectrum is equal to specified value.
16075     bool is_sameC(const unsigned int size_c) const {
16076       return _spectrum==size_c;
16077     }
16078 
16079     //! Test if image spectrum is equal to specified value.
16080     template<typename t>
16081     bool is_sameC(const CImg<t>& img) const {
16082       return is_sameC(img._spectrum);
16083     }
16084 
16085     //! Test if image width and height are equal to specified values.
16086     /**
16087        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
16088     **/
16089     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
16090       return _width==size_x && _height==size_y;
16091     }
16092 
16093     //! Test if image width and height are the same as that of another image.
16094     /**
16095        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
16096     **/
16097     template<typename t>
16098     bool is_sameXY(const CImg<t>& img) const {
16099       return is_sameXY(img._width,img._height);
16100     }
16101 
16102     //! Test if image width and height are the same as that of an existing display window.
16103     /**
16104        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
16105     **/
16106     bool is_sameXY(const CImgDisplay& disp) const {
16107       return is_sameXY(disp._width,disp._height);
16108     }
16109 
16110     //! Test if image width and depth are equal to specified values.
16111     /**
16112        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
16113     **/
16114     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
16115       return _width==size_x && _depth==size_z;
16116     }
16117 
16118     //! Test if image width and depth are the same as that of another image.
16119     /**
16120        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16121     **/
16122     template<typename t>
16123     bool is_sameXZ(const CImg<t>& img) const {
16124       return is_sameXZ(img._width,img._depth);
16125     }
16126 
16127     //! Test if image width and spectrum are equal to specified values.
16128     /**
16129        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
16130     **/
16131     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
16132       return _width==size_x && _spectrum==size_c;
16133     }
16134 
16135     //! Test if image width and spectrum are the same as that of another image.
16136     /**
16137        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16138     **/
16139     template<typename t>
16140     bool is_sameXC(const CImg<t>& img) const {
16141       return is_sameXC(img._width,img._spectrum);
16142     }
16143 
16144     //! Test if image height and depth are equal to specified values.
16145     /**
16146        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
16147     **/
16148     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
16149       return _height==size_y && _depth==size_z;
16150     }
16151 
16152     //! Test if image height and depth are the same as that of another image.
16153     /**
16154        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16155     **/
16156     template<typename t>
16157     bool is_sameYZ(const CImg<t>& img) const {
16158       return is_sameYZ(img._height,img._depth);
16159     }
16160 
16161     //! Test if image height and spectrum are equal to specified values.
16162     /**
16163        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
16164     **/
16165     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
16166       return _height==size_y && _spectrum==size_c;
16167     }
16168 
16169     //! Test if image height and spectrum are the same as that of another image.
16170     /**
16171        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16172     **/
16173     template<typename t>
16174     bool is_sameYC(const CImg<t>& img) const {
16175       return is_sameYC(img._height,img._spectrum);
16176     }
16177 
16178     //! Test if image depth and spectrum are equal to specified values.
16179     /**
16180        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
16181     **/
16182     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
16183       return _depth==size_z && _spectrum==size_c;
16184     }
16185 
16186     //! Test if image depth and spectrum are the same as that of another image.
16187     /**
16188        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16189     **/
16190     template<typename t>
16191     bool is_sameZC(const CImg<t>& img) const {
16192       return is_sameZC(img._depth,img._spectrum);
16193     }
16194 
16195     //! Test if image width, height and depth are equal to specified values.
16196     /**
16197        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
16198     **/
16199     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
16200       return is_sameXY(size_x,size_y) && _depth==size_z;
16201     }
16202 
16203     //! Test if image width, height and depth are the same as that of another image.
16204     /**
16205        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16206     **/
16207     template<typename t>
16208     bool is_sameXYZ(const CImg<t>& img) const {
16209       return is_sameXYZ(img._width,img._height,img._depth);
16210     }
16211 
16212     //! Test if image width, height and spectrum are equal to specified values.
16213     /**
16214        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16215     **/
16216     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
16217       return is_sameXY(size_x,size_y) && _spectrum==size_c;
16218     }
16219 
16220     //! Test if image width, height and spectrum are the same as that of another image.
16221     /**
16222        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16223     **/
16224     template<typename t>
16225     bool is_sameXYC(const CImg<t>& img) const {
16226       return is_sameXYC(img._width,img._height,img._spectrum);
16227     }
16228 
16229     //! Test if image width, depth and spectrum are equal to specified values.
16230     /**
16231        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16232     **/
16233     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
16234       return is_sameXZ(size_x,size_z) && _spectrum==size_c;
16235     }
16236 
16237     //! Test if image width, depth and spectrum are the same as that of another image.
16238     /**
16239        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16240     **/
16241     template<typename t>
16242     bool is_sameXZC(const CImg<t>& img) const {
16243       return is_sameXZC(img._width,img._depth,img._spectrum);
16244     }
16245 
16246     //! Test if image height, depth and spectrum are equal to specified values.
16247     /**
16248        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16249     **/
16250     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
16251       return is_sameYZ(size_y,size_z) && _spectrum==size_c;
16252     }
16253 
16254     //! Test if image height, depth and spectrum are the same as that of another image.
16255     /**
16256        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16257     **/
16258     template<typename t>
16259     bool is_sameYZC(const CImg<t>& img) const {
16260       return is_sameYZC(img._height,img._depth,img._spectrum);
16261     }
16262 
16263     //! Test if image width, height, depth and spectrum are equal to specified values.
16264     /**
16265        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
16266        verified.
16267     **/
16268     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
16269                      const unsigned int size_z, const unsigned int size_c) const {
16270       return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
16271     }
16272 
16273     //! Test if image width, height, depth and spectrum are the same as that of another image.
16274     /**
16275        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16276     **/
16277     template<typename t>
16278     bool is_sameXYZC(const CImg<t>& img) const {
16279       return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
16280     }
16281 
16282     //! Test if specified coordinates are inside image bounds.
16283     /**
16284        Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
16285        and \c false otherwise.
16286        \param x X-coordinate of the pixel value.
16287        \param y Y-coordinate of the pixel value.
16288        \param z Z-coordinate of the pixel value.
16289        \param c C-coordinate of the pixel value.
16290        \note
16291        - Return \c true only if all these conditions are verified:
16292          - The image instance is \e not empty.
16293          - <tt>0<=x<=\ref width() - 1</tt>.
16294          - <tt>0<=y<=\ref height() - 1</tt>.
16295          - <tt>0<=z<=\ref depth() - 1</tt>.
16296          - <tt>0<=c<=\ref spectrum() - 1</tt>.
16297     **/
16298     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
16299       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
16300     }
16301 
16302     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
16303     /**
16304        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
16305        and \c false otherwise.
16306        \param pixel Reference to pixel value to test.
16307        \param[out] x X-coordinate of the pixel value, if test succeeds.
16308        \param[out] y Y-coordinate of the pixel value, if test succeeds.
16309        \param[out] z Z-coordinate of the pixel value, if test succeeds.
16310        \param[out] c C-coordinate of the pixel value, if test succeeds.
16311        \note
16312        - Useful to convert an offset to a buffer value into pixel value coordinates:
16313        \code
16314        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image
16315        const unsigned long offset = 1249;       // Offset to the pixel (49,12,0,0)
16316        unsigned int x,y,z,c;
16317        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates
16318          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
16319                      offset,x,y,z,c);
16320        }
16321        \endcode
16322     **/
16323     template<typename t>
16324     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
16325       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
16326       const T *const ppixel = &pixel;
16327       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16328       ulongT off = (ulongT)(ppixel - _data);
16329       const ulongT nc = off/whd;
16330       off%=whd;
16331       const ulongT nz = off/wh;
16332       off%=wh;
16333       const ulongT ny = off/_width, nx = off%_width;
16334       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
16335       return true;
16336     }
16337 
16338     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
16339     /**
16340        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
16341     **/
16342     template<typename t>
16343     bool contains(const T& pixel, t& x, t& y, t& z) const {
16344       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
16345       const T *const ppixel = &pixel;
16346       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16347       ulongT off = ((ulongT)(ppixel - _data))%whd;
16348       const ulongT nz = off/wh;
16349       off%=wh;
16350       const ulongT ny = off/_width, nx = off%_width;
16351       x = (t)nx; y = (t)ny; z = (t)nz;
16352       return true;
16353     }
16354 
16355     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
16356     /**
16357        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
16358     **/
16359     template<typename t>
16360     bool contains(const T& pixel, t& x, t& y) const {
16361       const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
16362       const T *const ppixel = &pixel;
16363       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16364       ulongT off = ((unsigned int)(ppixel - _data))%wh;
16365       const ulongT ny = off/_width, nx = off%_width;
16366       x = (t)nx; y = (t)ny;
16367       return true;
16368     }
16369 
16370     //! Test if pixel value is inside image bounds and get its X-coordinate.
16371     /**
16372        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
16373     **/
16374     template<typename t>
16375     bool contains(const T& pixel, t& x) const {
16376       const T *const ppixel = &pixel;
16377       if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
16378       x = (t)(((ulongT)(ppixel - _data))%_width);
16379       return true;
16380     }
16381 
16382     //! Test if pixel value is inside image bounds.
16383     /**
16384        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
16385     **/
16386     bool contains(const T& pixel) const {
16387       const T *const ppixel = &pixel;
16388       return !is_empty() && ppixel>=_data && ppixel<_data + size();
16389     }
16390 
16391     //! Test if pixel buffers of instance and input images overlap.
16392     /**
16393        Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
16394        and \c false otherwise.
16395        \param img Input image to compare with.
16396        \note
16397        - Buffer overlapping may happen when manipulating \e shared images.
16398        - If two image buffers overlap, operating on one of the image will probably modify the other one.
16399        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
16400        \par Example
16401        \code
16402        const CImg<float>
16403          img1("reference.jpg"),             // Load RGB-color image
16404          img2 = img1.get_shared_channel(1); // Get shared version of the green channel
16405        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps
16406          std::printf("Buffers overlap!\n");
16407        }
16408        \endcode
16409     **/
16410     template<typename t>
16411     bool is_overlapped(const CImg<t>& img) const {
16412       const ulongT csiz = size(), isiz = img.size();
16413       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
16414     }
16415 
16416     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object.
16417     /**
16418        Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
16419        valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image.
16420        \param primitives List of primitives of the 3D object.
16421        \param colors List of colors of the 3D object.
16422        \param opacities List (or image) of opacities of the 3D object.
16423        \param full_check Tells if full checking of the 3D object must be performed.
16424        \param[out] error_message C-string to contain the error message, if the test does not succeed.
16425        \note
16426        - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of
16427          each 3D object component is checked.
16428        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
16429     **/
16430     template<typename tp, typename tc, typename to>
16431     bool is_object3d(const CImgList<tp>& primitives,
16432                      const CImgList<tc>& colors,
16433                      const to& opacities,
16434                      const bool full_check=true,
16435                      char *const error_message=0) const {
16436       if (error_message) *error_message = 0;
16437 
16438       // Check consistency for the particular case of an empty 3D object.
16439       if (is_empty()) {
16440         if (primitives || colors || opacities) {
16441           if (error_message) cimg_sprintf(error_message,
16442                                           "3D object (%u,%u) defines no vertices but %u primitives, "
16443                                           "%u colors and %lu opacities",
16444                                           _width,primitives._width,primitives._width,
16445                                           colors._width,(unsigned long)opacities.size());
16446           return false;
16447         }
16448         return true;
16449       }
16450 
16451       // Check consistency of vertices.
16452       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions
16453         if (error_message) cimg_sprintf(error_message,
16454                                         "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
16455                                         _width,primitives._width,_width,_height,_depth,_spectrum);
16456         return false;
16457       }
16458       if (colors._width>primitives._width + 1) {
16459         if (error_message) cimg_sprintf(error_message,
16460                                         "3D object (%u,%u) defines %u colors",
16461                                         _width,primitives._width,colors._width);
16462         return false;
16463       }
16464       if (opacities.size()>primitives._width) {
16465         if (error_message) cimg_sprintf(error_message,
16466                                         "3D object (%u,%u) defines %lu opacities",
16467                                         _width,primitives._width,(unsigned long)opacities.size());
16468         return false;
16469       }
16470       if (!full_check) return true;
16471 
16472       // Check consistency of primitives.
16473       cimglist_for(primitives,l) {
16474         const CImg<tp>& primitive = primitives[l];
16475         const unsigned int psiz = (unsigned int)primitive.size();
16476         switch (psiz) {
16477         case 1 : { // Point
16478           const unsigned int i0 = (unsigned int)primitive(0);
16479           if (i0>=_width) {
16480             if (error_message) cimg_sprintf(error_message,
16481                                             "3D object (%u,%u) refers to invalid vertex index %u in "
16482                                             "point primitive [%u]",
16483                                             _width,primitives._width,i0,l);
16484             return false;
16485           }
16486         } break;
16487         case 5 : { // Sphere
16488           const unsigned int
16489             i0 = (unsigned int)primitive(0),
16490             i1 = (unsigned int)primitive(1);
16491           if (i0>=_width || i1>=_width) {
16492             if (error_message) cimg_sprintf(error_message,
16493                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
16494                                             "sphere primitive [%u]",
16495                                             _width,primitives._width,i0,i1,l);
16496             return false;
16497           }
16498         } break;
16499         case 2 : case 6 : { // Segment
16500           const unsigned int
16501             i0 = (unsigned int)primitive(0),
16502             i1 = (unsigned int)primitive(1);
16503           if (i0>=_width || i1>=_width) {
16504             if (error_message) cimg_sprintf(error_message,
16505                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
16506                                             "segment primitive [%u]",
16507                                             _width,primitives._width,i0,i1,l);
16508             return false;
16509           }
16510         } break;
16511         case 3 : case 9 : { // Triangle
16512           const unsigned int
16513             i0 = (unsigned int)primitive(0),
16514             i1 = (unsigned int)primitive(1),
16515             i2 = (unsigned int)primitive(2);
16516           if (i0>=_width || i1>=_width || i2>=_width) {
16517             if (error_message) cimg_sprintf(error_message,
16518                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
16519                                             "triangle primitive [%u]",
16520                                             _width,primitives._width,i0,i1,i2,l);
16521             return false;
16522           }
16523         } break;
16524         case 4 : case 12 : { // Quadrangle
16525           const unsigned int
16526             i0 = (unsigned int)primitive(0),
16527             i1 = (unsigned int)primitive(1),
16528             i2 = (unsigned int)primitive(2),
16529             i3 = (unsigned int)primitive(3);
16530           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
16531             if (error_message) cimg_sprintf(error_message,
16532                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
16533                                             "quadrangle primitive [%u]",
16534                                             _width,primitives._width,i0,i1,i2,i3,l);
16535             return false;
16536           }
16537         } break;
16538         default :
16539           if (error_message) cimg_sprintf(error_message,
16540                                           "3D object (%u,%u) defines an invalid primitive [%u] of size %u",
16541                                           _width,primitives._width,l,(unsigned int)psiz);
16542           return false;
16543         }
16544       }
16545 
16546       // Check consistency of colors.
16547       cimglist_for(colors,c) {
16548         const CImg<tc>& color = colors[c];
16549         if (!color) {
16550           if (error_message) cimg_sprintf(error_message,
16551                                           "3D object (%u,%u) defines no color for primitive [%u]",
16552                                           _width,primitives._width,c);
16553           return false;
16554         }
16555       }
16556 
16557       // Check consistency of light texture.
16558       if (colors._width>primitives._width) {
16559         const CImg<tc> &light = colors.back();
16560         if (!light || light._depth>1) {
16561           if (error_message) cimg_sprintf(error_message,
16562                                           "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
16563                                           _width,primitives._width,light._width,
16564                                           light._height,light._depth,light._spectrum);
16565           return false;
16566         }
16567       }
16568 
16569       return true;
16570     }
16571 
16572     //! Test if image instance represents a valid serialization of a 3D object.
16573     /**
16574        Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise.
16575        \param full_check Tells if full checking of the instance must be performed.
16576        \param[out] error_message C-string to contain the error message, if the test does not succeed.
16577        \note
16578        - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of
16579          each 3D object component is checked.
16580        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
16581     **/
16582     bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
16583       if (error_message) *error_message = 0;
16584 
16585       // Check instance dimension and header.
16586       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
16587         if (error_message) cimg_sprintf(error_message,
16588                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
16589                                         _width,_height,_depth,_spectrum);
16590         return false;
16591       }
16592       const T *ptrs = _data, *const ptre = end();
16593       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
16594           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
16595         if (error_message) cimg_sprintf(error_message,
16596                                         "CImg3d header not found");
16597         return false;
16598       }
16599       const unsigned int
16600         nb_points = cimg::float2uint((float)*(ptrs++)),
16601         nb_primitives = cimg::float2uint((float)*(ptrs++));
16602 
16603       // Check consistency of number of vertices / primitives.
16604       if (!full_check) {
16605         const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
16606         if (_data + minimal_size>ptre) {
16607           if (error_message) cimg_sprintf(error_message,
16608                                           "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
16609                                           nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
16610           return false;
16611         }
16612       }
16613 
16614       // Check consistency of vertex data.
16615       if (!nb_points) {
16616         if (nb_primitives) {
16617           if (error_message) cimg_sprintf(error_message,
16618                                           "CImg3d (%u,%u) defines no vertices but %u primitives",
16619                                           nb_points,nb_primitives,nb_primitives);
16620           return false;
16621         }
16622         if (ptrs!=ptre) {
16623           if (error_message) cimg_sprintf(error_message,
16624                                           "CImg3d (%u,%u) is an empty object but contains %u value%s "
16625                                           "more than expected",
16626                                           nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
16627           return false;
16628         }
16629         return true;
16630       }
16631       if (ptrs + 3*nb_points>ptre) {
16632         if (error_message) cimg_sprintf(error_message,
16633                                         "CImg3d (%u,%u) defines only %u vertices data",
16634                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
16635         return false;
16636       }
16637       ptrs+=3*nb_points;
16638 
16639       // Check consistency of primitive data.
16640       if (ptrs==ptre) {
16641         if (error_message) cimg_sprintf(error_message,
16642                                         "CImg3d (%u,%u) defines %u vertices but no primitive",
16643                                         nb_points,nb_primitives,nb_points);
16644         return false;
16645       }
16646 
16647       if (!full_check) return true;
16648 
16649       for (unsigned int p = 0; p<nb_primitives; ++p) {
16650         const unsigned int nb_inds = (unsigned int)*(ptrs++);
16651         switch (nb_inds) {
16652         case 1 : { // Point
16653           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
16654           if (i0>=nb_points) {
16655             if (error_message) cimg_sprintf(error_message,
16656                                             "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]",
16657                                             nb_points,nb_primitives,i0,p);
16658             return false;
16659           }
16660         } break;
16661         case 5 : { // Sphere
16662           const unsigned int
16663             i0 = cimg::float2uint((float)*(ptrs++)),
16664             i1 = cimg::float2uint((float)*(ptrs++));
16665           ptrs+=3;
16666           if (i0>=nb_points || i1>=nb_points) {
16667             if (error_message) cimg_sprintf(error_message,
16668                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
16669                                             "sphere primitive [%u]",
16670                                             nb_points,nb_primitives,i0,i1,p);
16671             return false;
16672           }
16673         } break;
16674         case 2 : case 6 : { // Segment
16675           const unsigned int
16676             i0 = cimg::float2uint((float)*(ptrs++)),
16677             i1 = cimg::float2uint((float)*(ptrs++));
16678           if (nb_inds==6) ptrs+=4;
16679           if (i0>=nb_points || i1>=nb_points) {
16680             if (error_message) cimg_sprintf(error_message,
16681                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
16682                                             "segment primitive [%u]",
16683                                             nb_points,nb_primitives,i0,i1,p);
16684             return false;
16685           }
16686         } break;
16687         case 3 : case 9 : { // Triangle
16688           const unsigned int
16689             i0 = cimg::float2uint((float)*(ptrs++)),
16690             i1 = cimg::float2uint((float)*(ptrs++)),
16691             i2 = cimg::float2uint((float)*(ptrs++));
16692           if (nb_inds==9) ptrs+=6;
16693           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
16694             if (error_message) cimg_sprintf(error_message,
16695                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
16696                                             "triangle primitive [%u]",
16697                                             nb_points,nb_primitives,i0,i1,i2,p);
16698             return false;
16699           }
16700         } break;
16701         case 4 : case 12 : { // Quadrangle
16702           const unsigned int
16703             i0 = cimg::float2uint((float)*(ptrs++)),
16704             i1 = cimg::float2uint((float)*(ptrs++)),
16705             i2 = cimg::float2uint((float)*(ptrs++)),
16706             i3 = cimg::float2uint((float)*(ptrs++));
16707           if (nb_inds==12) ptrs+=8;
16708           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
16709             if (error_message) cimg_sprintf(error_message,
16710                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
16711                                             "quadrangle primitive [%u]",
16712                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
16713             return false;
16714           }
16715         } break;
16716         default :
16717           if (error_message) cimg_sprintf(error_message,
16718                                           "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
16719                                           nb_points,nb_primitives,p,nb_inds);
16720           return false;
16721         }
16722         if (ptrs>ptre) {
16723           if (error_message) cimg_sprintf(error_message,
16724                                           "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
16725                                           "%u values missing",
16726                                           nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
16727           return false;
16728         }
16729       }
16730 
16731       // Check consistency of color data.
16732       if (ptrs==ptre) {
16733         if (error_message) cimg_sprintf(error_message,
16734                                         "CImg3d (%u,%u) defines no color/texture data",
16735                                         nb_points,nb_primitives);
16736         return false;
16737       }
16738       for (unsigned int c = 0; c<nb_primitives; ++c) {
16739         if (*(ptrs++)!=(T)-128) ptrs+=2;
16740         else if ((ptrs+=3)<ptre) {
16741           const unsigned int
16742             w = (unsigned int)*(ptrs - 3),
16743             h = (unsigned int)*(ptrs - 2),
16744             s = (unsigned int)*(ptrs - 1);
16745           if (!h && !s) {
16746             if (w>=c) {
16747               if (error_message) cimg_sprintf(error_message,
16748                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u "
16749                                               "for primitive [%u]",
16750                                               nb_points,nb_primitives,w,c);
16751               return false;
16752             }
16753           } else ptrs+=w*h*s;
16754         }
16755         if (ptrs>ptre) {
16756           if (error_message) cimg_sprintf(error_message,
16757                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
16758                                           "%u values missing",
16759                                           nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
16760           return false;
16761         }
16762       }
16763 
16764       // Check consistency of opacity data.
16765       if (ptrs==ptre) {
16766         if (error_message) cimg_sprintf(error_message,
16767                                         "CImg3d (%u,%u) defines no opacity data",
16768                                         nb_points,nb_primitives);
16769         return false;
16770       }
16771       for (unsigned int o = 0; o<nb_primitives; ++o) {
16772         if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
16773           const unsigned int
16774             w = (unsigned int)*(ptrs - 3),
16775             h = (unsigned int)*(ptrs - 2),
16776             s = (unsigned int)*(ptrs - 1);
16777           if (!h && !s) {
16778             if (w>=o) {
16779               if (error_message) cimg_sprintf(error_message,
16780                                               "CImg3d (%u,%u) refers to invalid shared opacity index %u "
16781                                               "for primitive [%u]",
16782                                               nb_points,nb_primitives,w,o);
16783               return false;
16784             }
16785           } else ptrs+=w*h*s;
16786         }
16787         if (ptrs>ptre) {
16788           if (error_message) cimg_sprintf(error_message,
16789                                           "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
16790                                           nb_points,nb_primitives,o);
16791           return false;
16792         }
16793       }
16794 
16795       // Check end of data.
16796       if (ptrs<ptre) {
16797         if (error_message) cimg_sprintf(error_message,
16798                                         "CImg3d (%u,%u) contains %u value%s more than expected",
16799                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
16800         return false;
16801       }
16802       return true;
16803     }
16804 
16805     static bool _is_CImg3d(const T val, const char c) {
16806       return val>=(T)c && val<(T)(c + 1);
16807     }
16808 
16809     //@}
16810     //-------------------------------------
16811     //
16812     //! \name Mathematical Functions
16813     //@{
16814     //-------------------------------------
16815 
16816     // Define the math formula parser/compiler and expression evaluator.
16817     struct _cimg_math_parser {
16818       CImg<doubleT> mem;
16819       CImg<intT> memtype, memmerge;
16820       CImgList<ulongT> _code, &code, code_begin, code_end,
16821         _code_begin_t, &code_begin_t, _code_end_t, &code_end_t;
16822       CImg<ulongT> opcode;
16823       const CImg<ulongT> *p_code_end, *p_code;
16824       const CImg<ulongT> *const p_break;
16825 
16826       CImg<charT> expr, pexpr;
16827       const CImg<T>& imgin;
16828       const CImgList<T>& listin;
16829       CImg<T> &imgout;
16830       CImgList<T>& listout;
16831 
16832       CImg<doubleT> _img_stats, &img_stats, constcache_vals;
16833       CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm;
16834       CImg<uintT> mem_img_stats, constcache_inds;
16835 
16836       CImg<uintT> level, variable_pos, reserved_label;
16837       CImgList<charT> variable_def, macro_def, macro_body;
16838       CImgList<boolT> macro_body_is_string;
16839       char *user_macro;
16840 
16841       unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type,
16842         constcache_size;
16843       bool is_parallelizable, is_end_code, is_fill, return_new_comp, need_input_copy;
16844       double *result;
16845       cimg_uint64 rng;
16846       const char *const calling_function, *s_op, *ss_op;
16847       typedef double (*mp_func)(_cimg_math_parser&);
16848 
16849 #define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value?
16850 #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
16851 #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
16852 #define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)?
16853 #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
16854 #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
16855 #define _cimg_mp_calling_function s_calling_function()._data
16856 #define _cimg_mp_op(s) s_op = s; ss_op = ss
16857 #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
16858 #define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char)
16859 #define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char)
16860 #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
16861 #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char)
16862 #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
16863 #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
16864 #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
16865 #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val)))
16866 #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
16867 #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
16868 #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
16869 #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
16870 #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
16871 #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
16872 #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
16873 #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
16874 #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
16875 #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
16876 #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
16877 #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
16878 #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
16879 #define _cimg_mp_strerr \
16880   *se = saved_char; \
16881   for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \
16882   if (*s0==';') ++s0; \
16883   while (cimg::is_blank(*s0)) ++s0; \
16884   cimg::strellipsize(s0,64)
16885 
16886       // Constructors / Destructors.
16887       ~_cimg_math_parser() {
16888         cimg::srand(rng);
16889       }
16890 
16891       _cimg_math_parser(const char *const expression, const char *const funcname=0,
16892                         const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
16893                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0,
16894                         const bool _is_fill=false):
16895         code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
16896         p_break((CImg<ulongT>*)(cimg_ulong)-2),
16897         imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()),
16898         imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()),
16899         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0),
16900         mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0),
16901         constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false),
16902         rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") {
16903 
16904 #if cimg_use_openmp!=0
16905         rng+=omp_get_thread_num();
16906 #endif
16907         if (!expression || !*expression)
16908           throw CImgArgumentException("[" cimg_appname "_math_parser] "
16909                                       "CImg<%s>::%s: Empty expression.",
16910                                       pixel_type(),_cimg_mp_calling_function);
16911         const char *_expression = expression;
16912         while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression;
16913         CImg<charT>::string(_expression).move_to(expr);
16914         char *ps = &expr.back() - 1;
16915         while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps;
16916         *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
16917 
16918         // Ease the retrieval of previous non-space characters afterwards.
16919         pexpr.assign(expr._width);
16920         char c, *pe = pexpr._data;
16921         for (ps = expr._data, c = ' '; *ps; ++ps) {
16922           if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' ';
16923           *(pe++) = c;
16924         }
16925         *pe = 0;
16926         level = get_level(expr);
16927 
16928         // Init constant values.
16929 #define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0)
16930 #define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0)
16931 #define _cimg_mp_slot_t 17
16932 #define _cimg_mp_slot_nan 29
16933 #define _cimg_mp_slot_x 30
16934 #define _cimg_mp_slot_y 31
16935 #define _cimg_mp_slot_z 32
16936 #define _cimg_mp_slot_c 33
16937 
16938         mem.assign(96);
16939         for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
16940         for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
16941         mem[16] = 0.5;
16942         mem[_cimg_mp_slot_t] = 0; // thread_id
16943         mem[18] = (double)imgin._width; // w
16944         mem[19] = (double)imgin._height; // h
16945         mem[20] = (double)imgin._depth; // d
16946         mem[21] = (double)imgin._spectrum; // s
16947         mem[22] = (double)imgin._is_shared; // r
16948         mem[23] = (double)imgin._width*imgin._height; // wh
16949         mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
16950         mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
16951         mem[26] = (double)listin._width; // l
16952         mem[27] = std::exp(1.); // e
16953         mem[28] = cimg::PI; // pi
16954         mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
16955 
16956         // Set value property :
16957         // { -1 = reserved (e.g. variable) | 0 = computation value |
16958         //    1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
16959         memtype.assign(mem._width,1,1,1,0);
16960         for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
16961         memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] =
16962           memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1;
16963         mempos = _cimg_mp_slot_c + 1;
16964         variable_pos.assign(8);
16965 
16966         reserved_label.assign(128,1,1,1,~0U);
16967         // reserved_label[0-31] are used to store the memory index of these variables:
16968         // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv,
16969         // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM,
16970         // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary
16971 
16972         // Compile expression into a sequence of opcodes.
16973         s_op = ""; ss_op = expr._data;
16974         const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,0);
16975         if (!_cimg_mp_is_constant(ind_result)) {
16976           if (_cimg_mp_is_vector(ind_result))
16977             CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
16978               fill(cimg::type<double>::nan());
16979           else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type<double>::nan();
16980         }
16981 
16982         // Free resources used for compiling expression and prepare evaluation.
16983         result_dim = _cimg_mp_size(ind_result);
16984         if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
16985         result = mem._data + ind_result;
16986         memtype.assign();
16987         constcache_vals.assign();
16988         constcache_inds.assign();
16989         level.assign();
16990         variable_pos.assign();
16991         reserved_label.assign();
16992         expr.assign();
16993         pexpr.assign();
16994         opcode.assign();
16995         opcode._is_shared = true;
16996 
16997         // Execute begin() bloc if any specified.
16998         if (code_begin) {
16999           mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
17000           p_code_end = code_begin.end();
17001           for (p_code = code_begin; p_code<p_code_end; ++p_code) {
17002             opcode._data = p_code->_data;
17003             const ulongT target = opcode[1];
17004             mem[target] = _cimg_mp_defunc(*this);
17005           }
17006         }
17007         p_code_end = code.end();
17008       }
17009 
17010       _cimg_math_parser():
17011         code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
17012         p_code_end(0),p_break((CImg<ulongT>*)(cimg_ulong)-2),
17013         imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
17014         imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
17015         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0),
17016         result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),
17017         need_input_copy(false),rng(0),calling_function(0) {
17018         mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
17019         result = mem._data;
17020       }
17021 
17022       _cimg_math_parser(const _cimg_math_parser& mp):
17023         mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t),
17024         p_code_end(mp.p_code_end),p_break(mp.p_break),
17025         imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),
17026         img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm),
17027         debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0),
17028         is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),
17029         need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)),
17030         rng((cimg::_rand(),cimg::rng())),calling_function(0) {
17031 
17032 #if cimg_use_openmp!=0
17033         mem[_cimg_mp_slot_t] = (double)omp_get_thread_num();
17034         rng+=omp_get_thread_num();
17035 #endif
17036         opcode.assign();
17037         opcode._is_shared = true;
17038       }
17039 
17040       // Compilation procedure.
17041       unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
17042                            const unsigned char bloc_flags) {
17043         if (depth>256) {
17044           cimg::strellipsize(expr,64);
17045           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17046                                       "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
17047                                       "in expression '%s%s%s'.",
17048                                       pixel_type(),_cimg_mp_calling_function,
17049                                       (ss - 4)>expr._data?"...":"",
17050                                       (ss - 4)>expr._data?ss - 4:expr._data,
17051                                       se<&expr.back()?"...":"");
17052         }
17053         char c1, c2;
17054 
17055         // Simplify expression when possible.
17056         do {
17057           c2 = 0;
17058           if (ss<se) {
17059             while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';'
17060             while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';'
17061           }
17062           while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses
17063             ++ss; --se; c2 = 1;
17064           }
17065           if (*ss=='_' && ss + 1<se && ss[1]=='(') { // Remove leading '_(something)' comment.
17066             const unsigned int clevel = level[ss - expr._data];
17067             ss+=2;
17068             while (ss<se && (*ss!=')' || level[ss - expr._data]!=clevel)) ++ss;
17069             if (ss<se) ++ss;
17070             if (ss>=se) return _cimg_mp_slot_nan;
17071             c2 = 1;
17072           }
17073         } while (c2 && ss<se);
17074 
17075         if (se<=ss || !*ss) {
17076           cimg::strellipsize(expr,64);
17077           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17078                                       "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.",
17079                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
17080                                       *s_op=='F'?"argument":"item",
17081                                       (ss_op - 4)>expr._data?"...":"",
17082                                       (ss_op - 4)>expr._data?ss_op - 4:expr._data,
17083                                       ss_op + std::strlen(ss_op)<&expr.back()?"...":"");
17084         }
17085 
17086         static const size_t siz_ref = 7*sizeof(unsigned int);
17087         const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
17088         const unsigned int depth1 = depth + 1;
17089         unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
17090         char
17091           *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
17092           *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
17093           *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
17094           *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
17095         double val = 0, val1, val2;
17096         mp_func op;
17097         return_new_comp = false;
17098 
17099         // Bits of 'bloc_flags' tell about in which code bloc we currently are:
17100         // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t().
17101         const bool is_inside_critical = (bool)(bloc_flags&1);
17102 
17103         // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
17104         // linked to the returned memory slot (reference that cannot be determined at compile time).
17105         // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
17106         //                   3 = image value (coordinates) | 4 = image value as a vector (offsets) |
17107         //                   5 = image value as a vector (coordinates) }.
17108         // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
17109         // When p_ref[0]==0, p_ref is actually unlinked.
17110         // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
17111         // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
17112         // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
17113         // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
17114         // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
17115         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; }
17116 
17117         const char saved_char = *se; *se = 0;
17118         const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
17119         bool is_sth, is_relative;
17120         CImg<uintT> ref;
17121         CImg<charT> variable_name;
17122         CImgList<ulongT> l_opcode;
17123 
17124         // Look for a single value or a pre-defined variable.
17125         int nb = 0;
17126         s = ss + (*ss=='+' || *ss=='-'?1:0);
17127         if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
17128           is_sth = *ss=='-';
17129           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
17130           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
17131           if (nb==1 && is_sth) val = -val;
17132         } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number
17133           is_sth = *ss=='-';
17134           if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) {
17135             nb = 1;
17136             val = (double)arg1;
17137             if (is_sth) val = -val;
17138           }
17139         }
17140         if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
17141         if (nb==1) _cimg_mp_constant(val);
17142         if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
17143 
17144         if (ss1==se) switch (*ss) { // One-char reserved variable
17145           case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c);
17146           case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20);
17147           case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27);
17148           case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19);
17149           case 'k' :
17150             if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']);
17151             pos = get_mem_img_index();
17152             if (pos!=~0U) _cimg_mp_return(pos);
17153             _cimg_mp_return_nan();
17154           case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26);
17155           case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22);
17156           case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21);
17157           case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t);
17158           case 'n' :
17159             if (reserved_label[(int)'n']!=~0U) _cimg_mp_return(reserved_label[(int)'n']);
17160 #if cimg_use_openmp!=0
17161             _cimg_mp_constant((double)omp_get_max_threads());
17162 #else
17163             _cimg_mp_return(1);
17164 #endif
17165           case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18);
17166           case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x);
17167           case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y);
17168           case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z);
17169           case 'u' :
17170             if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']);
17171             _cimg_mp_scalar2(mp_u,0,1);
17172           case 'g' :
17173             if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']);
17174             _cimg_mp_scalar0(mp_g);
17175           case 'i' :
17176             if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']);
17177             _cimg_mp_scalar0(mp_i);
17178           case 'I' :
17179             _cimg_mp_op("Variable 'I'");
17180             if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']);
17181             if (!imgin._spectrum) _cimg_mp_return(0);
17182             need_input_copy = true;
17183             pos = vector(imgin._spectrum);
17184             CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
17185             return_new_comp = true;
17186             _cimg_mp_return(pos);
17187           case 'R' :
17188             if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']);
17189             need_input_copy = true;
17190             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
17191           case 'G' :
17192             if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']);
17193             need_input_copy = true;
17194             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
17195           case 'B' :
17196             if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']);
17197             need_input_copy = true;
17198             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
17199           case 'A' :
17200             if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']);
17201             need_input_copy = true;
17202             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
17203           }
17204         else if (ss2==se) { // Two-chars reserved variable
17205           arg1 = arg2 = ~0U;
17206           if (*ss=='w' && *ss1=='h') // wh
17207             _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
17208           if (*ss=='p' && *ss1=='i') // pi
17209             _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
17210           if (*ss=='i') {
17211             if (*ss1>='0' && *ss1<='9') { // i0...i9
17212               pos = 20 + *ss1 - '0';
17213               if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
17214               need_input_copy = true;
17215               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0);
17216             }
17217             switch (*ss1) {
17218             case 'm' : arg1 = 4; arg2 = 0; break; // im
17219             case 'M' : arg1 = 5; arg2 = 1; break; // iM
17220             case 'a' : arg1 = 6; arg2 = 2; break; // ia
17221             case 'v' : arg1 = 7; arg2 = 3; break; // iv
17222             case 's' : arg1 = 8; arg2 = 12; break; // is
17223             case 'p' : arg1 = 9; arg2 = 13; break; // ip
17224             case 'c' : // ic
17225               if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
17226               if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
17227               _cimg_mp_return(mem_img_median);
17228               break;
17229             case 'n' : // in
17230               if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
17231               if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0;
17232               _cimg_mp_return(mem_img_norm);
17233             }
17234           }
17235           else if (*ss1=='m') switch (*ss) {
17236             case 'x' : arg1 = 12; arg2 = 4; break; // xm
17237             case 'y' : arg1 = 13; arg2 = 5; break; // ym
17238             case 'z' : arg1 = 14; arg2 = 6; break; // zm
17239             case 'c' : arg1 = 15; arg2 = 7; break; // cm
17240             }
17241           else if (*ss1=='M') switch (*ss) {
17242             case 'x' : arg1 = 16; arg2 = 8; break; // xM
17243             case 'y' : arg1 = 17; arg2 = 9; break; // yM
17244             case 'z' : arg1 = 18; arg2 = 10; break; // zM
17245             case 'c' : arg1 = 19; arg2 = 11; break; // cM
17246             }
17247           if (arg1!=~0U) {
17248             if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
17249             if (!img_stats) {
17250               img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
17251               mem_img_stats.assign(1,14,1,1,~0U);
17252             }
17253             if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
17254             _cimg_mp_return(mem_img_stats[arg2]);
17255           }
17256         } else if (ss3==se) { // Three-chars reserved variable
17257           if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
17258             _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
17259         } else if (ss4==se) { // Four-chars reserved variable
17260           if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
17261             _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
17262         }
17263 
17264         pos = ~0U;
17265         is_sth = false;
17266 
17267         for (s0 = ss, s = ss1; s<se1; ++s)
17268           if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
17269             is_end_code = false;
17270             arg1 = compile(s0,s++,depth,0,bloc_flags);
17271             if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17272             is_sth = true;
17273             while (*s && (cimg::is_blank(*s) || *s==';')) ++s;
17274             s0 = s;
17275           }
17276         if (is_sth) {
17277           is_end_code = false;
17278           arg1 = compile(s0,se,depth,p_ref,bloc_flags);
17279           if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17280           _cimg_mp_return(pos!=~0U?pos:_cimg_mp_slot_nan);
17281         }
17282 
17283         // Declare / assign variable, vector value or image value.
17284         for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
17285           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
17286               *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
17287               *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
17288               level[s - expr._data]==clevel) {
17289             variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
17290             cimg::strpare(variable_name,false,true);
17291             const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
17292             char *const ve1 = ss + l_variable_name - 1;
17293             _cimg_mp_op("Operator '='");
17294 
17295             // Assign image value (direct).
17296             if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
17297                 (reserved_label[(int)*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[(int)*ss]))) {
17298               is_relative = *ss=='j' || *ss=='J';
17299 
17300               if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
17301                 if (!is_inside_critical) is_parallelizable = false;
17302                 if (*ss2=='#') { // Index specified
17303                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17304                   p1 = compile(ss3,s0++,depth1,0,bloc_flags);
17305                   _cimg_mp_check_list(true);
17306                 } else { p1 = ~0U; s0 = ss2; }
17307                 arg1 = compile(s0,ve1,depth1,0,bloc_flags); // Offset
17308                 _cimg_mp_check_type(arg1,0,1,0);
17309                 arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17310                 _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,0);
17311                 if (_cimg_mp_is_vector(arg2)) {
17312                   if (p1!=~0U) {
17313                     _cimg_mp_check_constant_index(p1);
17314                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17315                     p2 = listin[p3]._spectrum;
17316                   } else p2 = imgin._spectrum;
17317                   if (!p2) _cimg_mp_return(0);
17318                   _cimg_mp_check_type(arg2,2,2,p2);
17319                 } else p2 = 0;
17320 
17321                 if (p_ref) {
17322                   *p_ref = _cimg_mp_is_vector(arg2)?4:2;
17323                   p_ref[1] = p1;
17324                   p_ref[2] = (unsigned int)is_relative;
17325                   p_ref[3] = arg1;
17326                   if (_cimg_mp_is_vector(arg2))
17327                     set_reserved_vector(arg2); // Prevent from being used in further optimization
17328                   else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17329                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
17330                 }
17331 
17332                 if (p1!=~0U) {
17333                   if (!listout) _cimg_mp_return(arg2);
17334                   if (*ss>='i')
17335                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17336                                         arg2,p1,arg1).move_to(code);
17337                   else if (_cimg_mp_is_scalar(arg2))
17338                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17339                                         arg2,p1,arg1).move_to(code);
17340                   else
17341                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17342                                         arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
17343                 } else {
17344                   if (!imgout) _cimg_mp_return(arg2);
17345                   if (*ss>='i')
17346                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17347                                         arg2,arg1).move_to(code);
17348                   else if (_cimg_mp_is_scalar(arg2))
17349                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17350                                         arg2,arg1).move_to(code);
17351                   else
17352                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17353                                         arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
17354                 }
17355                 _cimg_mp_return(arg2);
17356               }
17357 
17358               if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
17359                 if (!is_inside_critical) is_parallelizable = false;
17360                 if (*ss2=='#') { // Index specified
17361                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17362                   p1 = compile(ss3,s0++,depth1,0,bloc_flags);
17363                   _cimg_mp_check_list(true);
17364                 } else { p1 = ~0U; s0 = ss2; }
17365                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17366                 arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17367                 arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17368                 arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17369                 arg5 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17370                 _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,0);
17371                 if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
17372                   s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17373                   arg1 = compile(s0,s1,depth1,0,bloc_flags);
17374                   if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17375                     p2 = _cimg_mp_size(arg1); // Vector size
17376                     ++arg1;
17377                     if (p2>1) {
17378                       arg2 = arg1 + 1;
17379                       if (p2>2) {
17380                         arg3 = arg2 + 1;
17381                         if (p2>3) arg4 = arg3 + 1;
17382                       }
17383                     }
17384                   } else if (s1<ve1) { // Y
17385                     s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17386                     arg2 = compile(s1,s2,depth1,0,bloc_flags);
17387                     if (s2<ve1) { // Z
17388                       s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17389                       arg3 = compile(s2,s3,depth1,0,bloc_flags);
17390                       if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,bloc_flags); // C
17391                     }
17392                   }
17393                 }
17394 
17395                 if (_cimg_mp_is_vector(arg5)) {
17396                   if (p1!=~0U) {
17397                     _cimg_mp_check_constant_index(p1);
17398                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17399                     p2 = listin[p3]._spectrum;
17400                   } else p2 = imgin._spectrum;
17401                   if (!p2) _cimg_mp_return(0);
17402                   _cimg_mp_check_type(arg5,2,2,p2);
17403                 } else p2 = 0;
17404 
17405 
17406                 if (p_ref) {
17407                   *p_ref = _cimg_mp_is_vector(arg5)?5:3;
17408                   p_ref[1] = p1;
17409                   p_ref[2] = (unsigned int)is_relative;
17410                   p_ref[3] = arg1;
17411                   p_ref[4] = arg2;
17412                   p_ref[5] = arg3;
17413                   p_ref[6] = arg4;
17414                   if (_cimg_mp_is_vector(arg5))
17415                     set_reserved_vector(arg5); // Prevent from being used in further optimization
17416                   else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -1;
17417                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1;
17418                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
17419                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17420                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
17421                   if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
17422                 }
17423                 if (p1!=~0U) {
17424                   if (!listout) _cimg_mp_return(arg5);
17425                   if (*ss>='i')
17426                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17427                                         arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
17428                   else if (_cimg_mp_is_scalar(arg5))
17429                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17430                                         arg5,p1,arg1,arg2,arg3).move_to(code);
17431                   else
17432                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17433                                         arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17434                 } else {
17435                   if (!imgout) _cimg_mp_return(arg5);
17436                   if (*ss>='i')
17437                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17438                                         arg5,arg1,arg2,arg3,arg4).move_to(code);
17439                   else if (_cimg_mp_is_scalar(arg5))
17440                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17441                                         arg5,arg1,arg2,arg3).move_to(code);
17442                   else
17443                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17444                                         arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17445                 }
17446                 _cimg_mp_return(arg5);
17447               }
17448             }
17449 
17450             // Assign vector value (direct).
17451             if (l_variable_name>3 && *ve1==']' && *ss!='[') {
17452               s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
17453               if (s0>ss && is_varname(ss,s0 - ss)) {
17454                 variable_name[s0 - ss] = 0; // Remove brackets in variable name
17455                 get_variable_pos(variable_name,arg1,arg2);
17456                 arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot
17457                 if (arg1==~0U || _cimg_mp_is_scalar(arg1))
17458                   compile(ss,s0,depth1,0,bloc_flags); // Variable does not exist or is not a vector -> error
17459 
17460                 arg2 = compile(++s0,ve1,depth1,0,bloc_flags); // Index
17461                 arg3 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17462                 _cimg_mp_check_type(arg3,2,1,0);
17463 
17464                 if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly
17465                   nb = (int)mem[arg2];
17466                   if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
17467                     arg1+=nb + 1;
17468                     CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17469                     _cimg_mp_return(arg1);
17470                   }
17471                   compile(ss,s,depth1,0,bloc_flags); // Out-of-bounds reference -> error
17472                 }
17473 
17474                 // Case of non-constant index -> return assigned value + linked reference
17475                 if (p_ref) {
17476                   *p_ref = 1;
17477                   p_ref[1] = arg1;
17478                   p_ref[2] = arg2;
17479                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization
17480                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17481                 }
17482                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2).
17483                   move_to(code);
17484                 _cimg_mp_return(arg3);
17485               }
17486             }
17487 
17488             // Assign user-defined macro.
17489             if (l_variable_name>2 && *ve1==')' && *ss!='(') {
17490               s0 = ve1; while (s0>ss && *s0!='(') --s0;
17491               if (is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) &&
17492                   std::strncmp(variable_name,"print(",6)) { // Valid macro name
17493                 s0 = variable_name._data + (s0 - ss);
17494                 *s0 = 0;
17495                 s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
17496                 CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
17497                 ++s; while (*s && cimg::is_blank(*s)) ++s;
17498                 CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
17499 
17500                 p1 = 1; // Index of current parsed argument
17501                 for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
17502                   if (p1>24) {
17503                     _cimg_mp_strerr;
17504                     cimg::strellipsize(variable_name,64);
17505                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17506                                                 "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
17507                                                 "definition '%s()', in expression '%s%s%s'.",
17508                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17509                                                 variable_name._data,
17510                                                 s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17511                   }
17512                   while (*s && cimg::is_blank(*s)) ++s;
17513                   if (*s==')' && p1==1) break; // Function has no arguments
17514 
17515                   s2 = s; // Start of the argument name
17516                   is_sth = true; // is_valid_argument_name?
17517                   if (*s>='0' && *s<='9') is_sth = false;
17518                   else for (ns = s; ns<s1 && *ns!=',' && !cimg::is_blank(*ns); ++ns)
17519                          if (!is_varchar(*ns)) { is_sth = false; break; }
17520                   s3 = ns; // End of the argument name
17521                   while (*ns && cimg::is_blank(*ns)) ++ns;
17522                   if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
17523                     _cimg_mp_strerr;
17524                     cimg::strellipsize(variable_name,64);
17525                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17526                                                 "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
17527                                                 "macro '%s()', in expression '%s%s%s'.",
17528                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17529                                                 is_sth?"Empty":"Invalid",p1,
17530                                                 variable_name._data,
17531                                                 s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17532                   }
17533                   if (ns==s1 || *ns==',') { // New argument found
17534                     *s3 = 0;
17535                     p2 = (unsigned int)(s3 - s2); // Argument length
17536                     for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
17537                       if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
17538                             (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
17539                         if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
17540                           *(ps - 1) = (char)p1;
17541                           if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
17542                             std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
17543                             macro_body[0]._width-=p2 + 1;
17544                           } else { // Has pre number sign only
17545                             std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17546                             macro_body[0]._width-=p2;
17547                           }
17548                         } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
17549                           *(ps++) = (char)p1;
17550                           std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17551                           macro_body[0]._width-=p2;
17552                         } else { // Not near a number sign
17553                           if (p2<3) {
17554                             ps-=(ulongT)macro_body[0]._data;
17555                             macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
17556                             ps+=(ulongT)macro_body[0]._data;
17557                           } else macro_body[0]._width-=p2 - 3;
17558                           std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
17559                           *(ps++) = '(';
17560                           *(ps++) = (char)p1;
17561                           *(ps++) = ')';
17562                         }
17563                       } else ++ps;
17564                     }
17565                   }
17566                 }
17567 
17568                 // Store number of arguments.
17569                 macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
17570 
17571                 // Detect parts of function body inside a string.
17572                 is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
17573                 _cimg_mp_return_nan();
17574               }
17575             }
17576 
17577             // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
17578             const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
17579             s0 = variable_name._data;
17580             if (is_const) {
17581               s0+=6; while (cimg::is_blank(*s0)) ++s0;
17582               variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
17583             }
17584             if (is_varname(variable_name)) { // Valid variable name
17585 
17586               // Assign variable (direct).
17587               get_variable_pos(variable_name,arg1,arg2);
17588               arg3 = compile(s + 1,se,depth1,0,bloc_flags);
17589               is_sth = return_new_comp; // is arg3 a new blank object?
17590               if (is_const) _cimg_mp_check_constant(arg3,2,0);
17591               arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
17592 
17593               if (arg1==~0U) { // Create new variable
17594                 if (_cimg_mp_is_vector(arg3)) { // Vector variable
17595                   arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3);
17596                   set_reserved_vector(arg1); // Prevent from being used in further optimization
17597                 } else { // Scalar variable
17598                   if (is_const) arg1 = arg3;
17599                   else {
17600                     arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3);
17601                     memtype[arg1] = -1;
17602                   }
17603                 }
17604 
17605                 if (arg2!=~0U) reserved_label[arg2] = arg1;
17606                   else {
17607                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
17608                     variable_pos[variable_def._width] = arg1;
17609                     variable_name.move_to(variable_def);
17610                   }
17611 
17612               } else { // Variable already exists -> assign a new value
17613                 if (is_const || _cimg_mp_is_constant(arg1)) {
17614                   _cimg_mp_strerr;
17615                   cimg::strellipsize(variable_name,64);
17616                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
17617                                               "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
17618                                               "in expression '%s%s%s'.",
17619                                               pixel_type(),_cimg_mp_calling_function,s_op,
17620                                               _cimg_mp_is_constant(arg1)?"already-defined ":"non-",
17621                                               variable_name._data,
17622                                               !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"",
17623                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17624                 }
17625                 _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
17626                 if (_cimg_mp_is_vector(arg1)) { // Vector
17627                   if (_cimg_mp_is_vector(arg3)) // From vector
17628                     CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)).
17629                       move_to(code);
17630                   else // From scalar
17631                     CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3).
17632                       move_to(code);
17633                 } else // Scalar
17634                   CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17635               }
17636               return_new_comp = false;
17637               _cimg_mp_return(arg1);
17638             }
17639 
17640             // Assign lvalue (variable name was not valid for a direct assignment).
17641             arg1 = ~0U;
17642             is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
17643             if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
17644 
17645             if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
17646               ref.assign(7);
17647               arg1 = compile(ss,s,depth1,ref,bloc_flags); // Lvalue slot
17648               arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17649 
17650               if (*ref==1) { // Vector value (scalar): V[k] = scalar
17651                 _cimg_mp_check_type(arg2,2,1,0);
17652                 arg3 = ref[1]; // Vector slot
17653                 arg4 = ref[2]; // Index
17654                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17655                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17656                   move_to(code);
17657                 _cimg_mp_return(arg2);
17658               }
17659 
17660               if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
17661                 if (!is_inside_critical) is_parallelizable = false;
17662                 _cimg_mp_check_type(arg2,2,1,0);
17663                 p1 = ref[1]; // Index
17664                 is_relative = (bool)ref[2];
17665                 arg3 = ref[3]; // Offset
17666                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17667                 if (p1!=~0U) {
17668                   if (!listout) _cimg_mp_return(arg2);
17669                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17670                                       arg2,p1,arg3).move_to(code);
17671                 } else {
17672                   if (!imgout) _cimg_mp_return(arg2);
17673                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17674                                       arg2,arg3).move_to(code);
17675                 }
17676                 _cimg_mp_return(arg2);
17677               }
17678 
17679               if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
17680                 if (!is_inside_critical) is_parallelizable = false;
17681                 _cimg_mp_check_type(arg2,2,1,0);
17682                 p1 = ref[1]; // Index
17683                 is_relative = (bool)ref[2];
17684                 arg3 = ref[3]; // X
17685                 arg4 = ref[4]; // Y
17686                 arg5 = ref[5]; // Z
17687                 arg6 = ref[6]; // C
17688                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17689                 if (p1!=~0U) {
17690                   if (!listout) _cimg_mp_return(arg2);
17691                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17692                                       arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
17693                 } else {
17694                   if (!imgout) _cimg_mp_return(arg2);
17695                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17696                                       arg2,arg3,arg4,arg5,arg6).move_to(code);
17697                 }
17698                 _cimg_mp_return(arg2);
17699               }
17700 
17701               if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
17702                 if (!is_inside_critical) is_parallelizable = false;
17703                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17704                 p1 = ref[1]; // Index
17705                 is_relative = (bool)ref[2];
17706                 arg3 = ref[3]; // Offset
17707                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17708                 if (p1!=~0U) {
17709                   if (!listout) _cimg_mp_return(arg2);
17710                   if (_cimg_mp_is_scalar(arg2))
17711                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17712                                          arg2,p1,arg3).move_to(code);
17713                   else {
17714                     _cimg_mp_check_constant_index(p1);
17715                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17716                                          arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
17717                   }
17718 
17719                 } else {
17720                   if (!imgout) _cimg_mp_return(arg2);
17721                   if (_cimg_mp_is_scalar(arg2))
17722                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17723                                         arg2,arg3).move_to(code);
17724                   else
17725                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17726                                         arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
17727                 }
17728                 _cimg_mp_return(arg2);
17729               }
17730 
17731               if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
17732                 if (!is_inside_critical) is_parallelizable = false;
17733                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17734                 p1 = ref[1]; // Index
17735                 is_relative = (bool)ref[2];
17736                 arg3 = ref[3]; // X
17737                 arg4 = ref[4]; // Y
17738                 arg5 = ref[5]; // Z
17739                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17740                 if (p1!=~0U) {
17741                   if (!listout) _cimg_mp_return(arg2);
17742                   if (_cimg_mp_is_scalar(arg2))
17743                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17744                                         arg2,p1,arg3,arg4,arg5).move_to(code);
17745                   else {
17746                     _cimg_mp_check_constant_index(p1);
17747                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17748                                          arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17749                   }
17750 
17751                 } else {
17752                   if (!imgout) _cimg_mp_return(arg2);
17753                   if (_cimg_mp_is_scalar(arg2))
17754                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17755                                         arg2,arg3,arg4,arg5).move_to(code);
17756                   else
17757                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17758                                         arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17759                 }
17760                 _cimg_mp_return(arg2);
17761               }
17762 
17763               if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
17764                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17765                 if (_cimg_mp_is_vector(arg2)) // From vector
17766                   CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
17767                     move_to(code);
17768                 else // From scalar
17769                   CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
17770                     move_to(code);
17771                 _cimg_mp_return(arg1);
17772               }
17773 
17774               if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar
17775                 _cimg_mp_check_type(arg2,2,1,0);
17776                 CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
17777                 _cimg_mp_return(arg1);
17778               }
17779             }
17780 
17781             // No assignment expressions match -> error
17782             _cimg_mp_strerr;
17783             cimg::strellipsize(variable_name,64);
17784             throw CImgArgumentException("[" cimg_appname "_math_parser] "
17785                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17786                                         "in expression '%s%s%s'.",
17787                                         pixel_type(),_cimg_mp_calling_function,s_op,
17788                                         arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"",
17789                                         variable_name._data,
17790                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17791           }
17792 
17793         // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
17794         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17795           if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
17796               level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
17797             _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
17798 
17799             ref.assign(7);
17800             arg1 = compile(ss,ns,depth1,ref,bloc_flags); // Vector slot
17801             arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Right operand
17802             _cimg_mp_check_type(arg1,1,2,2);
17803             _cimg_mp_check_type(arg2,2,3,2);
17804             if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
17805               if (*ps=='*')
17806                 CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
17807               else if (*ps=='/')
17808                 CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
17809               else
17810                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
17811             } else { // Complex **= scalar
17812               if (*ps=='*') {
17813                 if (arg2==1) _cimg_mp_return(arg1);
17814                 self_vector_s(arg1,mp_self_mul,arg2);
17815               } else if (*ps=='/') {
17816                 if (arg2==1) _cimg_mp_return(arg1);
17817                 self_vector_s(arg1,mp_self_div,arg2);
17818               } else {
17819                 if (arg2==1) _cimg_mp_return(arg1);
17820                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
17821               }
17822             }
17823 
17824             if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
17825               if (!is_inside_critical) is_parallelizable = false;
17826               p1 = ref[1]; // Index
17827               is_relative = (bool)ref[2];
17828               arg3 = ref[3]; // Offset
17829               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17830               if (p1!=~0U) {
17831                 if (!listout) _cimg_mp_return(arg1);
17832                 _cimg_mp_check_constant_index(p1);
17833                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17834                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17835               } else {
17836                 if (!imgout) _cimg_mp_return(arg1);
17837                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17838                                      arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17839               }
17840 
17841             } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
17842               if (!is_inside_critical) is_parallelizable = false;
17843               p1 = ref[1]; // Index
17844               is_relative = (bool)ref[2];
17845               arg3 = ref[3]; // X
17846               arg4 = ref[4]; // Y
17847               arg5 = ref[5]; // Z
17848               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17849               if (p1!=~0U) {
17850                 if (!listout) _cimg_mp_return(arg1);
17851                 _cimg_mp_check_constant_index(p1);
17852                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17853                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17854               } else {
17855                 if (!imgout) _cimg_mp_return(arg1);
17856                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17857                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17858               }
17859             }
17860 
17861             _cimg_mp_return(arg1);
17862           }
17863 
17864         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17865           if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
17866                           *ps=='&' || *ps=='^' || *ps=='|' ||
17867                           (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
17868               level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
17869             switch (*ps) {
17870             case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
17871             case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
17872             case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
17873             case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
17874             case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
17875             case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
17876             case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
17877             case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
17878             case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
17879             default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
17880             }
17881             s1 = *ps=='>' || *ps=='<'?ns:ps;
17882 
17883             ref.assign(7);
17884             arg1 = compile(ss,s1,depth1,ref,bloc_flags); // Variable slot
17885             arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to apply
17886 
17887             // Check for particular case to be simplified.
17888             if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
17889             if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
17890 
17891             // Apply operator on a copy to prevent modifying a constant or a variable.
17892             if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
17893               if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
17894               else arg1 = scalar1(mp_copy,arg1);
17895             }
17896 
17897             if (*ref==1) { // Vector value (scalar): V[k] += scalar
17898               _cimg_mp_check_type(arg2,2,1,0);
17899               arg3 = ref[1]; // Vector slot
17900               arg4 = ref[2]; // Index
17901               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17902               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17903               CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17904                 move_to(code);
17905               _cimg_mp_return(arg1);
17906             }
17907 
17908             if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
17909               if (!is_inside_critical) is_parallelizable = false;
17910               _cimg_mp_check_type(arg2,2,1,0);
17911               p1 = ref[1]; // Index
17912               is_relative = (bool)ref[2];
17913               arg3 = ref[3]; // Offset
17914               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17915               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17916               if (p1!=~0U) {
17917                 if (!listout) _cimg_mp_return(arg1);
17918                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17919                                     arg1,p1,arg3).move_to(code);
17920               } else {
17921                 if (!imgout) _cimg_mp_return(arg1);
17922                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17923                                     arg1,arg3).move_to(code);
17924               }
17925               _cimg_mp_return(arg1);
17926             }
17927 
17928             if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
17929               if (!is_inside_critical) is_parallelizable = false;
17930               _cimg_mp_check_type(arg2,2,1,0);
17931               p1 = ref[1]; // Index
17932               is_relative = (bool)ref[2];
17933               arg3 = ref[3]; // X
17934               arg4 = ref[4]; // Y
17935               arg5 = ref[5]; // Z
17936               arg6 = ref[6]; // C
17937               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17938               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17939               if (p1!=~0U) {
17940                 if (!listout) _cimg_mp_return(arg1);
17941                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17942                                     arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
17943               } else {
17944                 if (!imgout) _cimg_mp_return(arg1);
17945                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17946                                     arg1,arg3,arg4,arg5,arg6).move_to(code);
17947               }
17948               _cimg_mp_return(arg1);
17949             }
17950 
17951             if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
17952               if (!is_inside_critical) is_parallelizable = false;
17953               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17954               p1 = ref[1]; // Index
17955               is_relative = (bool)ref[2];
17956               arg3 = ref[3]; // Offset
17957               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17958               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17959               if (p1!=~0U) {
17960                 if (!listout) _cimg_mp_return(arg1);
17961                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17962                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17963               } else {
17964                 if (!imgout) _cimg_mp_return(arg1);
17965                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17966                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17967               }
17968               _cimg_mp_return(arg1);
17969             }
17970 
17971             if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
17972               if (!is_inside_critical) is_parallelizable = false;
17973               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17974               p1 = ref[1]; // Index
17975               is_relative = (bool)ref[2];
17976               arg3 = ref[3]; // X
17977               arg4 = ref[4]; // Y
17978               arg5 = ref[5]; // Z
17979               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17980               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17981               if (p1!=~0U) {
17982                 if (!listout) _cimg_mp_return(arg1);
17983                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17984                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17985               } else {
17986                 if (!imgout) _cimg_mp_return(arg1);
17987                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17988                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17989               }
17990               _cimg_mp_return(arg1);
17991             }
17992 
17993             if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
17994               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17995               if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
17996               else self_vector_s(arg1,op,arg2); // Vector += scalar
17997               _cimg_mp_return(arg1);
17998             }
17999 
18000             if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar
18001               _cimg_mp_check_type(arg2,2,1,0);
18002               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
18003               _cimg_mp_return(arg1);
18004             }
18005 
18006             variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
18007             cimg::strpare(variable_name,false,true);
18008             _cimg_mp_strerr;
18009             cimg::strellipsize(variable_name,64);
18010             throw CImgArgumentException("[" cimg_appname "_math_parser] "
18011                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
18012                                         "in expression '%s%s%s'.",
18013                                         pixel_type(),_cimg_mp_calling_function,s_op,
18014                                         _cimg_mp_is_constant(arg1)?"const ":"",
18015                                         variable_name._data,
18016                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18017           }
18018 
18019         for (s = ss1; s<se1; ++s)
18020           if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
18021             _cimg_mp_op("Operator '?:'");
18022             s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
18023             arg1 = compile(ss,s,depth1,0,bloc_flags);
18024             _cimg_mp_check_type(arg1,1,1,0);
18025             if (_cimg_mp_is_constant(arg1)) {
18026               if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,bloc_flags);
18027               else return *s1!=':'?0:compile(++s1,se,depth1,0,bloc_flags);
18028             }
18029             p2 = code._width;
18030             arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,bloc_flags);
18031             p3 = code._width;
18032             arg3 = *s1==':'?compile(++s1,se,depth1,0,bloc_flags):
18033               _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
18034             _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
18035             arg4 = _cimg_mp_size(arg2);
18036             if (arg4) pos = vector(arg4); else pos = scalar();
18037             CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
18038                                 p3 - p2,code._width - p3,arg4).move_to(code,p2);
18039             return_new_comp = true;
18040             _cimg_mp_return(pos);
18041           }
18042 
18043         for (s = se3, ns = se2; s>ss; --s, --ns)
18044           if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
18045             _cimg_mp_op("Operator '||'");
18046             arg1 = compile(ss,s,depth1,0,bloc_flags);
18047             _cimg_mp_check_type(arg1,1,1,0);
18048             if (arg1>0 && arg1<=16) _cimg_mp_return(1);
18049             p2 = code._width;
18050             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18051             _cimg_mp_check_type(arg2,2,1,0);
18052             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18053               _cimg_mp_constant(mem[arg1] || mem[arg2]);
18054             if (!arg1) _cimg_mp_return(arg2);
18055             pos = scalar();
18056             CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
18057               move_to(code,p2);
18058             return_new_comp = true;
18059             _cimg_mp_return(pos);
18060           }
18061 
18062         for (s = se3, ns = se2; s>ss; --s, --ns)
18063           if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
18064             _cimg_mp_op("Operator '&&'");
18065             arg1 = compile(ss,s,depth1,0,bloc_flags);
18066             _cimg_mp_check_type(arg1,1,1,0);
18067             if (!arg1) _cimg_mp_return(0);
18068             p2 = code._width;
18069             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18070             _cimg_mp_check_type(arg2,2,1,0);
18071             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18072               _cimg_mp_constant(mem[arg1] && mem[arg2]);
18073             if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
18074             pos = scalar();
18075             CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
18076               move_to(code,p2);
18077             return_new_comp = true;
18078             _cimg_mp_return(pos);
18079           }
18080 
18081         for (s = se2; s>ss; --s)
18082           if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
18083             _cimg_mp_op("Operator '|'");
18084             arg1 = compile(ss,s,depth1,0,bloc_flags);
18085             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18086             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18087             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
18088             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18089               if (!arg2) _cimg_mp_return(arg1);
18090               _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
18091             }
18092             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18093               if (!arg1) _cimg_mp_return(arg2);
18094               _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
18095             }
18096             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18097               _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]);
18098             if (!arg2) _cimg_mp_return(arg1);
18099             if (!arg1) _cimg_mp_return(arg2);
18100             _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
18101           }
18102 
18103         for (s = se2; s>ss; --s)
18104           if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
18105             _cimg_mp_op("Operator '&'");
18106             arg1 = compile(ss,s,depth1,0,bloc_flags);
18107             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18108             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18109             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
18110             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
18111             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
18112             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18113               _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]);
18114             if (!arg1 || !arg2) _cimg_mp_return(0);
18115             _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
18116           }
18117 
18118         for (s = se3, ns = se2; s>ss; --s, --ns)
18119           if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
18120             _cimg_mp_op("Operator '!='");
18121             arg1 = compile(ss,s,depth1,0,bloc_flags);
18122             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18123             if (arg1==arg2) _cimg_mp_return(0);
18124             p1 = _cimg_mp_size(arg1);
18125             p2 = _cimg_mp_size(arg2);
18126             if (p1 || p2) {
18127               if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
18128               pos = scalar();
18129               CImg<ulongT>::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18130               return_new_comp = true;
18131               _cimg_mp_return(pos);
18132             }
18133             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
18134             _cimg_mp_scalar2(mp_neq,arg1,arg2);
18135           }
18136 
18137         for (s = se3, ns = se2; s>ss; --s, --ns)
18138           if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
18139             _cimg_mp_op("Operator '=='");
18140             arg1 = compile(ss,s,depth1,0,bloc_flags);
18141             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18142             if (arg1==arg2) _cimg_mp_return(1);
18143             p1 = _cimg_mp_size(arg1);
18144             p2 = _cimg_mp_size(arg2);
18145             if (p1 || p2) {
18146               if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
18147               pos = scalar();
18148               CImg<ulongT>::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18149               return_new_comp = true;
18150               _cimg_mp_return(pos);
18151             }
18152             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]);
18153             _cimg_mp_scalar2(mp_eq,arg1,arg2);
18154           }
18155 
18156         for (s = se3, ns = se2; s>ss; --s, --ns)
18157           if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
18158             _cimg_mp_op("Operator '<='");
18159             arg1 = compile(ss,s,depth1,0,bloc_flags);
18160             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18161             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18162             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
18163             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
18164             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
18165             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
18166             if (arg1==arg2) _cimg_mp_return(1);
18167             _cimg_mp_scalar2(mp_lte,arg1,arg2);
18168           }
18169 
18170         for (s = se3, ns = se2; s>ss; --s, --ns)
18171           if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
18172             _cimg_mp_op("Operator '>='");
18173             arg1 = compile(ss,s,depth1,0,bloc_flags);
18174             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18175             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18176             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
18177             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
18178             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
18179             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
18180             if (arg1==arg2) _cimg_mp_return(1);
18181             _cimg_mp_scalar2(mp_gte,arg1,arg2);
18182           }
18183 
18184         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18185           if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
18186             _cimg_mp_op("Operator '<'");
18187             arg1 = compile(ss,s,depth1,0,bloc_flags);
18188             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18189             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18190             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
18191             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
18192             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
18193             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]);
18194             if (arg1==arg2) _cimg_mp_return(0);
18195             _cimg_mp_scalar2(mp_lt,arg1,arg2);
18196           }
18197 
18198         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18199           if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>')
18200             _cimg_mp_op("Operator '>'");
18201             arg1 = compile(ss,s,depth1,0,bloc_flags);
18202             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18203             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18204             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
18205             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
18206             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
18207             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]);
18208             if (arg1==arg2) _cimg_mp_return(0);
18209             _cimg_mp_scalar2(mp_gt,arg1,arg2);
18210           }
18211 
18212         for (s = se3, ns = se2; s>ss; --s, --ns)
18213           if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
18214             _cimg_mp_op("Operator '<<'");
18215             arg1 = compile(ss,s,depth1,0,bloc_flags);
18216             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18217             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18218             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18219               _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
18220             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18221               if (!arg2) _cimg_mp_return(arg1);
18222               _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
18223             }
18224             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18225               _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
18226             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18227               _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]);
18228             if (!arg1) _cimg_mp_return(0);
18229             if (!arg2) _cimg_mp_return(arg1);
18230             _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
18231           }
18232 
18233         for (s = se3, ns = se2; s>ss; --s, --ns)
18234           if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
18235             _cimg_mp_op("Operator '>>'");
18236             arg1 = compile(ss,s,depth1,0,bloc_flags);
18237             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18238             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18239             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18240               _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
18241             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18242               if (!arg2) _cimg_mp_return(arg1);
18243               _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
18244             }
18245             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18246               _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
18247             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18248               _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]);
18249             if (!arg1) _cimg_mp_return(0);
18250             if (!arg2) _cimg_mp_return(arg1);
18251             _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
18252           }
18253 
18254         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18255           if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18256               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18257               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18258                                                                                      *(ps - 1)<='9')))) &&
18259               level[s - expr._data]==clevel) { // Addition ('+')
18260             _cimg_mp_op("Operator '+'");
18261             arg1 = compile(ss,s,depth1,0,bloc_flags);
18262             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18263             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18264             if (!arg2) _cimg_mp_return(arg1);
18265             if (!arg1) _cimg_mp_return(arg2);
18266             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
18267             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
18268             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
18269             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]);
18270             if (code) { // Try to spot linear case 'a*b + c'
18271               CImg<ulongT> &pop = code.back();
18272               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18273                 arg3 = (unsigned int)pop[1];
18274                 arg4 = (unsigned int)pop[2];
18275                 arg5 = (unsigned int)pop[3];
18276                 code.remove();
18277                 CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18278                 _cimg_mp_return(arg3);
18279               }
18280             }
18281             if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
18282             if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
18283             _cimg_mp_scalar2(mp_add,arg1,arg2);
18284           }
18285 
18286         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18287           if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18288               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18289               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18290                                                                                      *(ps - 1)<='9')))) &&
18291               level[s - expr._data]==clevel) { // Subtraction ('-')
18292             _cimg_mp_op("Operator '-'");
18293             arg1 = compile(ss,s,depth1,0,bloc_flags);
18294             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18295             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18296             if (!arg2) _cimg_mp_return(arg1);
18297             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
18298             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
18299             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18300               if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
18301               _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
18302             }
18303             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]);
18304             if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
18305             if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'
18306               CImg<ulongT> &pop = code.back();
18307               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18308                 arg3 = (unsigned int)pop[1];
18309                 arg4 = (unsigned int)pop[2];
18310                 arg5 = (unsigned int)pop[3];
18311                 code.remove();
18312                 CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
18313                                      arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
18314                 _cimg_mp_return(arg3);
18315               }
18316             }
18317             if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
18318             _cimg_mp_scalar2(mp_sub,arg1,arg2);
18319           }
18320 
18321         for (s = se3, ns = se2; s>ss; --s, --ns)
18322           if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
18323             _cimg_mp_op("Operator '**'");
18324             arg1 = compile(ss,s,depth1,0,bloc_flags);
18325             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18326             _cimg_mp_check_type(arg1,1,3,2);
18327             _cimg_mp_check_type(arg2,2,3,2);
18328             if (arg2==1) _cimg_mp_return(arg1);
18329             if (arg1==1) _cimg_mp_return(arg2);
18330             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18331               pos = vector(2);
18332               CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
18333               return_new_comp = true;
18334               _cimg_mp_return(pos);
18335             }
18336             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
18337             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
18338             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
18339             if (!arg1 || !arg2) _cimg_mp_return(0);
18340             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18341           }
18342 
18343         for (s = se3, ns = se2; s>ss; --s, --ns)
18344           if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
18345             _cimg_mp_op("Operator '//'");
18346             arg1 = compile(ss,s,depth1,0,bloc_flags);
18347             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18348             _cimg_mp_check_type(arg1,1,3,2);
18349             _cimg_mp_check_type(arg2,2,3,2);
18350             if (arg2==1) _cimg_mp_return(arg1);
18351             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18352               pos = vector(2);
18353               CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
18354               return_new_comp = true;
18355               _cimg_mp_return(pos);
18356             }
18357             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18358             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18359               pos = vector(2);
18360               CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
18361               return_new_comp = true;
18362               _cimg_mp_return(pos);
18363             }
18364             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
18365             if (!arg1) _cimg_mp_return(0);
18366             _cimg_mp_scalar2(mp_div,arg1,arg2);
18367           }
18368 
18369         for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
18370             _cimg_mp_op("Operator '*'");
18371             arg1 = compile(ss,s,depth1,0,bloc_flags);
18372             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18373             p2 = _cimg_mp_size(arg2);
18374             if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication
18375               pos = vector(p2);
18376               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
18377               return_new_comp = true;
18378               _cimg_mp_return(pos);
18379             }
18380             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18381             if (arg2==1) _cimg_mp_return(arg1);
18382             if (arg1==1) _cimg_mp_return(arg2);
18383             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
18384             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
18385             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
18386             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
18387 
18388             if (code) { // Try to spot double multiplication 'a*b*c'
18389               CImg<ulongT> &pop = code.back();
18390               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18391                 arg3 = (unsigned int)pop[1];
18392                 arg4 = (unsigned int)pop[2];
18393                 arg5 = (unsigned int)pop[3];
18394                 code.remove();
18395                 CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18396                 _cimg_mp_return(arg3);
18397               }
18398             }
18399             if (!arg1 || !arg2) _cimg_mp_return(0);
18400             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18401           }
18402 
18403         for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
18404             _cimg_mp_op("Operator '/'");
18405             arg1 = compile(ss,s,depth1,0,bloc_flags);
18406             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18407             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18408             if (arg2==1) _cimg_mp_return(arg1);
18409             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
18410             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18411             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
18412             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
18413             if (!arg1) _cimg_mp_return(0);
18414             _cimg_mp_scalar2(mp_div,arg1,arg2);
18415           }
18416 
18417         for (s = se2, ns = se1; s>ss; --s, --ns)
18418           if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
18419             _cimg_mp_op("Operator '%'");
18420             arg1 = compile(ss,s,depth1,0,bloc_flags);
18421             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18422             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18423             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
18424             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
18425             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
18426             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18427               _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
18428             _cimg_mp_scalar2(mp_modulo,arg1,arg2);
18429           }
18430 
18431         if (se1>ss) {
18432           if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
18433             _cimg_mp_op("Operator '+'");
18434             _cimg_mp_return(compile(ss1,se,depth1,0,bloc_flags));
18435           }
18436 
18437           if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
18438             _cimg_mp_op("Operator '-'");
18439             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18440             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
18441             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]);
18442             _cimg_mp_scalar1(mp_minus,arg1);
18443           }
18444 
18445           if (*ss=='!') { // Logical not ('!')
18446             _cimg_mp_op("Operator '!'");
18447             if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
18448               arg1 = compile(ss2,se,depth1,0,bloc_flags);
18449               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
18450               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
18451               _cimg_mp_scalar1(mp_bool,arg1);
18452             }
18453             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18454             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
18455             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]);
18456             _cimg_mp_scalar1(mp_logical_not,arg1);
18457           }
18458 
18459           if (*ss=='~') { // Bitwise not ('~')
18460             _cimg_mp_op("Operator '~'");
18461             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18462             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
18463             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]);
18464             _cimg_mp_scalar1(mp_bitwise_not,arg1);
18465           }
18466         }
18467 
18468         for (s = se3, ns = se2; s>ss; --s, --ns)
18469           if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
18470             _cimg_mp_op("Operator '^^'");
18471             arg1 = compile(ss,s,depth1,0,bloc_flags);
18472             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18473             _cimg_mp_check_type(arg1,1,3,2);
18474             _cimg_mp_check_type(arg2,2,3,2);
18475             if (arg2==1) _cimg_mp_return(arg1);
18476             pos = vector(2);
18477             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18478               CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
18479             else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
18480               CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
18481             else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18482               CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
18483             else
18484               CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
18485             return_new_comp = true;
18486             _cimg_mp_return(pos);
18487           }
18488 
18489         for (s = se2; s>ss; --s)
18490           if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
18491             _cimg_mp_op("Operator '^'");
18492             arg1 = compile(ss,s,depth1,0,bloc_flags);
18493             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18494             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18495             if (arg2==1) _cimg_mp_return(arg1);
18496             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
18497             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
18498             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
18499             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18500               _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
18501             switch (arg2) {
18502             case 0 : _cimg_mp_return(1);
18503             case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
18504             case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
18505             case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
18506             default :
18507               if (_cimg_mp_is_constant(arg2)) {
18508                 if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
18509                 else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
18510               }
18511               _cimg_mp_scalar2(mp_pow,arg1,arg2);
18512             }
18513           }
18514 
18515         // Percentage computation.
18516         if (*se1=='%') {
18517           arg1 = compile(ss,se1,depth1,0,bloc_flags);
18518           arg2 = _cimg_mp_is_constant(arg1)?0:constant(100);
18519           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18520           if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100);
18521           _cimg_mp_scalar2(mp_div,arg1,arg2);
18522         }
18523 
18524         // Degree to radian postfix operator ('°' in UTF-8).
18525         if ((unsigned char)*se2==0xC2 && (unsigned char)*se1==0xB0) {
18526           arg1 = compile(ss,se2,depth1,0,bloc_flags);
18527           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
18528           if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]*cimg::PI/180);
18529           _cimg_mp_scalar1(mp_deg2rad,arg1);
18530         }
18531 
18532         // Pre/post-decrement and increment.
18533         is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
18534         if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) {
18535           if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
18536             _cimg_mp_op("Operator '++'");
18537             op = mp_self_increment;
18538           } else {
18539             _cimg_mp_op("Operator '--'");
18540             op = mp_self_decrement;
18541           }
18542           ref.assign(7);
18543           arg1 = is_sth?compile(ss2,se,depth1,ref,bloc_flags):
18544             compile(ss,se2,depth1,ref,bloc_flags); // Variable slot
18545 
18546           // Apply operator on a copy to prevent modifying a constant or a variable.
18547           if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
18548             if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
18549             else arg1 = scalar1(mp_copy,arg1);
18550           }
18551 
18552           if (is_sth) pos = arg1; // Determine return index, depending on pre/post action
18553           else {
18554             if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
18555             else pos = scalar1(mp_copy,arg1);
18556           }
18557 
18558           if (*ref==1) { // Vector value (scalar): V[k]++
18559             arg3 = ref[1]; // Vector slot
18560             arg4 = ref[2]; // Index
18561             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18562             CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
18563             CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
18564               move_to(code);
18565             _cimg_mp_return(pos);
18566           }
18567 
18568           if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
18569             if (!is_inside_critical) is_parallelizable = false;
18570             p1 = ref[1]; // Index
18571             is_relative = (bool)ref[2];
18572             arg3 = ref[3]; // Offset
18573             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18574             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18575             if (p1!=~0U) {
18576               if (!listout) _cimg_mp_return(pos);
18577               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
18578                                   arg1,p1,arg3).move_to(code);
18579             } else {
18580               if (!imgout) _cimg_mp_return(pos);
18581               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
18582                                   arg1,arg3).move_to(code);
18583             }
18584             _cimg_mp_return(pos);
18585           }
18586 
18587           if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
18588             if (!is_inside_critical) is_parallelizable = false;
18589             p1 = ref[1]; // Index
18590             is_relative = (bool)ref[2];
18591             arg3 = ref[3]; // X
18592             arg4 = ref[4]; // Y
18593             arg5 = ref[5]; // Z
18594             arg6 = ref[6]; // C
18595             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18596             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18597             if (p1!=~0U) {
18598               if (!listout) _cimg_mp_return(pos);
18599               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
18600                                   arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
18601             } else {
18602               if (!imgout) _cimg_mp_return(pos);
18603               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
18604                                   arg1,arg3,arg4,arg5,arg6).move_to(code);
18605             }
18606             _cimg_mp_return(pos);
18607           }
18608 
18609           if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
18610             if (!is_inside_critical) is_parallelizable = false;
18611             p1 = ref[1]; // Index
18612             is_relative = (bool)ref[2];
18613             arg3 = ref[3]; // Offset
18614             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18615             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18616             if (p1!=~0U) {
18617               if (!listout) _cimg_mp_return(pos);
18618               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
18619                                   arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
18620             } else {
18621               if (!imgout) _cimg_mp_return(pos);
18622               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
18623                                   arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
18624             }
18625             _cimg_mp_return(pos);
18626           }
18627 
18628           if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
18629             if (!is_inside_critical) is_parallelizable = false;
18630             p1 = ref[1]; // Index
18631             is_relative = (bool)ref[2];
18632             arg3 = ref[3]; // X
18633             arg4 = ref[4]; // Y
18634             arg5 = ref[5]; // Z
18635             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18636             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18637             if (p1!=~0U) {
18638               if (!listout) _cimg_mp_return(pos);
18639               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
18640                                   arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18641             } else {
18642               if (!imgout) _cimg_mp_return(pos);
18643               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
18644                                   arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18645             }
18646             _cimg_mp_return(pos);
18647           }
18648 
18649           if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
18650             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18651             _cimg_mp_return(pos);
18652           }
18653 
18654           if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++
18655             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18656             _cimg_mp_return(pos);
18657           }
18658 
18659           if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
18660           else variable_name.assign(ss,(unsigned int)(se1 - ss));
18661           variable_name.back() = 0;
18662           cimg::strpare(variable_name,false,true);
18663           _cimg_mp_strerr;
18664           cimg::strellipsize(variable_name,64);
18665           throw CImgArgumentException("[" cimg_appname "_math_parser] "
18666                                       "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
18667                                       "in expression '%s%s%s'.",
18668                                       pixel_type(),_cimg_mp_calling_function,s_op,
18669                                       _cimg_mp_is_constant(arg1)?"const ":"",
18670                                       variable_name._data,
18671                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18672         }
18673 
18674         // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
18675         if (*se1==']') {
18676           _cimg_mp_op("Value accessor '[]'");
18677 
18678           // Find opening bracket for the offset.
18679           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18680           if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
18681           is_sth=s0>ss && *(s0-1)==']';  // Particular case s.a. '..[..][..]' ?
18682           is_relative = *ss=='j' || *ss=='J';
18683 
18684           if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' &&
18685               (reserved_label[(int)*ss]==~0U ||
18686                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector
18687             if (*ss2=='#') { // Index specified
18688               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18689               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18690               _cimg_mp_check_constant_index(p1);
18691               _cimg_mp_check_list(false);
18692             } else { p1 = ~0U; s0 = ss2; }
18693             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18694             p2 = 1 + (p1!=~0U);
18695             arg1 = compile(s0,s1,depth1,0,bloc_flags); // Offset
18696             _cimg_mp_check_type(arg1,p2,1,0);
18697             arg2 = ~0U;
18698             if (s1<se1) {
18699               arg2 = compile(++s1,se1,depth1,0,bloc_flags); // Boundary
18700               _cimg_mp_check_type(arg2,p2 + 1,1,0);
18701             }
18702             if (p_ref && arg2==~0U) {
18703               *p_ref = 4;
18704               p_ref[1] = p1;
18705               p_ref[2] = (unsigned int)is_relative;
18706               p_ref[3] = arg1;
18707               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18708             }
18709             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
18710             if (p1==~0U) p2 = imgin._spectrum;
18711             else {
18712               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
18713               p2 = listin[p3]._spectrum;
18714             }
18715             if (!p2) _cimg_mp_return(0);
18716             pos = vector(p2);
18717             if (p1!=~0U) {
18718               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
18719                                   pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18720             } else {
18721               need_input_copy = true;
18722               CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
18723                                   pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18724             }
18725             return_new_comp = true;
18726             _cimg_mp_return(pos);
18727           }
18728 
18729           if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' &&
18730               (reserved_label[(int)*ss]==~0U ||
18731                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar
18732             if (*ss2=='#') { // Index specified
18733               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18734               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18735             } else { p1 = ~0U; s0 = ss2; }
18736             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18737             arg1 = compile(s0,s1,depth1,0,bloc_flags); // Offset
18738             arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):~0U; // Boundary
18739             if (p_ref && arg2==~0U) {
18740               *p_ref = 2;
18741               p_ref[1] = p1;
18742               p_ref[2] = (unsigned int)is_relative;
18743               p_ref[3] = arg1;
18744               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
18745               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18746             }
18747             if (p1!=~0U) {
18748               if (!listin) _cimg_mp_return(0);
18749               pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18750             } else {
18751               if (!imgin) _cimg_mp_return(0);
18752               need_input_copy = true;
18753               pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18754             }
18755             memtype[pos] = -1; // Prevent from being used in further optimization
18756             _cimg_mp_return(pos);
18757           }
18758 
18759           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18760           if (s0>ss) { // Vector element
18761             arg1 = compile(ss,s0,depth1,0,bloc_flags);
18762             if (_cimg_mp_is_scalar(arg1)) {
18763               variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
18764               _cimg_mp_strerr;
18765               cimg::strellipsize(variable_name,64);
18766               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18767                                           "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
18768                                           "in expression '%s%s%s'.",
18769                                           pixel_type(),_cimg_mp_calling_function,s_op,
18770                                           variable_name._data,
18771                                           s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18772 
18773             }
18774             s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18775 
18776             if (s1<se1) { // Two or three arguments -> sub-vector extraction
18777               p1 = _cimg_mp_size(arg1);
18778               arg2 = compile(++s0,s1,depth1,0,bloc_flags); // Starting index
18779               s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18780               arg3 = compile(s1,s0,depth1,0,bloc_flags); // Length
18781               arg4 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):1; // Step
18782               _cimg_mp_check_constant(arg3,2,3);
18783               arg3 = (unsigned int)mem[arg3];
18784               pos = vector(arg3);
18785               CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
18786               return_new_comp = true;
18787               _cimg_mp_return(pos);
18788             }
18789 
18790             // One argument -> vector value reference
18791             arg2 = compile(++s0,se1,depth1,0,bloc_flags);
18792             if (_cimg_mp_is_constant(arg2)) { // Constant index
18793               nb = (int)mem[arg2];
18794               if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
18795               variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
18796               _cimg_mp_strerr;
18797               cimg::strellipsize(variable_name,64);
18798               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18799                                           "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
18800                                           "(vector '%s' has dimension %u), "
18801                                           "in expression '%s%s%s'.",
18802                                           pixel_type(),_cimg_mp_calling_function,
18803                                           variable_name._data,nb,
18804                                           variable_name._data,_cimg_mp_size(arg1),
18805                                           s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18806             }
18807             if (p_ref) {
18808               *p_ref = 1;
18809               p_ref[1] = arg1;
18810               p_ref[2] = arg2;
18811               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization
18812             }
18813             pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
18814             memtype[pos] = -1; // Prevent from being used in further optimization
18815             _cimg_mp_return(pos);
18816           }
18817         }
18818 
18819         // Look for a function call, an access to image value, or a parenthesis.
18820         if (*se1==')') {
18821           if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,bloc_flags)); // Simple parentheses
18822           _cimg_mp_op("Value accessor '()'");
18823           is_relative = *ss=='j' || *ss=='J';
18824           s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
18825 
18826           // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
18827           if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
18828             if (*ss2=='#') { // Index specified
18829               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18830               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18831               _cimg_mp_check_constant_index(p1);
18832               _cimg_mp_check_list(false);
18833             } else { p1 = ~0U; s0 = ss2; }
18834             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
18835             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
18836             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
18837             arg4 = arg5 = ~0U;
18838             if (s0<se1) {
18839               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18840               arg1 = compile(s0,s1,depth1,0,bloc_flags);
18841               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18842                 p2 = _cimg_mp_size(arg1);
18843                 ++arg1;
18844                 if (p2>1) {
18845                   arg2 = arg1 + 1;
18846                   if (p2>2) arg3 = arg2 + 1;
18847                 }
18848                 if (s1<se1) {
18849                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18850                   arg4 = compile(s1,s2,depth1,0,bloc_flags);
18851                   arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):~0U;
18852                 }
18853               } else if (s1<se1) {
18854                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18855                 arg2 = compile(s1,s2,depth1,0,bloc_flags);
18856                 if (s2<se1) {
18857                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18858                   arg3 = compile(s2,s3,depth1,0,bloc_flags);
18859                   if (s3<se1) {
18860                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18861                     arg4 = compile(s3,s2,depth1,0,bloc_flags);
18862                     arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):~0U;
18863                   }
18864                 }
18865               }
18866             }
18867             if (p_ref && arg4==~0U && arg5==~0U) {
18868               *p_ref = 5;
18869               p_ref[1] = p1;
18870               p_ref[2] = (unsigned int)is_relative;
18871               p_ref[3] = arg1;
18872               p_ref[4] = arg2;
18873               p_ref[5] = arg3;
18874               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
18875               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18876               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
18877               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
18878             }
18879             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
18880             if (p1==~0U) p2 = imgin._spectrum;
18881             else if (_cimg_mp_is_constant(p1)) {
18882               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
18883               p2 = listin[p3]._spectrum;
18884             }
18885             if (!p2) _cimg_mp_return(0);
18886             pos = vector(p2);
18887             if (p1!=~0U)
18888               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
18889                                    pos,p1,arg1,arg2,arg3,
18890                                    arg4==~0U?_cimg_mp_interpolation:arg4,
18891                                    arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
18892             else {
18893               need_input_copy = true;
18894               CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
18895                                   pos,arg1,arg2,arg3,
18896                                   arg4==~0U?_cimg_mp_interpolation:arg4,
18897                                   arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
18898             }
18899             return_new_comp = true;
18900             _cimg_mp_return(pos);
18901           }
18902 
18903           // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
18904           if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
18905             if (*ss2=='#') { // Index specified
18906               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18907               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18908             } else { p1 = ~0U; s0 = ss2; }
18909             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
18910             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
18911             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
18912             arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
18913             arg5 = arg6 = ~0U;
18914             if (s0<se1) {
18915               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18916               arg1 = compile(s0,s1,depth1,0,bloc_flags);
18917               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18918                 p2 = _cimg_mp_size(arg1);
18919                 ++arg1;
18920                 if (p2>1) {
18921                   arg2 = arg1 + 1;
18922                   if (p2>2) {
18923                     arg3 = arg2 + 1;
18924                     if (p2>3) arg4 = arg3 + 1;
18925                   }
18926                 }
18927                 if (s1<se1) {
18928                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18929                   arg5 = compile(s1,s2,depth1,0,bloc_flags);
18930                   arg6 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):~0U;
18931                 }
18932               } else if (s1<se1) {
18933                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18934                 arg2 = compile(s1,s2,depth1,0,bloc_flags);
18935                 if (s2<se1) {
18936                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18937                   arg3 = compile(s2,s3,depth1,0,bloc_flags);
18938                   if (s3<se1) {
18939                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18940                     arg4 = compile(s3,s2,depth1,0,bloc_flags);
18941                     if (s2<se1) {
18942                       s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18943                       arg5 = compile(s2,s3,depth1,0,bloc_flags);
18944                       arg6 = s3<se1?compile(++s3,se1,depth1,0,bloc_flags):~0U;
18945                     }
18946                   }
18947                 }
18948               }
18949             }
18950             if (p_ref && arg5==~0U && arg6==~0U) {
18951               *p_ref = 3;
18952               p_ref[1] = p1;
18953               p_ref[2] = (unsigned int)is_relative;
18954               p_ref[3] = arg1;
18955               p_ref[4] = arg2;
18956               p_ref[5] = arg3;
18957               p_ref[6] = arg4;
18958               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
18959               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18960               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
18961               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
18962               if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
18963             }
18964 
18965             if (p1!=~0U) {
18966               if (!listin) _cimg_mp_return(0);
18967               pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
18968                             p1,arg1,arg2,arg3,arg4,
18969                             arg5==~0U?_cimg_mp_interpolation:arg5,
18970                             arg6==~0U?_cimg_mp_boundary:arg6);
18971             } else {
18972               if (!imgin) _cimg_mp_return(0);
18973               need_input_copy = true;
18974               pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
18975                             arg1,arg2,arg3,arg4,
18976                             arg5==~0U?_cimg_mp_interpolation:arg5,
18977                             arg6==~0U?_cimg_mp_boundary:arg6);
18978             }
18979             memtype[pos] = -1; // Prevent from being used in further optimization
18980             _cimg_mp_return(pos);
18981           }
18982 
18983           // Mathematical functions.
18984           switch (*ss) {
18985 
18986           case 'a' :
18987             if (!std::strncmp(ss,"abs(",4)) { // Absolute value
18988               _cimg_mp_op("Function 'abs()'");
18989               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
18990               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
18991               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1]));
18992               _cimg_mp_scalar1(mp_abs,arg1);
18993             }
18994 
18995             if (!std::strncmp(ss,"addr(",5)) { // Pointer address
18996               _cimg_mp_op("Function 'addr()'");
18997               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
18998               _cimg_mp_constant((double)arg1);
18999             }
19000 
19001             if (!std::strncmp(ss,"acos(",5)) { // Arccos
19002               _cimg_mp_op("Function 'acos()'");
19003               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19004               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
19005               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1]));
19006               _cimg_mp_scalar1(mp_acos,arg1);
19007             }
19008 
19009             if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine
19010               _cimg_mp_op("Function 'acosh()'");
19011               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19012               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1);
19013               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::acosh(mem[arg1]));
19014               _cimg_mp_scalar1(mp_acosh,arg1);
19015             }
19016 
19017             if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine
19018               _cimg_mp_op("Function 'asinh()'");
19019               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19020               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1);
19021               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::asinh(mem[arg1]));
19022               _cimg_mp_scalar1(mp_asinh,arg1);
19023             }
19024 
19025             if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent
19026               _cimg_mp_op("Function 'atanh()'");
19027               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19028               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1);
19029               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::atanh(mem[arg1]));
19030               _cimg_mp_scalar1(mp_atanh,arg1);
19031             }
19032 
19033             if (!std::strncmp(ss,"arg(",4) ||
19034                 !std::strncmp(ss,"arg0(",5) ||
19035                 !std::strncmp(ss,"arg1(",5)) { // Nth argument
19036               _cimg_mp_op(*ss3=='('?"Function 'arg()'":*ss3=='0'?"Function 'arg0()'":"Function 'arg1()'");
19037               s0 = ss4 + (*ss3!='('?1:0);
19038               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19039               arg1 = compile(s0,s1,depth1,0,bloc_flags);
19040               _cimg_mp_check_type(arg1,1,1,0);
19041               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19042               arg2 = compile(s1,s2,depth1,0,bloc_flags);
19043               p2 = _cimg_mp_size(arg2);
19044               p3 = 3;
19045               CImg<ulongT>::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode);
19046               for (s = ++s2; s<se; ++s) {
19047                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19048                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19049                 arg3 = compile(s,ns,depth1,0,bloc_flags);
19050                 _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
19051                 CImg<ulongT>::vector(arg3).move_to(l_opcode);
19052                 ++p3;
19053                 s = ns;
19054               }
19055               (l_opcode>'y').move_to(opcode);
19056               opcode[2] = opcode._height;
19057               if (_cimg_mp_is_constant(arg1)) {
19058                 p3-=1; // Number of args
19059                 if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1);
19060                 else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
19061                 if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
19062                 if (p2) {
19063                   pos = vector(p2);
19064                   std::memset(&mem[pos] + 1,0,p2*sizeof(double));
19065                   return_new_comp = true;
19066                   _cimg_mp_return(pos);
19067                 } else _cimg_mp_return(0);
19068               }
19069               pos = opcode[1] = p2?vector(p2):scalar();
19070               opcode.move_to(code);
19071               return_new_comp = true;
19072               _cimg_mp_return(pos);
19073             }
19074 
19075             if (!std::strncmp(ss,"asin(",5)) { // Arcsin
19076               _cimg_mp_op("Function 'asin()'");
19077               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19078               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
19079               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1]));
19080               _cimg_mp_scalar1(mp_asin,arg1);
19081             }
19082 
19083             if (!std::strncmp(ss,"atan(",5)) { // Arctan
19084               _cimg_mp_op("Function 'atan()'");
19085               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19086               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
19087               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1]));
19088               _cimg_mp_scalar1(mp_atan,arg1);
19089             }
19090 
19091             if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
19092               _cimg_mp_op("Function 'atan2()'");
19093               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19094               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
19095               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
19096               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19097               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
19098               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
19099               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
19100               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
19101                 _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
19102               _cimg_mp_scalar2(mp_atan2,arg1,arg2);
19103             }
19104             break;
19105 
19106           case 'b' :
19107             if (!std::strncmp(ss,"break(",6)) { // Break current loop
19108               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19109                 CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
19110                 _cimg_mp_return_nan();
19111               }
19112             }
19113 
19114             if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
19115               _cimg_mp_op("Function 'breakpoint()'");
19116               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19117                 CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
19118                 _cimg_mp_return_nan();
19119               }
19120             }
19121 
19122             if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
19123               _cimg_mp_op("Function 'bool()'");
19124               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19125               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
19126               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
19127               _cimg_mp_scalar1(mp_bool,arg1);
19128             }
19129 
19130             if (!std::strncmp(ss,"begin(",6)) { // Begin
19131               _cimg_mp_op("Function 'begin()'");
19132               s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
19133               if (s1!=se1) {
19134                 const bool is_inside_begin = (bool)(bloc_flags&2);
19135                 if (!is_inside_begin) code.swap(code_begin);
19136                 arg1 = compile(s1,se1,depth1,p_ref,2);
19137                 if (!is_inside_begin) code.swap(code_begin);
19138                 _cimg_mp_return(arg1);
19139               } else _cimg_mp_return_nan();
19140             }
19141 
19142             if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread
19143               _cimg_mp_op("Function 'begin_t()'");
19144               s1 = ss8; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
19145               if (s1!=se1) {
19146                 const bool is_inside_begin_t = (bool)(bloc_flags&4);
19147                 if (!is_inside_begin_t) code.swap(code_begin_t);
19148                 arg1 = compile(s1,se1,depth1,p_ref,4);
19149                 if (!is_inside_begin_t) code.swap(code_begin_t);
19150                 _cimg_mp_return(arg1);
19151               } else _cimg_mp_return_nan();
19152             }
19153             break;
19154 
19155           case 'c' :
19156             if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
19157               _cimg_mp_op("Function 'cabs()'");
19158               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19159               _cimg_mp_check_type(arg1,0,3,2);
19160               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0);
19161               _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
19162             }
19163 
19164             if (!std::strncmp(ss,"carg(",5)) { // Complex argument
19165               _cimg_mp_op("Function 'carg()'");
19166               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19167               _cimg_mp_check_type(arg1,0,3,2);
19168               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1);
19169               _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
19170             }
19171 
19172             if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
19173               _cimg_mp_op("Function 'cbrt()'");
19174               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19175               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
19176               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1]));
19177               _cimg_mp_scalar1(mp_cbrt,arg1);
19178             }
19179 
19180             if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
19181               _cimg_mp_op("Function 'cconj()'");
19182               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19183               _cimg_mp_check_type(arg1,0,3,2);
19184               pos = vector(2);
19185               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code);
19186               else CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code);
19187               return_new_comp = true;
19188               _cimg_mp_return(pos);
19189             }
19190 
19191             if (!std::strncmp(ss,"ceil(",5)) { // Ceil
19192               _cimg_mp_op("Function 'ceil()'");
19193               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19194               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
19195               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1]));
19196               _cimg_mp_scalar1(mp_ceil,arg1);
19197             }
19198 
19199             if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
19200               _cimg_mp_op("Function 'cexp()'");
19201               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19202               _cimg_mp_check_type(arg1,0,3,2);
19203               pos = vector(2);
19204               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code);
19205               else CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code);
19206               return_new_comp = true;
19207               _cimg_mp_return(pos);
19208             }
19209 
19210             if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
19211               _cimg_mp_op("Function 'clog()'");
19212               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19213               _cimg_mp_check_type(arg1,0,3,2);
19214               pos = vector(2);
19215               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code);
19216               else CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code);
19217               return_new_comp = true;
19218               _cimg_mp_return(pos);
19219             }
19220 
19221             if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine
19222               _cimg_mp_op("Function 'ccos()'");
19223               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19224               _cimg_mp_check_type(arg1,0,3,2);
19225               pos = vector(2);
19226               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code);
19227               else CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code);
19228               return_new_comp = true;
19229               _cimg_mp_return(pos);
19230             }
19231 
19232             if (!std::strncmp(ss,"csin(",5)) { // Complex sine
19233               _cimg_mp_op("Function 'csin()'");
19234               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19235               _cimg_mp_check_type(arg1,0,3,2);
19236               pos = vector(2);
19237               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code);
19238               else CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code);
19239               return_new_comp = true;
19240               _cimg_mp_return(pos);
19241             }
19242 
19243             if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent
19244               _cimg_mp_op("Function 'ctan()'");
19245               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19246               _cimg_mp_check_type(arg1,0,3,2);
19247               pos = vector(2);
19248               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code);
19249               else CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code);
19250               return_new_comp = true;
19251               _cimg_mp_return(pos);
19252             }
19253 
19254             if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine
19255               _cimg_mp_op("Function 'ccosh()'");
19256               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19257               _cimg_mp_check_type(arg1,0,3,2);
19258               pos = vector(2);
19259               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code);
19260               else CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code);
19261               return_new_comp = true;
19262               _cimg_mp_return(pos);
19263             }
19264 
19265             if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine
19266               _cimg_mp_op("Function 'csinh()'");
19267               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19268               _cimg_mp_check_type(arg1,0,3,2);
19269               pos = vector(2);
19270               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code);
19271               else CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code);
19272               return_new_comp = true;
19273               _cimg_mp_return(pos);
19274             }
19275 
19276             if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent
19277               _cimg_mp_op("Function 'ctanh()'");
19278               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
19279               _cimg_mp_check_type(arg1,0,3,2);
19280               pos = vector(2);
19281               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code);
19282               else CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code);
19283               return_new_comp = true;
19284               _cimg_mp_return(pos);
19285             }
19286 
19287             if (!std::strncmp(ss,"continue(",9)) { // Continue loop
19288               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19289                 CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
19290                 _cimg_mp_return_nan();
19291               }
19292             }
19293 
19294             if (!std::strncmp(ss,"copy(",5)) { // Memory copy
19295               _cimg_mp_op("Function 'copy()'");
19296               ref.assign(14);
19297               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19298               arg1 = p1 = compile(ss5,s1,depth1,ref,bloc_flags);
19299               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19300               arg2 = compile(s1,s2,depth1,ref._data + 7,bloc_flags);
19301               arg3 = arg4 = arg5 = ~0U; arg6 = 1;
19302               if (s2<se1) {
19303                 s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
19304                 arg3 = compile(s2,s3,depth1,0,bloc_flags);
19305                 if (s3<se1) {
19306                   s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19307                   arg4 = compile(s3,s1,depth1,0,bloc_flags);
19308                   if (s1<se1) {
19309                     s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19310                     arg5 = compile(s1,s2,depth1,0,bloc_flags);
19311                     arg6 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
19312                   }
19313                 }
19314               }
19315               if (_cimg_mp_is_vector(arg1)) {
19316                 if (!ref[0]) ++arg1;
19317                 else if (ref[0]>=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]);
19318               }
19319               if (_cimg_mp_is_vector(arg2)) {
19320                 if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2));
19321                 if (!ref[7]) ++arg2;
19322                 if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]);
19323               }
19324               if (arg3==~0U) arg3 = 1;
19325               if (arg4==~0U) arg4 = 1;
19326               if (arg5==~0U) arg5 = 1;
19327               _cimg_mp_check_type(arg3,3,1,0);
19328               _cimg_mp_check_type(arg4,4,1,0);
19329               _cimg_mp_check_type(arg5,5,1,0);
19330               _cimg_mp_check_type(arg6,5,1,0);
19331               CImg<ulongT>(1,22).move_to(code);
19332               code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
19333               code.back().get_shared_rows(8,21).fill(ref);
19334               _cimg_mp_return(p1);
19335             }
19336 
19337             if (!std::strncmp(ss,"cos(",4)) { // Cosine
19338               _cimg_mp_op("Function 'cos()'");
19339               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
19340               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
19341               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1]));
19342               _cimg_mp_scalar1(mp_cos,arg1);
19343             }
19344 
19345             if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
19346               _cimg_mp_op("Function 'cosh()'");
19347               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
19348               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
19349               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1]));
19350               _cimg_mp_scalar1(mp_cosh,arg1);
19351             }
19352 
19353             if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
19354               _cimg_mp_op("Function 'critical()'");
19355               p1 = code._width;
19356               arg1 = compile(ss + 9,se1,depth1,p_ref,bloc_flags | 1);
19357               CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
19358               _cimg_mp_return(arg1);
19359             }
19360 
19361             if (!std::strncmp(ss,"crop(",5)) { // Image crop
19362               _cimg_mp_op("Function 'crop()'");
19363               if (*ss5=='#') { // Index specified
19364                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19365                 p1 = compile(ss6,s0++,depth1,0,bloc_flags);
19366                 _cimg_mp_check_list(false);
19367               } else { p1 = ~0U; s0 = ss5; need_input_copy = true; }
19368               pos = 0;
19369               is_sth = false; // Coordinates specified as a vector?
19370               if (s0<se1) for (s = s0; s<se; ++s, ++pos) {
19371                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19372                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19373                 arg1 = compile(s,ns,depth1,0,bloc_flags);
19374                 if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
19375                   opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
19376                                                   arg1 + (ulongT)_cimg_mp_size(arg1));
19377                   opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode);
19378                   is_sth = true;
19379                 } else {
19380                   _cimg_mp_check_type(arg1,pos + 1,1,0);
19381                   CImg<ulongT>::vector(arg1).move_to(l_opcode);
19382                 }
19383                 s = ns;
19384               }
19385               (l_opcode>'y').move_to(opcode);
19386 
19387               arg1 = 0; arg2 = (p1!=~0U);
19388               switch (opcode._height) {
19389               case 0 : case 1 :
19390                 CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
19391                 break;
19392               case 2 :
19393                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
19394                 arg1 = arg2 + 2;
19395                 break;
19396               case 3 :
19397                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
19398                 arg1 = arg2 + 2;
19399                 break;
19400               case 4 :
19401                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
19402                   move_to(opcode);
19403                 arg1 = arg2 + (is_sth?2:3);
19404                 break;
19405               case 5 :
19406                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
19407                   move_to(opcode);
19408                 arg1 = arg2 + (is_sth?2:3);
19409                 break;
19410               case 6 :
19411                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
19412                                     _cimg_mp_boundary).move_to(opcode);
19413                 arg1 = arg2 + (is_sth?2:4);
19414                 break;
19415               case 7 :
19416                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
19417                                     opcode[6]).move_to(opcode);
19418                 arg1 = arg2 + (is_sth?2:4);
19419                 break;
19420               case 8 :
19421                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
19422                                     opcode[7],_cimg_mp_boundary).move_to(opcode);
19423                 arg1 = arg2 + (is_sth?2:5);
19424                 break;
19425               case 9 :
19426                 arg1 = arg2 + (is_sth?2:5);
19427                 break;
19428               default : // Error -> too much arguments
19429                 _cimg_mp_strerr;
19430                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19431                                             "CImg<%s>::%s: %s: Too much arguments specified, "
19432                                             "in expression '%s%s%s'.",
19433                                             pixel_type(),_cimg_mp_calling_function,s_op,
19434                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19435               }
19436 
19437               _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
19438               _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
19439               _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
19440               _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
19441               if (opcode[4]!=(ulongT)~0U) {
19442                 _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3);
19443                 opcode[4] = (ulongT)mem[opcode[4]];
19444               }
19445               if (opcode[5]!=(ulongT)~0U) {
19446                 _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3);
19447                 opcode[5] = (ulongT)mem[opcode[5]];
19448               }
19449               if (opcode[6]!=(ulongT)~0U) {
19450                 _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3);
19451                 opcode[6] = (ulongT)mem[opcode[6]];
19452               }
19453               if (opcode[7]!=(ulongT)~0U) {
19454                 _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3);
19455                 opcode[7] = (ulongT)mem[opcode[7]];
19456               }
19457               _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
19458 
19459               if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
19460                   opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
19461                 p2 = 0;
19462                 if (p1!=~0U) {
19463                   _cimg_mp_check_constant(p1,1,1);
19464                   p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
19465                 }
19466                 const CImg<T> &img = p1!=~0U?listin[p2]:imgin;
19467                 if (!img) {
19468                   _cimg_mp_strerr;
19469                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
19470                                               "CImg<%s>::%s: %s: Cannot crop empty image when "
19471                                               "some xyzc-coordinates are unspecified, in expression '%s%s%s'.",
19472                                               pixel_type(),_cimg_mp_calling_function,s_op,
19473                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19474                 }
19475                 if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
19476                 if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
19477                 if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
19478                 if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
19479               }
19480 
19481               pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
19482               CImg<ulongT>::vector((ulongT)mp_crop,
19483                                   pos,p1,
19484                                   *opcode,opcode[1],opcode[2],opcode[3],
19485                                   opcode[4],opcode[5],opcode[6],opcode[7],
19486                                   opcode[8]).move_to(code);
19487               return_new_comp = true;
19488               _cimg_mp_return(pos);
19489             }
19490 
19491             if (!std::strncmp(ss,"cross(",6)) { // Cross product
19492               _cimg_mp_op("Function 'cross()'");
19493               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19494               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
19495               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
19496               _cimg_mp_check_type(arg1,1,2,3);
19497               _cimg_mp_check_type(arg2,2,2,3);
19498               pos = vector(3);
19499               CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
19500               return_new_comp = true;
19501               _cimg_mp_return(pos);
19502             }
19503 
19504             if (!std::strncmp(ss,"cut(",4)) { // Cut
19505               _cimg_mp_op("Function 'cut()'");
19506               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19507               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
19508               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19509               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
19510               arg3 = compile(++s2,se1,depth1,0,bloc_flags);
19511               _cimg_mp_check_type(arg2,2,1,0);
19512               _cimg_mp_check_type(arg3,3,1,0);
19513               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
19514               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
19515                 val = mem[arg1];
19516                 val1 = mem[arg2];
19517                 val2 = mem[arg3];
19518                 _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
19519               }
19520               _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
19521             }
19522 
19523             if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate
19524               is_sth = *ss2=='n'; // is_convolve?
19525               _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'");
19526               op = is_sth?mp_convolve:mp_correlate;
19527               const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector
19528                                                 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA
19529                                                 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM
19530                                                 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode
19531                                                 ~0U,~0U,~0U, // [15]=xcenter, [16]=ycenter, [17]=zcenter
19532                                                 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart
19533                                                 ~0U,~0U,~0U, // [21]=xend, [22]=yend, [23]=zend
19534                                                 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride
19535                                                 1,1,1, // [27]=xdilation, [28]=ydilation, [29]=zdilation,
19536                                                 0 }; // [30]=interpolation_type
19537 
19538               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19539               CImg<ulongT>(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode);
19540 
19541               arg1 = 2;
19542               for (s = std::strchr(ss,'(') + 1; s<se && arg1<l_opcode[0]._height; ++s) {
19543                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19544                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19545                 l_opcode(0,arg1++) = compile(s,ns,depth1,0,bloc_flags);
19546                 s = ns;
19547               }
19548               l_opcode[0].move_to(opcode);
19549 
19550               if (arg1<12 || arg1>opcode._height) {
19551                 _cimg_mp_strerr;
19552                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19553                                             "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.",
19554                                             pixel_type(),_cimg_mp_calling_function,s_op,
19555                                             arg1<12?"Not enough":"Too much",
19556                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19557               }
19558               _cimg_mp_check_type(opcode[2],1,2,0); // A
19559               _cimg_mp_check_constant(opcode[3],2,3); // wA
19560               _cimg_mp_check_constant(opcode[4],3,3); // hA
19561               _cimg_mp_check_constant(opcode[5],4,3); // dA
19562               _cimg_mp_check_constant(opcode[6],5,3); // sA
19563               _cimg_mp_check_type(opcode[7],6,2,0); // M
19564               _cimg_mp_check_constant(opcode[8],7,3); // wM
19565               _cimg_mp_check_constant(opcode[9],8,3); // hM
19566               _cimg_mp_check_constant(opcode[10],9,3); // dM
19567               _cimg_mp_check_constant(opcode[11],10,3); // sM
19568               _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions
19569               _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized
19570               _cimg_mp_check_constant(opcode[14],13,1); // channel_mode
19571               if (opcode[15]!=~0U) _cimg_mp_check_type(opcode[15],14,1,0); // xcenter
19572               if (opcode[16]!=~0U) _cimg_mp_check_type(opcode[16],15,1,0); // ycenter
19573               if (opcode[17]!=~0U) _cimg_mp_check_type(opcode[17],16,1,0); // zcenter
19574               _cimg_mp_check_constant(opcode[18],17,1); // xstart
19575               _cimg_mp_check_constant(opcode[19],18,1); // ystart
19576               _cimg_mp_check_constant(opcode[20],19,1); // zstart
19577               if (opcode[21]!=~0U) _cimg_mp_check_constant(opcode[21],20,1); // xend
19578               if (opcode[22]!=~0U) _cimg_mp_check_constant(opcode[22],21,1); // yend
19579               if (opcode[23]!=~0U) _cimg_mp_check_constant(opcode[23],22,1); // zend
19580               _cimg_mp_check_constant(opcode[24],23,0); // xstride
19581               _cimg_mp_check_constant(opcode[25],24,0); // ystride
19582               _cimg_mp_check_constant(opcode[26],25,0); // zstride
19583               _cimg_mp_check_type(opcode[27],26,1,0); // xdilation
19584               _cimg_mp_check_type(opcode[28],27,1,0); // ydilation
19585               _cimg_mp_check_type(opcode[29],28,1,0); // zdilation
19586               _cimg_mp_check_type(opcode[30],29,1,0); // interpolation_type
19587 
19588               const unsigned int
19589                 wA = (unsigned int)mem[opcode[3]],
19590                 hA = (unsigned int)mem[opcode[4]],
19591                 dA = (unsigned int)mem[opcode[5]],
19592                 sA = (unsigned int)mem[opcode[6]],
19593                 wM = (unsigned int)mem[opcode[8]],
19594                 hM = (unsigned int)mem[opcode[9]],
19595                 dM = (unsigned int)mem[opcode[10]],
19596                 sM = (unsigned int)mem[opcode[11]],
19597                 channel_mode = (unsigned int)mem[opcode[14]];
19598               const int
19599                 xstart = (int)mem[opcode[18]],
19600                 ystart = (int)mem[opcode[19]],
19601                 zstart = (int)mem[opcode[20]],
19602                 xend = opcode[21]!=~0U?(int)mem[opcode[21]]:wA - 1,
19603                 yend = opcode[22]!=~0U?(int)mem[opcode[22]]:hA - 1,
19604                 zend = opcode[23]!=~0U?(int)mem[opcode[23]]:dA - 1;
19605 
19606               if (xstart>xend || ystart>yend || zstart>zend) {
19607                 _cimg_mp_strerr;
19608                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19609                                             "CImg<%s>::%s: %s: Invalid xyz-start/end arguments "
19610                                             "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.",
19611                                             pixel_type(),_cimg_mp_calling_function,s_op,
19612                                             xstart,ystart,zstart,xend,yend,zend,
19613                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19614               }
19615 
19616               const float
19617                 xstride = (float)mem[opcode[24]],
19618                 ystride = (float)mem[opcode[25]],
19619                 zstride = (float)mem[opcode[26]];
19620 
19621               if (xstride<=0 || ystride<=0 || zstride<=0) {
19622                 _cimg_mp_strerr;
19623                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19624                                             "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), "
19625                                             "in expression '%s%s%s'.",
19626                                             pixel_type(),_cimg_mp_calling_function,s_op,
19627                                             xstride,ystride,zstride,
19628                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19629               }
19630 
19631               arg2 = xend - xstart + 1;
19632               arg3 = yend - ystart + 1;
19633               arg4 = zend + zstart + 1;
19634               arg5 = !channel_mode?sA*sM:channel_mode==1?std::max(sA,sM):
19635                 channel_mode==2?std::max(sA,sM)/std::min(sA,sM):1U;
19636 
19637               opcode[1] = pos = vector(arg2*arg3*arg4*arg5);
19638               opcode[3] = (ulongT)wA;
19639               opcode[4] = (ulongT)hA;
19640               opcode[5] = (ulongT)dA;
19641               opcode[6] = (ulongT)sA;
19642               opcode[8] = (ulongT)wM;
19643               opcode[9] = (ulongT)hM;
19644               opcode[10] = (ulongT)dM;
19645               opcode[11] = (ulongT)sM;
19646               opcode[14] = (ulongT)channel_mode;
19647               opcode[18] = (ulongT)xstart;
19648               opcode[19] = (ulongT)ystart;
19649               opcode[20] = (ulongT)zstart;
19650               opcode[21] = (ulongT)xend;
19651               opcode[22] = (ulongT)yend;
19652               opcode[23] = (ulongT)zend;
19653               opcode.move_to(code);
19654               return_new_comp = true;
19655               _cimg_mp_return(pos);
19656             }
19657             break;
19658 
19659           case 'd' :
19660             if (*ss1=='(') { // Image depth
19661               _cimg_mp_op("Function 'd()'");
19662               if (*ss2=='#') { // Index specified
19663                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
19664                 _cimg_mp_check_list(false);
19665               } else { if (ss2!=se1) break; p1 = ~0U; }
19666               _cimg_mp_scalar1(mp_image_d,p1);
19667             }
19668 
19669             if (!std::strncmp(ss,"date(",5)) { // Current date or file date
19670               _cimg_mp_op("Function 'date()'");
19671               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19672               arg1 = ss5!=se1?compile(ss5,s1,depth1,0,bloc_flags):~0U;
19673               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):~0U;
19674               if (arg2!=~0U) _cimg_mp_check_type(arg2,1,2,0);
19675               pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
19676               CImg<ulongT>::vector((ulongT)mp_date,pos,_cimg_mp_size(pos),
19677                                    arg1,arg1==~0U?~0U:_cimg_mp_size(arg1),
19678                                    arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code);
19679               return_new_comp = true;
19680               _cimg_mp_return(pos);
19681             }
19682 
19683             if (!std::strncmp(ss,"debug(",6)) { // Print debug info
19684               _cimg_mp_op("Function 'debug()'");
19685               p1 = code._width;
19686               arg1 = compile(ss6,se1,depth1,p_ref,bloc_flags);
19687               *se1 = 0;
19688               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
19689               cimg::strpare(variable_name,false,true);
19690               ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
19691                 variable_name)>'y').move_to(opcode);
19692               opcode[2] = opcode._height;
19693               opcode.move_to(code,p1);
19694               *se1 = ')';
19695               _cimg_mp_return(arg1);
19696             }
19697 
19698             if (!std::strncmp(ss,"deg2rad(",8)) { // Degrees to radians
19699               _cimg_mp_op("Function 'deg2rad()'");
19700               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
19701               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
19702               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]*cimg::PI/180);
19703               _cimg_mp_scalar1(mp_deg2rad,arg1);
19704             }
19705 
19706             if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
19707               _cimg_mp_op("Function 'display()'");
19708               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19709                 CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
19710                 _cimg_mp_return_nan();
19711               }
19712               if (*ss8!='#') { // Vector
19713                 s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19714                 arg1 = compile(ss8,s1,depth1,0,bloc_flags);
19715                 arg2 = 0; arg3 = arg4 = arg5 = 1;
19716                 if (s1<se1) {
19717                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19718                   arg2 = compile(s1 + 1,s2,depth1,0,bloc_flags);
19719                   if (s2<se1) {
19720                     s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
19721                     arg3 = compile(s2,s3,depth1,0,bloc_flags);
19722                     if (s3<se1) {
19723                       s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19724                       arg4 = compile(s3,s2,depth1,0,bloc_flags);
19725                       arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
19726                     }
19727                   }
19728                 }
19729                 _cimg_mp_check_type(arg2,2,1,0);
19730                 _cimg_mp_check_type(arg3,3,1,0);
19731                 _cimg_mp_check_type(arg4,4,1,0);
19732                 _cimg_mp_check_type(arg5,5,1,0);
19733 
19734                 c1 = *s1; *s1 = 0;
19735                 variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
19736                 cimg::strpare(variable_name,false,true);
19737                 if (_cimg_mp_is_vector(arg1))
19738                   ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
19739                     variable_name)>'y').move_to(opcode);
19740                 else
19741                   ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
19742                     variable_name)>'y').move_to(opcode);
19743                 opcode[2] = opcode._height;
19744                 opcode.move_to(code);
19745 
19746                 ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
19747                                        arg2,arg3,arg4,arg5),
19748                   variable_name)>'y').move_to(opcode);
19749                 opcode[2] = opcode._height;
19750                 opcode.move_to(code);
19751                 *s1 = c1;
19752                 _cimg_mp_return(arg1);
19753 
19754               } else { // Image
19755                 p1 = compile(ss8 + 1,se1,depth1,0,bloc_flags);
19756                 _cimg_mp_check_list(true);
19757                 CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
19758                 _cimg_mp_return_nan();
19759               }
19760             }
19761 
19762             if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
19763               _cimg_mp_op("Function 'det()'");
19764               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
19765               _cimg_mp_check_matrix_square(arg1,1);
19766               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19767               _cimg_mp_scalar2(mp_det,arg1,p1);
19768             }
19769 
19770             if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
19771               _cimg_mp_op("Function 'diag()'");
19772               CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode);
19773               for (s = ss5; s<se; ++s) {
19774                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19775                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19776                 arg2 = compile(s,ns,depth1,0,bloc_flags);
19777                 if (_cimg_mp_is_vector(arg2))
19778                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19779                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
19780                     move_to(l_opcode);
19781                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19782                 s = ns;
19783               }
19784               (l_opcode>'y').move_to(opcode);
19785               arg1 = opcode._height - 3;
19786               pos = vector(arg1*arg1);
19787               opcode[1] = pos;
19788               opcode[2] = opcode._height;
19789               opcode.move_to(code);
19790               return_new_comp = true;
19791               _cimg_mp_return(pos);
19792             }
19793 
19794             if (!std::strncmp(ss,"dot(",4)) { // Dot product
19795               _cimg_mp_op("Function 'dot()'");
19796               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19797               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
19798               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
19799               if (_cimg_mp_is_vector(arg1)) {
19800                 _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1));
19801                 _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
19802               }
19803               _cimg_mp_check_type(arg2,2,1,0);
19804               _cimg_mp_scalar2(mp_mul,arg1,arg2);
19805             }
19806 
19807             if (!std::strncmp(ss,"do(",3)) { // Do..while
19808               _cimg_mp_op("Function 'do()'");
19809               s0 = *ss2=='('?ss3:ss8;
19810               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19811               arg1 = code._width;
19812               arg6 = mempos;
19813               p1 = compile(s0,s1,depth1,0,bloc_flags); // Body
19814               arg2 = code._width;
19815               p2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):p1; // Condition
19816               _cimg_mp_check_type(p2,2,1,0);
19817               CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
19818                                    p1>=arg6 && !_cimg_mp_is_constant(p1),
19819                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
19820               _cimg_mp_return(p1);
19821             }
19822 
19823             if (!std::strncmp(ss,"draw(",5)) { // Draw image
19824               if (!is_inside_critical) is_parallelizable = false;
19825               _cimg_mp_op("Function 'draw()'");
19826               if (*ss5=='#') { // Index specified
19827                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19828                 p1 = compile(ss6,s0++,depth1,0,bloc_flags);
19829                 _cimg_mp_check_list(true);
19830               } else { p1 = ~0U; s0 = ss5; }
19831               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19832               arg1 = compile(s0,s1,depth1,0,bloc_flags);
19833               arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
19834               arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
19835               arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
19836               arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
19837               s0 = se1;
19838               if (s1<se1) {
19839                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19840                 arg2 = compile(++s1,s0,depth1,0,bloc_flags);
19841                 if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
19842                   p2 = _cimg_mp_size(arg2);
19843                   ++arg2;
19844                   if (p2>1) {
19845                     arg3 = arg2 + 1;
19846                     if (p2>2) {
19847                       arg4 = arg3 + 1;
19848                       if (p2>3) arg5 = arg4 + 1;
19849                     }
19850                   }
19851                   ++s0;
19852                   is_sth = true;
19853                 } else {
19854                   if (s0<se1) {
19855                     is_sth = p1!=~0U;
19856                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19857                     arg3 = compile(++s0,s1,depth1,0,bloc_flags);
19858                     _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
19859                     if (s1<se1) {
19860                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19861                       arg4 = compile(++s1,s0,depth1,0,bloc_flags);
19862                       _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
19863                       if (s0<se1) {
19864                         s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19865                         arg5 = compile(++s0,s1,depth1,0,bloc_flags);
19866                         _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
19867                         s0 = ++s1;
19868                       }
19869                     }
19870                   }
19871                   is_sth = false;
19872                 }
19873               }
19874 
19875               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19876               CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
19877                                    0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode);
19878 
19879               arg2 = arg3 = arg4 = arg5 = ~0U;
19880               p2 = p1!=~0U?0:1;
19881               if (s0<se1) {
19882                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19883                 arg2 = compile(s0,s1,depth1,0,bloc_flags);
19884                 _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
19885                 if (s1<se1) {
19886                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19887                   arg3 = compile(++s1,s0,depth1,0,bloc_flags);
19888                   _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
19889                   if (s0<se1) {
19890                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19891                     arg4 = compile(++s0,s1,depth1,0,bloc_flags);
19892                     _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
19893                     if (s1<se1) {
19894                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19895                       arg5 = compile(++s1,s0,depth1,0,bloc_flags);
19896                       _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
19897                     }
19898                   }
19899                 }
19900               }
19901               if (s0<s1) s0 = s1;
19902 
19903               l_opcode(0,8) = (ulongT)arg2;
19904               l_opcode(0,9) = (ulongT)arg3;
19905               l_opcode(0,10) = (ulongT)arg4;
19906               l_opcode(0,11) = (ulongT)arg5;
19907 
19908               if (s0<se1) {
19909                 s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19910                 arg6 = compile(++s0,s1,depth1,0,bloc_flags);
19911                 _cimg_mp_check_type(arg6,0,1,0);
19912                 l_opcode(0,12) = arg6;
19913                 if (s1<se1) {
19914                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19915                   p2 = compile(++s1,s0,depth1,0,bloc_flags);
19916                   _cimg_mp_check_type(p2,0,2,0);
19917                   l_opcode(0,13) = p2;
19918                   l_opcode(0,14) = _cimg_mp_size(p2);
19919                   p3 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):1;
19920                   _cimg_mp_check_type(p3,0,1,0);
19921                   l_opcode(0,15) = p3;
19922                 }
19923               }
19924               l_opcode[0].move_to(code);
19925               _cimg_mp_return(arg1);
19926             }
19927 
19928             break;
19929 
19930           case 'e' :
19931             if (!std::strncmp(ss,"echo(",5)) { // Echo
19932               _cimg_mp_op("Function 'echo()'");
19933               CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
19934               for (s = ss5; s<se1; ++s) {
19935                 ns = s; while (ns<se1 && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19936                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19937                 arg1 = compile(s,ns,depth1,0,bloc_flags);
19938                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
19939                 s = ns;
19940               }
19941               (l_opcode>'y').move_to(opcode);
19942               opcode[2] = opcode._height;
19943               opcode.move_to(code);
19944               _cimg_mp_return_nan();
19945             }
19946 
19947             if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
19948               _cimg_mp_op("Function 'eig()'");
19949               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
19950               _cimg_mp_check_matrix_square(arg1,1);
19951               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19952               pos = vector((p1 + 1)*p1);
19953               CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
19954               return_new_comp = true;
19955               _cimg_mp_return(pos);
19956             }
19957 
19958             if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
19959               if (!is_inside_critical) is_parallelizable = false;
19960               _cimg_mp_op("Function 'ellipse()'");
19961               if (*ss8=='#') { // Index specified
19962                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19963                 p1 = compile(ss + 9,s0++,depth1,0,bloc_flags);
19964                 _cimg_mp_check_list(true);
19965               } else { p1 = ~0U; s0 = ss8; }
19966               if (s0==se1) compile(s0,se1,depth1,0,bloc_flags); // 'missing' argument error
19967               CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
19968               for (s = s0; s<se; ++s) {
19969                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19970                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19971                 arg2 = compile(s,ns,depth1,0,bloc_flags);
19972                 if (_cimg_mp_is_vector(arg2))
19973                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19974                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
19975                     move_to(l_opcode);
19976                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19977                 s = ns;
19978               }
19979               (l_opcode>'y').move_to(opcode);
19980               opcode[2] = opcode._height;
19981               opcode.move_to(code);
19982               _cimg_mp_return_nan();
19983             }
19984 
19985             if (!std::strncmp(ss,"erf(",4)) { // Error function
19986               _cimg_mp_op("Function 'erf()'");
19987               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
19988               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1);
19989               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::erf(mem[arg1]));
19990               _cimg_mp_scalar1(mp_erf,arg1);
19991             }
19992 
19993             if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function
19994               _cimg_mp_op("Function 'erfinv()'");
19995               arg1 = compile(ss7,se1,depth1,0,bloc_flags);
19996               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1);
19997               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::erfinv(mem[arg1]));
19998               _cimg_mp_scalar1(mp_erfinv,arg1);
19999             }
20000 
20001             if (!std::strncmp(ss,"exp(",4)) { // Exponential
20002               _cimg_mp_op("Function 'exp()'");
20003               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20004               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
20005               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1]));
20006               _cimg_mp_scalar1(mp_exp,arg1);
20007             }
20008 
20009             if (!std::strncmp(ss,"expr(",5)) { // Vector from expression
20010               _cimg_mp_op("Function 'expr()'");
20011               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20012               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
20013               _cimg_mp_check_type(arg1,1,2,0);
20014               p1 = _cimg_mp_size(arg1);
20015               arg2 = arg3 = arg4 = arg5 = 0;
20016               if (s1<se1) {
20017                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20018                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20019                 _cimg_mp_check_constant(arg2,2,2);
20020                 arg2 = (unsigned int)mem[arg2];
20021                 if (arg2) arg3 = arg4 = arg5 = 1;
20022                 if (s2<se1) {
20023                   s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20024                   arg3 = compile(++s2,s1,depth1,0,bloc_flags);
20025                   _cimg_mp_check_constant(arg3,3,3);
20026                   arg3 = (unsigned int)mem[arg3];
20027                   if (s1<se1) {
20028                     s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20029                     arg4 = compile(++s1,s2,depth1,0,bloc_flags);
20030                     _cimg_mp_check_constant(arg4,4,3);
20031                     arg4 = (unsigned int)mem[arg4];
20032                     arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20033                     _cimg_mp_check_constant(arg5,5,3);
20034                     arg5 = (unsigned int)mem[arg5];
20035                   }
20036                 }
20037               }
20038               p2 = arg2*arg3*arg4*arg5;
20039               if (p2) pos = vector(p2); else pos = scalar();
20040               CImg<ulongT>::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code);
20041               return_new_comp = true;
20042               _cimg_mp_return(pos);
20043             }
20044 
20045             if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
20046               _cimg_mp_op("Function 'eye()'");
20047               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20048               _cimg_mp_check_constant(arg1,1,3);
20049               p1 = (unsigned int)mem[arg1];
20050               pos = vector(p1*p1);
20051               CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
20052               return_new_comp = true;
20053               _cimg_mp_return(pos);
20054             }
20055 
20056             if (!std::strncmp(ss,"end(",4)) { // End
20057               _cimg_mp_op("Function 'end()'");
20058               s1 = ss4; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
20059               if (s1!=se1) {
20060                 const bool is_inside_end = (bool)(bloc_flags&8);
20061                 if (!is_inside_end) code.swap(code_end);
20062                 compile(s1,se1,depth1,p_ref,8);
20063                 if (!is_inside_end) code.swap(code_end);
20064                 is_end_code = true;
20065               }
20066               _cimg_mp_return_nan();
20067             }
20068 
20069             if (!std::strncmp(ss,"end_t(",6)) { // End thread
20070               _cimg_mp_op("Function 'end_t()'");
20071               s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
20072               if (s1!=se1) {
20073                 const bool is_inside_end = (bool)(bloc_flags&16);
20074                 if (!is_inside_end) code.swap(code_end_t);
20075                 compile(s1,se1,depth1,p_ref,16);
20076                 if (!is_inside_end) code.swap(code_end_t);
20077                 is_end_code = true;
20078               }
20079               _cimg_mp_return_nan();
20080             }
20081             break;
20082 
20083           case 'f' :
20084             if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion
20085               _cimg_mp_op("Function 'f2ui()'");
20086               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20087               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1);
20088               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1]));
20089               _cimg_mp_scalar1(mp_f2ui,arg1);
20090             }
20091 
20092             if (!std::strncmp(ss,"fact(",5)) { // Factorial
20093               _cimg_mp_op("Function 'fact()'");
20094               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20095               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
20096               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1]));
20097               _cimg_mp_scalar1(mp_factorial,arg1);
20098             }
20099 
20100             if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
20101               _cimg_mp_op("Function 'fibo()'");
20102               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20103               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
20104               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1]));
20105               _cimg_mp_scalar1(mp_fibonacci,arg1);
20106             }
20107 
20108             if (!std::strncmp(ss,"fill(",5)) { // Fill
20109               _cimg_mp_op("Function 'fill()'");
20110               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20111               arg1 = compile(ss5,s0,depth1,0,bloc_flags); // Object to fill
20112               if (_cimg_mp_is_constant(arg1))
20113                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20114                                             "CImg<%s>::%s: %s: Target scalar is constant, "
20115                                             "in expression '%s%s%s'.",
20116                                             pixel_type(),_cimg_mp_calling_function,s_op,
20117                                             ss>expr._data?"...":"",ss,se<&expr.back()?"...":"");
20118               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20119               p1 = code._width;
20120 
20121               if (s1<se1) { // Version with 3 arguments
20122                 variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
20123                 cimg::strpare(variable_name,false,true);
20124                 if (!is_varname(variable_name)) { // Invalid variable name
20125                   cimg::strellipsize(variable_name,64);
20126                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
20127                                               "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
20128                                               "in expression '%s%s%s'.",
20129                                               pixel_type(),_cimg_mp_calling_function,s_op,
20130                                               variable_name._data,
20131                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20132                 }
20133                 get_variable_pos(variable_name,arg2,arg3);
20134                 arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
20135                 if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
20136                                   _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error
20137                   cimg::strellipsize(variable_name,64);
20138                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
20139                                               "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
20140                                               "(expected 'scalar'), in expression '%s%s%s'.",
20141                                               pixel_type(),_cimg_mp_calling_function,s_op,
20142                                               s_type(arg2)._data,variable_name._data,
20143                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20144                 } else if (arg2==~0U) { // Variable does not exist -> create it
20145                   arg2 = scalar();
20146                   if (arg3!=~0U) reserved_label[arg3] = arg2;
20147                   else {
20148                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
20149                     variable_pos[variable_def._width] = arg2;
20150                     variable_name.move_to(variable_def);
20151                   }
20152                   memtype[arg2] = -1;
20153                 }
20154                 arg3 = compile(++s1,se1,depth1,0,bloc_flags);
20155                 _cimg_mp_check_type(arg3,3,1,0);
20156               } else { // Version with 2 arguments
20157                 arg2 = ~0U;
20158                 arg3 = compile(s0,se1,depth1,0,bloc_flags);
20159               }
20160               // arg2 = variable slot, arg3 = fill expression.
20161               _cimg_mp_check_type(arg3,3,1,0);
20162               CImg<ulongT>::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1).
20163                 move_to(code,p1);
20164               _cimg_mp_return(arg1);
20165             }
20166 
20167             if (!std::strncmp(ss,"find(",5)) { // Find
20168               _cimg_mp_op("Function 'find()'");
20169 
20170               // First argument: data to look at.
20171               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20172               if (*ss5=='#') { // Index specified
20173                 p1 = compile(ss6,s0,depth1,0,bloc_flags);
20174                 _cimg_mp_check_list(false);
20175                 arg1 = ~0U;
20176               } else { // Vector specified
20177                 arg1 = compile(ss5,s0,depth1,0,bloc_flags);
20178                 _cimg_mp_check_type(arg1,1,2,0);
20179                 p1 = ~0U;
20180               }
20181 
20182               // Second argument: data to find.
20183               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20184               arg2 = compile(s0,s1,depth1,0,bloc_flags);
20185 
20186               // Third and fourth arguments: starting index and search direction.
20187               arg3 = _cimg_mp_slot_nan; arg4 = 1;
20188               if (s1<se1) {
20189                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20190                 arg3 = compile(++s1,s0,depth1,0,bloc_flags);
20191                 _cimg_mp_check_type(arg3,3,1,0);
20192                 if (s0<se1) {
20193                   arg4 = compile(++s0,se1,depth1,0,bloc_flags);
20194                   _cimg_mp_check_type(arg4,4,1,0);
20195                 }
20196               }
20197               if (p1!=~0U) {
20198                 if (_cimg_mp_size(arg2)>1)
20199                   _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
20200                 _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20201               }
20202               if (_cimg_mp_size(arg2)>1)
20203                 _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
20204               _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20205             }
20206 
20207             if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
20208               _cimg_mp_op("Function 'for()'");
20209               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20210               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20211               s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
20212               arg1 = code._width;
20213               p1 = compile(ss4,s1,depth1,0,bloc_flags); // Init
20214               arg2 = code._width;
20215               p2 = compile(++s1,s2,depth1,0,bloc_flags); // Cond
20216               arg3 = code._width;
20217               arg6 = mempos;
20218               if (s3<se1) { // Body + post
20219                 p3 = compile(s3 + 1,se1,depth1,0,bloc_flags); // Body
20220                 arg4 = code._width;
20221                 pos = compile(++s2,s3,depth1,0,bloc_flags); // Post
20222               } else {
20223                 p3 = compile(++s2,se1,depth1,0,bloc_flags); // Body only
20224                 arg4 = pos = code._width;
20225               }
20226               _cimg_mp_check_type(p2,2,1,0);
20227               arg5 = _cimg_mp_size(pos);
20228               CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
20229                                    arg4 - arg3,code._width - arg4,
20230                                    p3>=arg6 && !_cimg_mp_is_constant(p3),
20231                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
20232               _cimg_mp_return(p3);
20233             }
20234 
20235             if (!std::strncmp(ss,"floor(",6)) { // Floor
20236               _cimg_mp_op("Function 'floor()'");
20237               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20238               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
20239               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1]));
20240               _cimg_mp_scalar1(mp_floor,arg1);
20241             }
20242 
20243             if (!std::strncmp(ss,"fsize(",6)) { // File size
20244               _cimg_mp_op("Function 'fsize()'");
20245               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20246               _cimg_mp_check_type(arg1,1,2,0);
20247               pos = scalar();
20248               CImg<ulongT>::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20249               return_new_comp = true;
20250               _cimg_mp_return(pos);
20251             }
20252             break;
20253 
20254           case 'g' :
20255             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
20256               _cimg_mp_op("Function 'gauss()'");
20257               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20258               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
20259               arg2 = arg3 = 1;
20260               if (s1<se1) {
20261                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20262                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20263                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
20264               }
20265               _cimg_mp_check_type(arg2,2,1,0);
20266               _cimg_mp_check_type(arg3,3,1,0);
20267               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
20268               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
20269                 val1 = mem[arg1];
20270                 val2 = mem[arg2];
20271                 _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
20272               }
20273               _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
20274             }
20275 
20276             if (!std::strncmp(ss,"gcd(",4)) { // Gcd
20277               _cimg_mp_op("Function 'gcd()'");
20278               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20279               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20280               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
20281               _cimg_mp_check_type(arg1,1,1,0);
20282               _cimg_mp_check_type(arg2,2,1,0);
20283               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
20284                 _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
20285               _cimg_mp_scalar2(mp_gcd,arg1,arg2);
20286             }
20287 
20288 #ifdef cimg_mp_func_get
20289             if (!std::strncmp(ss,"get(",4)) { // Get value/vector from external variable
20290               _cimg_mp_op("Function 'get()'");
20291               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20292               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20293               arg2 = arg3 = 0;
20294               if (s1<se1) {
20295                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20296                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20297                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20298               }
20299               _cimg_mp_check_type(arg1,1,2,0);
20300               _cimg_mp_check_constant(arg2,2,2);
20301               _cimg_mp_check_type(arg3,3,1,0);
20302               p1 = _cimg_mp_size(arg1);
20303               arg2 = (unsigned int)mem[arg2];
20304               if (arg2) pos = vector(arg2); else pos = scalar();
20305               CImg<ulongT>::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code);
20306               return_new_comp = true;
20307               _cimg_mp_return(pos);
20308             }
20309 #endif
20310             break;
20311 
20312           case 'h' :
20313             if (*ss1=='(') { // Image height
20314               _cimg_mp_op("Function 'h()'");
20315               if (*ss2=='#') { // Index specified
20316                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
20317                 _cimg_mp_check_list(false);
20318               } else { if (ss2!=se1) break; p1 = ~0U; }
20319               _cimg_mp_scalar1(mp_image_h,p1);
20320             }
20321             break;
20322 
20323           case 'i' :
20324             if (*ss1=='c' && *ss2=='(') { // Image median
20325               _cimg_mp_op("Function 'ic()'");
20326               if (*ss3=='#') { // Index specified
20327                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
20328                 _cimg_mp_check_list(false);
20329               } else { if (ss3!=se1) break; p1 = ~0U; }
20330               pos = scalar();
20331               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
20332               return_new_comp = true;
20333               _cimg_mp_return(pos);
20334             }
20335 
20336             if (*ss1=='n' && *ss2=='(') { // Image norm
20337               _cimg_mp_op("Function 'in()'");
20338               if (*ss3=='#') { // Index specified
20339                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
20340                 _cimg_mp_check_list(false);
20341               } else { if (ss3!=se1) break; p1 = ~0U; }
20342               pos = scalar();
20343               CImg<ulongT>::vector((ulongT)mp_image_norm,pos,p1).move_to(code);
20344               return_new_comp = true;
20345               _cimg_mp_return(pos);
20346             }
20347 
20348             if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
20349               _cimg_mp_op("Function 'if()'");
20350               s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20351               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20352               arg1 = compile(ss3,s1,depth1,0,bloc_flags);
20353               _cimg_mp_check_type(arg1,1,1,0);
20354               if (_cimg_mp_is_constant(arg1)) {
20355                 if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,bloc_flags);
20356                 else return s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20357               }
20358               p2 = code._width;
20359               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20360               p3 = code._width;
20361               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):
20362                 _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
20363               _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
20364               arg4 = _cimg_mp_size(arg2);
20365               if (arg4) pos = vector(arg4); else pos = scalar();
20366               CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
20367                                   p3 - p2,code._width - p3,arg4).move_to(code,p2);
20368               return_new_comp = true;
20369               _cimg_mp_return(pos);
20370             }
20371 
20372             if (!std::strncmp(ss,"inrange(",8)) { // Check value range
20373               _cimg_mp_op("Function 'inrange()'");
20374               s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20375               arg1 = compile(ss8,s1,depth1,0,bloc_flags);
20376               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20377               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20378               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20379               arg3 = compile(++s2,s1,depth1,0,bloc_flags);
20380               arg4 = arg5 = 1;
20381               if (s1<se1) {
20382                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20383                 arg4 = compile(++s1,s2,depth1,0,bloc_flags);
20384                 arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):arg4;
20385                 _cimg_mp_check_type(arg4,4,1,0);
20386                 _cimg_mp_check_type(arg5,5,1,0);
20387               }
20388               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) &&
20389                   _cimg_mp_is_constant(arg3) && _cimg_mp_is_constant(arg4) &&
20390                   _cimg_mp_is_constant(arg5)) { // Optimize constant case
20391                 val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
20392                 if (val2>=val1)
20393                   is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val<val2));
20394                 else
20395                   is_sth = (mem[arg5]?(val>=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val<val1));
20396                 _cimg_mp_return(is_sth?1:0);
20397               }
20398               p1 = _cimg_mp_size(arg1);
20399               p2 = _cimg_mp_size(arg2);
20400               p3 = _cimg_mp_size(arg3);
20401               arg6 = ~0U; // Size of return value
20402               if (!p1) {
20403                 arg6 = p2?p2:p3;
20404                 if (arg6) _cimg_mp_check_type(arg3,3,3,arg6);
20405               } else {
20406                 arg6 = p1;
20407                 _cimg_mp_check_type(arg2,2,3,arg6);
20408                 _cimg_mp_check_type(arg3,3,3,arg6);
20409               }
20410               pos = arg6?vector(arg6):scalar();
20411               CImg<ulongT>::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code);
20412               return_new_comp = true;
20413               _cimg_mp_return(pos);
20414             }
20415 
20416             if (!std::strncmp(ss,"int(",4)) { // Integer cast
20417               _cimg_mp_op("Function 'int()'");
20418               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20419               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
20420               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]);
20421               _cimg_mp_scalar1(mp_int,arg1);
20422             }
20423 
20424             if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion
20425               _cimg_mp_op("Function 'invert()'");
20426               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20427               arg1 = compile(ss7,s1,depth1,0,bloc_flags);
20428               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
20429               _cimg_mp_check_type(arg2,2,1,0);
20430               if (_cimg_mp_is_vector(arg1)) {
20431                 _cimg_mp_check_matrix_square(arg1,1);
20432                 p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
20433                 pos = vector(p1*p1);
20434                 CImg<ulongT>::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code);
20435                 return_new_comp = true;
20436                 _cimg_mp_return(pos);
20437               }
20438               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]);
20439               _cimg_mp_scalar2(mp_div,1,arg1);
20440             }
20441 
20442             if (*ss1=='s') { // Family of 'is_?()' functions
20443 
20444               if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
20445                 _cimg_mp_op("Function 'isbool()'");
20446                 if (ss7==se1) _cimg_mp_return(0);
20447                 try { arg1 = compile(ss7,se1,depth1,0,bloc_flags); }
20448                 catch(CImgException&) { _cimg_mp_return(0); }
20449                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
20450                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.);
20451                 _cimg_mp_scalar1(mp_isbool,arg1);
20452               }
20453 
20454               if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
20455                 _cimg_mp_op("Function 'isdir()'");
20456                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20457                 if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0);
20458                 pos = scalar();
20459                 CImg<ulongT>::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20460                 return_new_comp = true;
20461                 _cimg_mp_return(pos);
20462               }
20463 
20464               if (!std::strncmp(ss,"isfile(",7)) { // Is file?
20465                 _cimg_mp_op("Function 'isfile()'");
20466                 arg1 = compile(ss7,se1,depth1,0,bloc_flags);
20467                 if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0);
20468                 pos = scalar();
20469                 CImg<ulongT>::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20470                 return_new_comp = true;
20471                 _cimg_mp_return(pos);
20472               }
20473 
20474               if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
20475                 if (ss5>=se1) _cimg_mp_return(0);
20476                 _cimg_mp_op("Function 'isin()'");
20477                 pos = scalar();
20478                 CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
20479                 for (s = ss5; s<se; ++s) {
20480                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20481                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20482                   arg1 = compile(s,ns,depth1,0,bloc_flags);
20483                   if (_cimg_mp_is_vector(arg1))
20484                     CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
20485                                            arg1 + (ulongT)_cimg_mp_size(arg1)).
20486                       move_to(l_opcode);
20487                   else CImg<ulongT>::vector(arg1).move_to(l_opcode);
20488                   s = ns;
20489                 }
20490                 (l_opcode>'y').move_to(opcode);
20491                 opcode[2] = opcode._height;
20492                 opcode.move_to(code);
20493                 return_new_comp = true;
20494                 _cimg_mp_return(pos);
20495               }
20496 
20497               if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
20498                 _cimg_mp_op("Function 'isinf()'");
20499                 if (ss6==se1) _cimg_mp_return(0);
20500                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20501                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
20502                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
20503                 _cimg_mp_scalar1(mp_isinf,arg1);
20504               }
20505 
20506               if (!std::strncmp(ss,"isint(",6)) { // Is integer?
20507                 _cimg_mp_op("Function 'isint()'");
20508                 if (ss6==se1) _cimg_mp_return(0);
20509                 try { arg1 = compile(ss6,se1,depth1,0,bloc_flags); }
20510                 catch(CImgException&) { _cimg_mp_return(0); }
20511                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
20512                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1]));
20513                 _cimg_mp_scalar1(mp_isint,arg1);
20514               }
20515 
20516               if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
20517                 _cimg_mp_op("Function 'isnan()'");
20518                 if (ss6==se1) _cimg_mp_return(0);
20519                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20520                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
20521                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
20522                 _cimg_mp_scalar1(mp_isnan,arg1);
20523               }
20524 
20525               if (!std::strncmp(ss,"isnum(",6)) { // Is number?
20526                 _cimg_mp_op("Function 'isnum()'");
20527                 val = 0;
20528                 if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
20529                 _cimg_mp_return(0);
20530               }
20531 
20532               if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression?
20533                 _cimg_mp_op("Function 'isexpr()'");
20534                 if (ss7==se1) _cimg_mp_return(0);
20535                 try { arg1 = compile(ss7,se1,depth1,0,bloc_flags); }
20536                 catch (CImgException&) { _cimg_mp_return(0); }
20537                 _cimg_mp_return(1);
20538               }
20539             }
20540             break;
20541 
20542           case 'l' :
20543             if (*ss1=='(') { // Size of image list
20544               _cimg_mp_op("Function 'l()'");
20545               if (ss2!=se1) break;
20546               _cimg_mp_scalar0(mp_list_l);
20547             }
20548 
20549             if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation
20550               _cimg_mp_op("Function 'lerp()'");
20551               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20552               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
20553               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20554               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20555               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):16; // Default value is 0.5
20556               _cimg_mp_check_type(arg3,3,1,0);
20557               if (_cimg_mp_is_constant(arg3)) { // Optimize constant cases
20558                 if (!arg3) _cimg_mp_return(arg1);
20559                 if (arg3==1) _cimg_mp_return(arg2);
20560                 if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) {
20561                   const double t = mem[arg3];
20562                   _cimg_mp_constant(mem[arg1]*(1-t) + mem[arg2]*t);
20563                 }
20564               }
20565               if (_cimg_mp_is_scalar(arg1)) {
20566                 _cimg_mp_check_type(arg2,2,1,0);
20567                 _cimg_mp_scalar3(mp_lerp,arg1,arg2,arg3);
20568               }
20569               p1 = _cimg_mp_size(arg1);
20570               _cimg_mp_check_type(arg2,2,2,p1);
20571               pos = vector(p1);
20572               CImg<ulongT>::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code);
20573               return_new_comp = true;
20574               _cimg_mp_return(pos);
20575             }
20576 
20577             if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
20578               _cimg_mp_op("Function 'log()'");
20579               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20580               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
20581               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1]));
20582               _cimg_mp_scalar1(mp_log,arg1);
20583             }
20584 
20585             if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
20586               _cimg_mp_op("Function 'log2()'");
20587               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20588               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
20589               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1]));
20590               _cimg_mp_scalar1(mp_log2,arg1);
20591             }
20592 
20593             if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
20594               _cimg_mp_op("Function 'log10()'");
20595               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20596               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
20597               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1]));
20598               _cimg_mp_scalar1(mp_log10,arg1);
20599             }
20600 
20601             if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
20602               _cimg_mp_op("Function 'lowercase()'");
20603               arg1 = compile(ss + 10,se1,depth1,0,bloc_flags);
20604               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
20605               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1]));
20606               _cimg_mp_scalar1(mp_lowercase,arg1);
20607             }
20608             break;
20609 
20610           case 'm' :
20611             if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
20612               _cimg_mp_op("Function 'mul()'");
20613               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20614               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20615               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20616               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20617               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
20618               _cimg_mp_check_type(arg1,1,2,0);
20619               _cimg_mp_check_type(arg2,2,2,0);
20620               _cimg_mp_check_constant(arg3,3,3);
20621               p1 = _cimg_mp_size(arg1);
20622               p2 = _cimg_mp_size(arg2);
20623               p3 = (unsigned int)mem[arg3];
20624               arg5 = p2/p3;
20625               arg4 = p1/arg5;
20626               if (arg4*arg5!=p1 || arg5*p3!=p2) {
20627                 _cimg_mp_strerr;
20628                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20629                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
20630                                             "do not match with third argument 'nb_colsB=%u', "
20631                                             "in expression '%s%s%s'.",
20632                                             pixel_type(),_cimg_mp_calling_function,s_op,
20633                                             s_type(arg1)._data,s_type(arg2)._data,p3,
20634                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20635               }
20636               pos = vector(arg4*p3);
20637               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
20638               return_new_comp = true;
20639               _cimg_mp_return(pos);
20640             }
20641 
20642             if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary
20643               _cimg_mp_op("Function 'mproj()'");
20644               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20645               arg1 = compile(ss6,s1,depth1,0,bloc_flags); // S
20646               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20647               arg2 = compile(++s1,s2,depth1,0,bloc_flags); // ncolS
20648               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20649               arg3 = compile(++s2,s1,depth1,0,bloc_flags); // D
20650               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20651               arg4 = compile(++s1,s2,depth1,0,bloc_flags); // ncolD
20652               arg5 = arg6 = p3 = 0;
20653               if (s2<se1) {
20654                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20655                 arg5 = compile(++s2,s1,depth1,0,bloc_flags); // method
20656                 if (s1<se1) {
20657                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20658                   arg6 = compile(++s1,s2,depth1,0,bloc_flags); // max_iter
20659                   p3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0; // method
20660                 }
20661               }
20662               _cimg_mp_check_type(arg1,1,2,0);
20663               _cimg_mp_check_constant(arg2,2,3);
20664               _cimg_mp_check_type(arg3,3,2,0);
20665               _cimg_mp_check_constant(arg4,4,3);
20666               _cimg_mp_check_type(arg5,5,1,0);
20667               _cimg_mp_check_type(arg6,6,1,0);
20668               _cimg_mp_check_type(p3,7,1,0);
20669               p1 = _cimg_mp_size(arg1);
20670               p2 = _cimg_mp_size(arg3);
20671               const unsigned int
20672                 wS = (unsigned int)mem[arg2],
20673                 wD = (unsigned int)mem[arg4],
20674                 hS = p1/wS,
20675                 hD = p2/wD;
20676 
20677               if (wS*hS!=p1) {
20678                 _cimg_mp_strerr;
20679                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20680                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20681                                             "do not match with second argument 'nb_colsS=%u', "
20682                                             "in expression '%s%s%s'.",
20683                                             pixel_type(),_cimg_mp_calling_function,s_op,
20684                                             s_type(arg1)._data,wS,
20685                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20686               }
20687               if (wD*hD!=p2) {
20688                 _cimg_mp_strerr;
20689                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20690                                             "CImg<%s>::%s: %s: Type of third argument ('%s') "
20691                                             "do not match with fourth argument 'nb_colsD=%u', "
20692                                             "in expression '%s%s%s'.",
20693                                             pixel_type(),_cimg_mp_calling_function,s_op,
20694                                             s_type(arg3)._data,wD,
20695                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20696               }
20697               if (hS!=hD) {
20698                 _cimg_mp_strerr;
20699                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20700                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20701                                             "do not match with third argument ('%s'), "
20702                                             "in expression '%s%s%s'.",
20703                                             pixel_type(),_cimg_mp_calling_function,s_op,
20704                                             s_type(arg1)._data,s_type(arg3)._data,
20705                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20706               }
20707               pos = vector(wS*wD);
20708               CImg<ulongT>::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code);
20709               return_new_comp = true;
20710               _cimg_mp_return(pos);
20711             }
20712 
20713             if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables
20714               _cimg_mp_op("Function 'merge()'");
20715               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20716               pos = compile(ss6,s1,depth1,0,bloc_flags);
20717               arg1 = ~0U; // Merge operator (0='=',1='+',2='-',3='*',4='/',5='min',6='max')
20718               if (s1<se1) {
20719                 ++s1;
20720                 char st_op[4] = { 0 };
20721                 is_sth = false; // blank after operator?
20722                 if (cimg_sscanf(s1," %3[=+-*/minax]%c",st_op,&sep)==2 && (sep==')' || (is_sth=cimg::is_blank(sep)))) {
20723                   if (!is_sth || (is_sth && cimg_sscanf(s1," %*[=+-*/minax ]%c",&sep)==1 && sep==')')) {
20724                     cimg::strpare(st_op,' ',false,true);
20725                     if (!st_op[1]) arg1 = *st_op=='='?0:*st_op=='+'?1:*st_op=='-'?2:*st_op=='*'?3:*st_op=='/'?4:~0U;
20726                     if (*st_op=='m' && st_op[1]=='i' && st_op[2]=='n' && !st_op[3]) arg1 = 5;
20727                     if (*st_op=='m' && st_op[1]=='a' && st_op[2]=='x' && !st_op[3]) arg1 = 6;
20728                   }
20729                 }
20730               }
20731               cimg_rofY(memmerge,k) if (memmerge(0,k)==(int)pos) {
20732                 _cimg_mp_strerr;
20733                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20734                                             "CImg<%s>::%s: %s: Merge has already been requested before "
20735                                             "for specified variable "
20736                                             "in expression '%s%s%s'.",
20737                                             pixel_type(),_cimg_mp_calling_function,s_op,
20738                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20739               }
20740               if (arg1==~0U) {
20741                 _cimg_mp_strerr;
20742                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20743                                             "CImg<%s>::%s: %s: Invalid specified operator "
20744                                             "(should be one of '=,+,-,*,/,min,max'), "
20745                                             "in expression '%s%s%s'.",
20746                                             pixel_type(),_cimg_mp_calling_function,s_op,
20747                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20748               }
20749               memmerge.resize(3,memmerge._height + 1,1,1,0,0);
20750               memmerge(0,memmerge._height - 1) = (int)pos;
20751               memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos);
20752               memmerge(2,memmerge._height - 1) = (int)arg1;
20753               _cimg_mp_return(pos);
20754             }
20755             break;
20756 
20757           case 'n' :
20758 #ifdef cimg_mp_func_name
20759             if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector
20760               _cimg_mp_op("Function 'name()'");
20761               if (*ss5=='#') { // Index specified
20762                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20763                 p1 = compile(ss6,s0++,depth1,0,bloc_flags);
20764                 is_sth = true; // is_index_specified?
20765                 _cimg_mp_check_list(false);
20766               } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
20767               arg1 = s0<se1?compile(s0,se1,depth1,0,bloc_flags):~0U;
20768               if (arg1!=~0U) {
20769                 _cimg_mp_check_constant(arg1,is_sth?2:1,3);
20770                 arg1 = (unsigned int)mem[arg1];
20771               } else arg1 = 1024;
20772               pos = vector(arg1);
20773               CImg<ulongT>::vector((ulongT)mp_name,pos,p1,arg1).move_to(code);
20774               return_new_comp = true;
20775               _cimg_mp_return(pos);
20776             }
20777 #endif
20778 
20779             if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
20780               _cimg_mp_op("Function 'narg()'");
20781               if (ss5>=se1) _cimg_mp_return(0);
20782               arg1 = 0;
20783               for (s = ss5; s<se; ++s) {
20784                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20785                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20786                 ++arg1; s = ns;
20787               }
20788               _cimg_mp_constant((double)arg1);
20789             }
20790 
20791             if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
20792                 !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
20793                 (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
20794               _cimg_mp_op("Function 'normP()'");
20795               if (*ss4=='(') { arg1 = 2; s = ss5; }
20796               else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
20797               else if (arg1==~0U) {
20798                 arg1 = compile(ss4,s++,depth1,0,bloc_flags);
20799                 _cimg_mp_check_constant(arg1,0,2);
20800                 arg1 = (unsigned int)mem[arg1];
20801               } else s = std::strchr(ss4,'(') + 1;
20802               pos = scalar();
20803               switch (arg1) {
20804               case 0 :
20805                 CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break;
20806               case 1 :
20807                 CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break;
20808               case 2 :
20809                 CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break;
20810               case ~0U :
20811                 CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break;
20812               default :
20813                 CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
20814                   move_to(l_opcode);
20815               }
20816               for ( ; s<se; ++s) {
20817                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20818                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20819                 arg2 = compile(s,ns,depth1,0,bloc_flags);
20820                 if (_cimg_mp_is_vector(arg2))
20821                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20822                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20823                     move_to(l_opcode);
20824                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20825                 s = ns;
20826               }
20827 
20828               (l_opcode>'y').move_to(opcode);
20829               if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
20830                 _cimg_mp_scalar1(mp_abs,opcode[3]);
20831               opcode[2] = opcode._height;
20832               opcode.move_to(code);
20833               return_new_comp = true;
20834               _cimg_mp_return(pos);
20835             }
20836             break;
20837 
20838           case 'p' :
20839             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
20840               _cimg_mp_op("Function 'permut()'");
20841               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20842               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20843               arg1 = compile(ss7,s1,depth1,0,bloc_flags);
20844               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20845               arg3 = compile(++s2,se1,depth1,0,bloc_flags);
20846               _cimg_mp_check_type(arg1,1,1,0);
20847               _cimg_mp_check_type(arg2,2,1,0);
20848               _cimg_mp_check_type(arg3,3,1,0);
20849               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
20850                 _cimg_mp_constant(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
20851               _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
20852             }
20853 
20854             if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
20855               if (!is_inside_critical) is_parallelizable = false;
20856               _cimg_mp_op("Function 'polygon()'");
20857               if (*ss8=='#') { // Index specified
20858                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20859                 p1 = compile(ss + 9,s0++,depth1,0,bloc_flags);
20860                 _cimg_mp_check_list(true);
20861               } else { p1 = ~0U; s0 = ss8; }
20862               if (s0==se1) compile(s0,se1,depth1,0,bloc_flags); // 'missing' argument error
20863               CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
20864               for (s = s0; s<se; ++s) {
20865                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20866                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20867                 arg2 = compile(s,ns,depth1,0,bloc_flags);
20868                 if (_cimg_mp_is_vector(arg2))
20869                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20870                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20871                     move_to(l_opcode);
20872                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20873                 s = ns;
20874               }
20875               (l_opcode>'y').move_to(opcode);
20876               opcode[2] = opcode._height;
20877               opcode.move_to(code);
20878               _cimg_mp_return_nan();
20879             }
20880 
20881             if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
20882               is_sth = ss[5]=='s'; // is prints()
20883               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
20884               s0 = is_sth?ss7:ss6;
20885               if (*s0!='#' || is_sth) { // Regular expression
20886                 for (s = s0; s<se; ++s) {
20887                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20888                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20889                   pos = compile(s,ns,depth1,p_ref,bloc_flags);
20890                   c1 = *ns; *ns = 0;
20891                   variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
20892                   cimg::strpare(variable_name,false,true);
20893                   if (_cimg_mp_is_vector(pos)) // Vector
20894                     ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
20895                       variable_name)>'y').move_to(opcode);
20896                   else // Scalar
20897                     ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
20898                       variable_name)>'y').move_to(opcode);
20899                   opcode[2] = opcode._height;
20900                   opcode.move_to(code);
20901                   *ns = c1; s = ns;
20902                 }
20903                 _cimg_mp_return(pos);
20904               } else { // Image
20905                 p1 = compile(ss7,se1,depth1,0,bloc_flags);
20906                 _cimg_mp_check_list(true);
20907                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
20908                 _cimg_mp_return_nan();
20909               }
20910             }
20911 
20912             if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion
20913               _cimg_mp_op("Function 'pseudoinvert()'");
20914               s1 = ss + 13; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20915               arg1 = compile(ss + 13,s1,depth1,0,bloc_flags);
20916               arg2 = 1;
20917               arg3 = 0;
20918               if (s1<se1) {
20919                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20920                 arg2 = compile(s1,s2,depth1,0,bloc_flags);
20921                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20922               }
20923               _cimg_mp_check_type(arg1,1,2,0);
20924               _cimg_mp_check_constant(arg2,2,3);
20925               _cimg_mp_check_type(arg3,3,1,0);
20926               p1 = _cimg_mp_size(arg1);
20927               p2 = (unsigned int)mem[arg2];
20928               p3 = p1/p2;
20929               if (p3*p2!=p1) {
20930                 _cimg_mp_strerr;
20931                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20932                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20933                                             "does not match with second argument 'nb_colsA=%u', "
20934                                             "in expression '%s%s%s'.",
20935                                             pixel_type(),_cimg_mp_calling_function,s_op,
20936                                             s_type(arg1)._data,p2,
20937                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20938               }
20939               pos = vector(p1);
20940               CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code);
20941               return_new_comp = true;
20942               _cimg_mp_return(pos);
20943             }
20944             break;
20945 
20946           case 'r' :
20947             if (!std::strncmp(ss,"rad2deg(",8)) { // Degrees to radians
20948               _cimg_mp_op("Function 'rad2deg()'");
20949               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
20950               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_rad2deg,arg1);
20951               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]*180/cimg::PI);
20952               _cimg_mp_scalar1(mp_rad2deg,arg1);
20953             }
20954 
20955             if (!std::strncmp(ss,"ref(",4)) { // Variable declaration
20956               _cimg_mp_op("Function 'ref()'");
20957               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20958               if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,bloc_flags); // Will throw missing argument error
20959               arg3 = compile(ss4,s1++,depth1,p_ref,bloc_flags);
20960               *se1 = 0;
20961 
20962               if (!is_varname(s1)) { // Invalid variable name
20963                 variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0;
20964                 cimg::strellipsize(variable_name,64);
20965                 *se1 = ')';
20966                 _cimg_mp_strerr;
20967                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20968                                             "CImg<%s>::%s: %s: Invalid specified variable name '%s', "
20969                                             "in expression '%s%s%s'.",
20970                                             pixel_type(),_cimg_mp_calling_function,s_op,
20971                                             variable_name._data,
20972                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20973 
20974               }
20975               get_variable_pos(s1,arg1,arg2);
20976               if (arg2!=~0U) reserved_label[arg2] = arg3;
20977               else if (arg1!=~0U) variable_pos[arg1] = arg3;
20978               else { // New variable
20979                 if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
20980                 variable_pos[variable_def._width] = arg3;
20981                 CImg<char>::string(s1).move_to(variable_def);
20982               }
20983               if (_cimg_mp_is_vector(arg3))
20984                 set_reserved_vector(arg3); // Prevent from being used in further optimization
20985               else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
20986               *se1 = ')';
20987               _cimg_mp_return(arg3);
20988             }
20989 
20990             if (!std::strncmp(ss,"repeat(",7)) { // Repeat
20991               _cimg_mp_op("Function 'repeat()'");
20992               s0 = ss7; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20993               arg1 = compile(ss7,s0,depth1,0,bloc_flags); // Number of iterations
20994               _cimg_mp_check_type(arg1,1,1,0);
20995               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20996               p1 = code._width;
20997 
20998               if (s1<se1) { // Version with 3 arguments
20999                 variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
21000                 cimg::strpare(variable_name,false,true);
21001                 if (!is_varname(variable_name)) { // Invalid variable name
21002                   cimg::strellipsize(variable_name,64);
21003                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21004                                               "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
21005                                               "in expression '%s%s%s'.",
21006                                               pixel_type(),_cimg_mp_calling_function,s_op,
21007                                               variable_name._data,
21008                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21009                 }
21010                 get_variable_pos(variable_name,arg2,arg3);
21011                 arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
21012                 if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
21013                                   _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error
21014                   cimg::strellipsize(variable_name,64);
21015                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21016                                               "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
21017                                               "(expected 'scalar'), in expression '%s%s%s'.",
21018                                               pixel_type(),_cimg_mp_calling_function,s_op,
21019                                               s_type(arg2)._data,variable_name._data,
21020                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21021                 } else if (arg2==~0U) { // Variable does not exist -> create it
21022                   arg2 = scalar();
21023                   if (arg3!=~0U) reserved_label[arg3] = arg2;
21024                   else {
21025                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
21026                     variable_pos[variable_def._width] = arg2;
21027                     variable_name.move_to(variable_def);
21028                   }
21029                   memtype[arg2] = -1;
21030                 }
21031                 arg3 = compile(++s1,se1,depth1,0,bloc_flags);
21032               } else { // Version with 2 arguments
21033                 arg2 = ~0U;
21034                 arg3 = compile(s0,se1,depth1,0,bloc_flags);
21035               }
21036               // arg2 = variable slot, arg3 = fill expression.
21037               CImg<ulongT>::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1);
21038               _cimg_mp_return(arg3);
21039             }
21040 
21041             if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
21042               _cimg_mp_op("Function 'resize()'");
21043               if (*ss7!='#') { // Vector
21044                 s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21045                 arg1 = compile(ss7,s1,depth1,0,bloc_flags);
21046                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21047                 arg2 = compile(s1,s2,depth1,0,bloc_flags);
21048                 arg3 = 1;
21049                 arg4 = 0;
21050                 if (s2<se1) {
21051                   s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21052                   arg3 = compile(s2,s1,depth1,0,bloc_flags);
21053                   arg4 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):0;
21054                 }
21055                 _cimg_mp_check_constant(arg2,2,3);
21056                 arg2 = (unsigned int)mem[arg2];
21057                 _cimg_mp_check_type(arg3,3,1,0);
21058                 _cimg_mp_check_type(arg4,4,1,0);
21059                 pos = vector(arg2);
21060                 CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1),
21061                                      arg3,arg4).move_to(code);
21062                 return_new_comp = true;
21063                 _cimg_mp_return(pos);
21064 
21065               } else { // Image
21066                 if (!is_inside_critical) is_parallelizable = false;
21067                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21068                 p1 = compile(ss8,s0++,depth1,0,bloc_flags);
21069                 _cimg_mp_check_list(true);
21070                 l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
21071                 CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
21072                   move_to(l_opcode);
21073                 pos = 0;
21074                 for (s = s0; s<se && pos<10; ++s) {
21075                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21076                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21077                   arg1 = compile(s,ns,depth1,0,bloc_flags);
21078                   _cimg_mp_check_type(arg1,pos + 2,1,0);
21079                   l_opcode(0,pos + 3) = arg1;
21080                   s = ns;
21081                   ++pos;
21082                 }
21083                 if (pos<1 || pos>10) {
21084                   _cimg_mp_strerr;
21085                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21086                                               "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.",
21087                                               pixel_type(),_cimg_mp_calling_function,s_op,
21088                                               pos<1?"Missing":"Too much",
21089                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21090                 }
21091                 l_opcode[0].move_to(code);
21092                 _cimg_mp_return_nan();
21093               }
21094             }
21095 
21096             if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
21097               _cimg_mp_op("Function 'reverse()'");
21098               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
21099               if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
21100               p1 = _cimg_mp_size(arg1);
21101               pos = vector(p1);
21102               CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
21103               return_new_comp = true;
21104               _cimg_mp_return(pos);
21105             }
21106 
21107             if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
21108               _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
21109               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
21110               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21111               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21112               _cimg_mp_check_type(arg2,2,1,0);
21113               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
21114               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
21115                 _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
21116                                   cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
21117               _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
21118             }
21119 
21120             if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix
21121               _cimg_mp_op("Function 'rot()'");
21122               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21123               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21124               if (s1<se1) { // 3D rotation
21125                 _cimg_mp_check_type(arg1,1,3,3);
21126                 is_sth = false; // Is coordinates as vector?
21127                 if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
21128                   is_sth = true;
21129                   p2 = _cimg_mp_size(arg1);
21130                   ++arg1;
21131                   arg2 = arg3 = 0;
21132                   if (p2>1) {
21133                     arg2 = arg1 + 1;
21134                     if (p2>2) arg3 = arg2 + 1;
21135                   }
21136                   arg4 = compile(++s1,se1,depth1,0,bloc_flags);
21137                 } else {
21138                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21139                   arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21140                   s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
21141                   arg3 = compile(++s2,s3,depth1,0,bloc_flags);
21142                   arg4 = compile(++s3,se1,depth1,0,bloc_flags);
21143                   _cimg_mp_check_type(arg2,2,1,0);
21144                   _cimg_mp_check_type(arg3,3,1,0);
21145                 }
21146                 _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
21147                 pos = vector(9);
21148                 CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
21149               } else { // 2D rotation
21150                 _cimg_mp_check_type(arg1,1,1,0);
21151                 pos = vector(4);
21152                 CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
21153               }
21154               return_new_comp = true;
21155               _cimg_mp_return(pos);
21156             }
21157 
21158             if (!std::strncmp(ss,"round(",6)) { // Value rounding
21159               _cimg_mp_op("Function 'round()'");
21160               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21161               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21162               arg2 = 1;
21163               arg3 = 0;
21164               if (s1<se1) {
21165                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21166                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21167                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21168               }
21169               _cimg_mp_check_type(arg2,2,1,0);
21170               _cimg_mp_check_type(arg3,3,1,0);
21171               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
21172               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
21173                 _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
21174               _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
21175             }
21176 
21177 #ifdef cimg_mp_func_run
21178             if (!std::strncmp(ss,"run(",4)) { // Run external command
21179               _cimg_mp_op("Function 'run()'");
21180               if (!is_inside_critical) is_parallelizable = false;
21181               CImg<ulongT>::vector((ulongT)mp_run,0,0).move_to(l_opcode);
21182               pos = 1;
21183               for (s = ss4; s<se; ++s) {
21184                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21185                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21186                 arg1 = compile(s,ns,depth1,0,bloc_flags);
21187                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
21188                 s = ns;
21189               }
21190               (l_opcode>'y').move_to(opcode);
21191               pos = scalar();
21192               opcode[1] = pos;
21193               opcode[2] = opcode._height;
21194               opcode.move_to(code);
21195               return_new_comp = true;
21196               _cimg_mp_return(pos);
21197             }
21198 #endif
21199             break;
21200 
21201           case 's' :
21202             if (*ss1=='(') { // Image spectrum
21203               _cimg_mp_op("Function 's()'");
21204               if (*ss2=='#') { // Index specified
21205                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
21206                 _cimg_mp_check_list(false);
21207               } else { if (ss2!=se1) break; p1 = ~0U; }
21208               _cimg_mp_scalar1(mp_image_s,p1);
21209             }
21210 
21211             if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
21212               _cimg_mp_op("Function 'same()'");
21213               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21214               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21215               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21216               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21217               arg3 = 11;
21218               arg4 = 1;
21219               if (s2<se1) {
21220                 s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
21221                 arg3 = compile(++s2,s3,depth1,0,bloc_flags);
21222                 _cimg_mp_check_type(arg3,3,1,0);
21223                 arg4 = s3<se1?compile(++s3,se1,depth1,0,bloc_flags):1;
21224               }
21225               p1 = _cimg_mp_size(arg1);
21226               p2 = _cimg_mp_size(arg2);
21227               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
21228             }
21229 
21230 #ifdef cimg_mp_func_set
21231             if (!std::strncmp(ss,"set(",4)) { // Set value/vector to external variable
21232               _cimg_mp_op("Function 'set()'");
21233               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21234               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21235               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
21236               _cimg_mp_check_type(arg2,2,2,0);
21237               p1 = _cimg_mp_size(arg1);
21238               p2 = _cimg_mp_size(arg2);
21239               CImg<ulongT>::vector((ulongT)mp_set,arg1,p1,arg2,p2).move_to(code);
21240               _cimg_mp_return(arg1);
21241             }
21242 #endif
21243 
21244             if (!std::strncmp(ss,"shift(",6)) { // Shift vector
21245               _cimg_mp_op("Function 'shift()'");
21246               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21247               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21248               arg2 = 1; arg3 = 0;
21249               if (s1<se1) {
21250                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21251                 arg2 = compile(s1,s0,depth1,0,bloc_flags);
21252                 arg3 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):0;
21253               }
21254               _cimg_mp_check_type(arg1,1,2,0);
21255               _cimg_mp_check_type(arg2,2,1,0);
21256               _cimg_mp_check_type(arg3,3,1,0);
21257               p1 = _cimg_mp_size(arg1);
21258               pos = vector(p1);
21259               CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
21260               return_new_comp = true;
21261               _cimg_mp_return(pos);
21262             }
21263 
21264             if (!std::strncmp(ss,"sign(",5)) { // Sign
21265               _cimg_mp_op("Function 'sign()'");
21266               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21267               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
21268               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1]));
21269               _cimg_mp_scalar1(mp_sign,arg1);
21270             }
21271 
21272             if (!std::strncmp(ss,"sin(",4)) { // Sine
21273               _cimg_mp_op("Function 'sin()'");
21274               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21275               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
21276               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1]));
21277               _cimg_mp_scalar1(mp_sin,arg1);
21278             }
21279 
21280             if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
21281               _cimg_mp_op("Function 'sinc()'");
21282               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21283               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
21284               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1]));
21285               _cimg_mp_scalar1(mp_sinc,arg1);
21286             }
21287 
21288             if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
21289               _cimg_mp_op("Function 'sinh()'");
21290               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21291               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
21292               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1]));
21293               _cimg_mp_scalar1(mp_sinh,arg1);
21294             }
21295 
21296             if (!std::strncmp(ss,"size(",5)) { // Vector size
21297               _cimg_mp_op("Function 'size()'");
21298               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21299               _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
21300             }
21301 
21302             if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system
21303               _cimg_mp_op("Function 'solve()'");
21304               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21305               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21306               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21307               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21308               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
21309               _cimg_mp_check_type(arg1,1,2,0);
21310               _cimg_mp_check_type(arg2,2,2,0);
21311               _cimg_mp_check_constant(arg3,3,3);
21312               p1 = _cimg_mp_size(arg1);
21313               p2 = _cimg_mp_size(arg2);
21314               p3 = (unsigned int)mem[arg3];
21315               arg5 = p2/p3;
21316               arg4 = p1/arg5;
21317               if (arg4*arg5!=p1 || arg5*p3!=p2) {
21318                 _cimg_mp_strerr;
21319                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21320                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
21321                                             "do not match with third argument 'nb_colsB=%u', "
21322                                             "in expression '%s%s%s'.",
21323                                             pixel_type(),_cimg_mp_calling_function,s_op,
21324                                             s_type(arg1)._data,s_type(arg2)._data,p3,
21325                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21326               }
21327               pos = vector(arg4*p3);
21328               CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
21329               return_new_comp = true;
21330               _cimg_mp_return(pos);
21331             }
21332 
21333             if (!std::strncmp(ss,"sort(",5)) { // Sort vector
21334               _cimg_mp_op("Function 'sort()'");
21335               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21336               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21337               arg2 = arg4 = 1; arg3 = ~0U;
21338               if (s1<se1) {
21339                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21340                 arg2 = compile(s1,s0,depth1,0,bloc_flags);
21341                 if (s0<se1) {
21342                   s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21343                   arg3 = compile(s0,s1,depth1,0,bloc_flags);
21344                   arg4 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21345                 }
21346               }
21347               _cimg_mp_check_type(arg1,1,2,0);
21348               _cimg_mp_check_type(arg2,2,1,0);
21349               if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
21350               _cimg_mp_check_type(arg4,4,1,0);
21351               p1 = _cimg_mp_size(arg1);
21352               pos = vector(p1);
21353               CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
21354               return_new_comp = true;
21355               _cimg_mp_return(pos);
21356             }
21357 
21358             if (!std::strncmp(ss,"sqr(",4)) { // Square
21359               _cimg_mp_op("Function 'sqr()'");
21360               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21361               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
21362               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1]));
21363               _cimg_mp_scalar1(mp_sqr,arg1);
21364             }
21365 
21366             if (!std::strncmp(ss,"sqrt(",5)) { // Square root
21367               _cimg_mp_op("Function 'sqrt()'");
21368               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21369               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
21370               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1]));
21371               _cimg_mp_scalar1(mp_sqrt,arg1);
21372             }
21373 
21374             if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
21375               _cimg_mp_op("Function 'srand()'");
21376               arg1 = ss6<se1?compile(ss6,se1,depth1,0,bloc_flags):~0U;
21377               if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
21378               _cimg_mp_scalar0(mp_srand0);
21379             }
21380 
21381             if (!std::strncmp(ss,"stats(",6)) { // Image statistics
21382               _cimg_mp_op("Function 'stats()'");
21383               if (*ss6=='#') { // Index specified
21384                 p1 = compile(ss7,se1,depth1,0,bloc_flags);
21385                 _cimg_mp_check_list(false);
21386               } else { if (ss6!=se1) break; p1 = ~0U; }
21387               pos = vector(14);
21388               CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
21389               return_new_comp = true;
21390               _cimg_mp_return(pos);
21391             }
21392 
21393 #ifdef cimg_mp_func_store
21394             if (!std::strncmp(ss,"store(",6)) { // Store vector to variable
21395               _cimg_mp_op("Function 'store()'");
21396               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21397               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21398               p1 = _cimg_mp_size(arg1);
21399               p3 = std::max(1U,p1);
21400               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21401               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21402               _cimg_mp_check_type(arg2,2,2,0);
21403               p2 = _cimg_mp_size(arg2);
21404               arg3 = ~0U; arg4 = arg5 = arg6 = 1U; pos = 0;
21405               if (s2<se1) {
21406                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21407                 arg3 = compile(++s2,s1,depth1,0,bloc_flags);
21408                 _cimg_mp_check_type(arg3,3,1,0);
21409                 arg4 = arg5 = arg6 = 1U;
21410                 if (s1<se1) {
21411                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21412                   arg4 = compile(++s1,s2,depth1,0,bloc_flags);
21413                   _cimg_mp_check_type(arg4,4,1,0);
21414                   if (s2<se1) {
21415                     s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21416                     arg5 = compile(++s2,s1,depth1,0,bloc_flags);
21417                     _cimg_mp_check_type(arg5,5,1,0);
21418                     if (s1<se1) {
21419                       s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21420                       arg6 = compile(++s1,s2,depth1,0,bloc_flags);
21421                       pos = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21422                       _cimg_mp_check_type(arg6,6,1,0);
21423                       _cimg_mp_check_type(pos,7,1,0);
21424                     }
21425                   }
21426                 }
21427               }
21428               if (arg3==~0U) arg3 = constant(p3);
21429               CImg<ulongT>::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2,
21430                                    arg3,arg4,arg5,arg6,pos).move_to(code);
21431               _cimg_mp_return_nan();
21432             }
21433 #endif
21434 
21435             if (!std::strncmp(ss,"stov(",5)) { // String to double
21436               _cimg_mp_op("Function 'stov()'");
21437               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21438               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21439               arg2 = arg3 = 0;
21440               if (s1<se1) {
21441                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21442                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21443                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21444               }
21445               _cimg_mp_check_type(arg2,2,1,0);
21446               _cimg_mp_check_type(arg3,3,1,0);
21447               p1 = _cimg_mp_size(arg1);
21448               pos = scalar();
21449               CImg<ulongT>::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code);
21450               return_new_comp = true;
21451               _cimg_mp_return(pos);
21452             }
21453 
21454             if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments
21455               _cimg_mp_op("Function 'string()'");
21456               CImg<ulongT>::vector((ulongT)mp_string,0,0,0).move_to(l_opcode);
21457 
21458               if (*ss7=='#') { // Output vector size specified, with '#'
21459                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21460                 arg1 = compile(ss8,s0++,depth1,0,bloc_flags);
21461                 _cimg_mp_check_constant(arg1,1,3);
21462                 arg1 = (unsigned int)mem[arg1];
21463                 s = s0;
21464               } else { arg1=~0U; s = ss7; }
21465 
21466               p1 = 0;
21467               for (; s<se; ++s) {
21468                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21469                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21470                 arg2 = compile(s,ns,depth1,0,bloc_flags);
21471                 p2 = _cimg_mp_size(arg2);
21472                 if (p2) p1+=p2;
21473                 else {
21474                   if (_cimg_mp_is_constant(arg2)) p1+=cimg_snprintf(variable_name.assign(24),24,"%.17g",mem[arg2]);
21475                   else p1+=23;
21476                 }
21477                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21478                 s = ns;
21479               }
21480               if (arg1==~0U) arg1 = p1;
21481               pos = vector(arg1,0);
21482               (l_opcode>'y').move_to(opcode);
21483               opcode[1] = pos;
21484               opcode[2] = arg1;
21485               opcode[3] = opcode._height;
21486               opcode.move_to(code);
21487               return_new_comp = true;
21488               _cimg_mp_return(pos);
21489             }
21490 
21491             if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
21492               _cimg_mp_op("Function 'svd()'");
21493               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21494               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21495               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21496               _cimg_mp_check_type(arg1,1,2,0);
21497               _cimg_mp_check_constant(arg2,2,3);
21498               p1 = _cimg_mp_size(arg1);
21499               p2 = (unsigned int)mem[arg2];
21500               p3 = p1/p2;
21501               if (p3*p2!=p1) {
21502                 _cimg_mp_strerr;
21503                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21504                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
21505                                             "does not match with second argument 'nb_colsA=%u', "
21506                                             "in expression '%s%s%s'.",
21507                                             pixel_type(),_cimg_mp_calling_function,s_op,
21508                                             s_type(arg1)._data,p2,
21509                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21510               }
21511               pos = vector(p1 + p2 + p2*p2);
21512               CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
21513               return_new_comp = true;
21514               _cimg_mp_return(pos);
21515             }
21516 
21517             if (!std::strncmp(ss,"swap(",5)) { // Swap values
21518               _cimg_mp_op("Function 'swap()'");
21519               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21520               ref.assign(14);
21521               arg1 = compile(ss5,s1,depth1,ref,bloc_flags);
21522               arg2 = compile(++s1,se1,depth1,ref._data + 7,bloc_flags);
21523               p1 = _cimg_mp_size(arg1);
21524               _cimg_mp_check_type(arg2,2,p1?2:1,p1);
21525               if (_cimg_mp_is_constant(arg1) || _cimg_mp_is_constant(arg2)) {
21526                 _cimg_mp_strerr;
21527                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21528                                             "CImg<%s>::%s: %s: %s argument cannot be a constant, "
21529                                             "in expression '%s%s%s'.",
21530                                             pixel_type(),_cimg_mp_calling_function,s_op,
21531                                             _cimg_mp_is_constant(arg1)?"First":"Second",
21532                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21533               }
21534               CImg<ulongT>::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code);
21535 
21536               // Write back values of linked arg1 and arg2.
21537               const unsigned int *_ref = ref;
21538               is_sth = true; // Is first argument?
21539               do {
21540                 switch (*_ref) {
21541                 case 1 : // arg1: V[k]
21542                   arg3 = _ref[1]; // Vector slot
21543                   arg4 = _ref[2]; // Index
21544                   CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
21545                     move_to(code);
21546                   break;
21547                 case 2 : // arg1: i/j[_#ind,off]
21548                   if (!is_inside_critical) is_parallelizable = false;
21549                   p1 = _ref[1]; // Index
21550                   is_relative = (bool)_ref[2];
21551                   arg3 = _ref[3]; // Offset
21552                   if (p1!=~0U) {
21553                     if (listout)
21554                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
21555                                            arg1,p1,arg3).move_to(code);
21556                   } else {
21557                     if (imgout)
21558                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
21559                                            arg1,arg3).move_to(code);
21560                   }
21561                   break;
21562                 case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c)
21563                   if (!is_inside_critical) is_parallelizable = false;
21564                   p1 = _ref[1]; // Index
21565                   is_relative = (bool)_ref[2];
21566                   arg3 = _ref[3]; // X
21567                   arg4 = _ref[4]; // Y
21568                   arg5 = _ref[5]; // Z
21569                   arg6 = _ref[6]; // C
21570                   if (p1!=~0U) {
21571                     if (listout)
21572                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
21573                                            arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
21574                   } else {
21575                     if (imgout)
21576                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
21577                                            arg1,arg3,arg4,arg5,arg6).move_to(code);
21578                   }
21579                   break;
21580               case 4: // arg1: I/J[_#ind,off]
21581                 if (!is_inside_critical) is_parallelizable = false;
21582                 p1 = _ref[1]; // Index
21583                 is_relative = (bool)_ref[2];
21584                 arg3 = _ref[3]; // Offset
21585                 if (p1!=~0U) {
21586                   if (listout) {
21587                     if (_cimg_mp_is_scalar(arg1))
21588                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
21589                                            arg1,p1,arg3).move_to(code);
21590                     else {
21591                       _cimg_mp_check_constant_index(p1);
21592                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
21593                                            arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
21594                     }
21595                   }
21596                 } else {
21597                   if (imgout) {
21598                     if (_cimg_mp_is_scalar(arg1))
21599                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
21600                                            arg1,arg3).move_to(code);
21601                     else
21602                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
21603                                            arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
21604                   }
21605                 }
21606                 break;
21607                 case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c)
21608                   if (!is_inside_critical) is_parallelizable = false;
21609                   p1 = _ref[1]; // Index
21610                   is_relative = (bool)_ref[2];
21611                   arg3 = _ref[3]; // X
21612                   arg4 = _ref[4]; // Y
21613                   arg5 = _ref[5]; // Z
21614                   if (p1!=~0U) {
21615                     if (listout) {
21616                       if (_cimg_mp_is_scalar(arg1))
21617                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
21618                                              arg1,p1,arg3,arg4,arg5).move_to(code);
21619                       else {
21620                         _cimg_mp_check_constant_index(p1);
21621                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
21622                                              arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21623                       }
21624                     }
21625                   } else {
21626                     if (imgout) {
21627                       if (_cimg_mp_is_scalar(arg1))
21628                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
21629                                              arg1,arg3,arg4,arg5).move_to(code);
21630                       else
21631                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
21632                                              arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21633                     }
21634                   }
21635                   break;
21636                 }
21637 
21638                 _ref+=7;
21639                 arg1 = arg2;
21640                 is_sth = !is_sth;
21641               } while (!is_sth);
21642 
21643               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
21644               _cimg_mp_return(arg1);
21645             }
21646             break;
21647 
21648           case 't' :
21649             if (!std::strncmp(ss,"tan(",4)) { // Tangent
21650               _cimg_mp_op("Function 'tan()'");
21651               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21652               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
21653               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1]));
21654               _cimg_mp_scalar1(mp_tan,arg1);
21655             }
21656 
21657             if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
21658               _cimg_mp_op("Function 'tanh()'");
21659               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21660               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
21661               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1]));
21662               _cimg_mp_scalar1(mp_tanh,arg1);
21663             }
21664 
21665             if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
21666               _cimg_mp_op("Function 'trace()'");
21667               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
21668               _cimg_mp_check_matrix_square(arg1,1);
21669               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
21670               _cimg_mp_scalar2(mp_trace,arg1,p1);
21671             }
21672 
21673             if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose
21674               _cimg_mp_op("Function 'transpose()'");
21675               s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21676               arg1 = compile(ss + 10,s1,depth1,0,bloc_flags);
21677               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
21678               _cimg_mp_check_type(arg1,1,2,0);
21679               _cimg_mp_check_constant(arg2,2,3);
21680               p1 = _cimg_mp_size(arg1);
21681               p2 = (unsigned int)mem[arg2];
21682               p3 = p1/p2;
21683               if (p2*p3!=p1) {
21684                 _cimg_mp_strerr;
21685                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21686                                             "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
21687                                             "second argument 'nb_cols=%u', in expression '%s%s%s'.",
21688                                             pixel_type(),_cimg_mp_calling_function,s_op,
21689                                             s_type(arg1)._data,p2,
21690                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21691               }
21692               pos = vector(p3*p2);
21693               CImg<ulongT>::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code);
21694               return_new_comp = true;
21695               _cimg_mp_return(pos);
21696             }
21697             break;
21698 
21699           case 'u' :
21700             if (*ss1=='(') { // Random value with uniform distribution
21701               _cimg_mp_op("Function 'u()'");
21702               if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
21703               s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21704               arg1 = compile(ss2,s1,depth1,0,bloc_flags);
21705               if (s1<se1) arg2 = compile(++s1,se1,depth1,0,bloc_flags); else { arg2 = arg1; arg1 = 0; }
21706               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
21707               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
21708               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
21709               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
21710               _cimg_mp_scalar2(mp_u,arg1,arg2);
21711             }
21712 
21713             if (!std::strncmp(ss,"ui2f(",5)) { // Special uint->float conversion
21714               _cimg_mp_op("Function 'ui2f()'");
21715               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21716               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1);
21717               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1]));
21718               _cimg_mp_scalar1(mp_ui2f,arg1);
21719             }
21720 
21721             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
21722               _cimg_mp_op("Function 'unref()'");
21723               arg1=~0U;
21724               for (s0 = ss6; s0<se1; s0 = s1) {
21725                 if (s0>ss6 && *s0==',') ++s0;
21726                 s1 = s0; while (s1<se1 && *s1!=',') ++s1;
21727                 c1 = *s1;
21728                 if (s1>s0) {
21729                   *s1 = 0;
21730                   get_variable_pos(s0,arg1,arg2);
21731                   if (arg2!=~0U) reserved_label[arg2] = ~0U;
21732                   else if (arg1!=~0U) {
21733                     variable_def.remove(arg1);
21734                     if (arg1<variable_pos._width - 1)
21735                       std::memmove(variable_pos._data + arg1,variable_pos._data + arg1 + 1,
21736                                    sizeof(uintT)*(variable_pos._width - arg1 - 1));
21737                     --variable_pos._width;
21738                   }
21739                   *s1 = c1;
21740                 } else compile(s0,s1,depth1,0,bloc_flags); // Will throw a 'missing argument' exception
21741               }
21742               _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable
21743             }
21744 
21745             if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
21746               _cimg_mp_op("Function 'uppercase()'");
21747               arg1 = compile(ss + 10,se1,depth1,0,bloc_flags);
21748               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
21749               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1]));
21750               _cimg_mp_scalar1(mp_uppercase,arg1);
21751             }
21752             break;
21753 
21754           case 'v' :
21755             if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
21756                 !std::strncmp(ss,"vector(",7) ||
21757                 (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
21758               _cimg_mp_op("Function 'vector()'");
21759               arg2 = 0; // Number of specified values
21760               if (arg1==~0U && *ss6!='(') {
21761                 arg1 = compile(ss6,s++,depth1,0,bloc_flags);
21762                 _cimg_mp_check_constant(arg1,0,3);
21763                 arg1 = (unsigned int)mem[arg1];
21764               } else s = std::strchr(ss6,'(') + 1;
21765 
21766               if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#'
21767                 s0 = ++s; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21768                 arg1 = compile(s,s0++,depth1,0,bloc_flags);
21769                 _cimg_mp_check_constant(arg1,1,3);
21770                 arg1 = (unsigned int)mem[arg1];
21771                 s = s0;
21772               }
21773 
21774               if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
21775                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21776                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21777                   arg3 = compile(s,ns,depth1,0,bloc_flags);
21778                   if (_cimg_mp_is_vector(arg3)) {
21779                     arg4 = _cimg_mp_size(arg3);
21780                     CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
21781                     arg2+=arg4;
21782                   } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
21783                   s = ns;
21784                 }
21785               if (arg1==~0U) arg1 = arg2;
21786               if (!arg1) _cimg_mp_return(0);
21787               pos = vector(arg1);
21788               l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
21789               (l_opcode>'y').move_to(opcode);
21790               opcode[2] = opcode._height;
21791               opcode.move_to(code);
21792               return_new_comp = true;
21793               _cimg_mp_return(pos);
21794             }
21795 
21796             if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) ||
21797                 !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) ||
21798                 !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) ||
21799                 !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) ||
21800                 !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) ||
21801                 !std::strncmp(ss,"vprod(",6) ||
21802                 !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) ||
21803                 !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) ||
21804                 !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions
21805               _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'":
21806                                       ss[4]=='k'?"Function 'vargkth()'":
21807                                       ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'":
21808                                       ss[5]=='i'?"Function vargminabs()'":
21809                                       ss[7]=='('?"Function 'vargmax()'":
21810                                       "Function 'vargmaxabs()'"):
21811                           ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"):
21812                           ss[1]=='k'?"Function 'vkth()'":
21813                           ss[1]=='p'?"Function 'vprod()'":
21814                           ss[1]=='v'?"Function 'vvar()'":
21815                           ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'":
21816                                       "Function 'vminabs()'"):
21817                           ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'":
21818                                       "Function 'vmaxabs()'"):
21819                           "Function 'vmed()'");
21820               op = ss[1]=='a'?(ss[2]=='v'?mp_vavg:
21821                                ss[4]=='k'?mp_vargkth:
21822                                ss[5]=='i' && ss[7]=='('?mp_vargmin:
21823                                ss[5]=='i'?mp_vargminabs:
21824                                ss[7]=='('?mp_vargmax:mp_vargmaxabs):
21825                 ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd):
21826                 ss[1]=='k'?mp_vkth:
21827                 ss[1]=='p'?mp_vprod:
21828                 ss[1]=='v'?mp_vvar:
21829                 ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs):
21830                 ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs):
21831                 mp_vmedian;
21832               CImg<ulongT>::vector((ulongT)op,0,0,0).move_to(l_opcode);
21833               p1 = ~0U;
21834               p3 = 1;
21835               for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
21836                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21837                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21838                 arg2 = compile(s,ns,depth1,0,bloc_flags);
21839                 p2 = _cimg_mp_size(arg2);
21840                 if (p1==~0U) { if (_cimg_mp_is_vector(arg2)) p1 = p2; }
21841                 else _cimg_mp_check_type(arg2,p3,3,p1);
21842                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21843                 s = ns;
21844                 ++p3;
21845               }
21846               (l_opcode>'y').move_to(opcode);
21847               if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1);
21848               opcode[1] = pos;
21849               opcode[2] = p1;
21850               opcode[3] = opcode._height;
21851               opcode.move_to(code);
21852               return_new_comp = true;
21853               _cimg_mp_return(pos);
21854             }
21855 
21856             if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
21857               _cimg_mp_op("Function 'vtos()'");
21858               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21859               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21860               arg2 = 0; arg3 = ~0U;
21861               if (s1<se1) {
21862                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21863                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21864                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):~0U;
21865               }
21866               _cimg_mp_check_type(arg2,2,1,0);
21867               if (arg3==~0U) { // Auto-guess best output vector size
21868                 p1 = _cimg_mp_size(arg1);
21869                 p1 = p1?25*p1 - 1:24;
21870               } else {
21871                 _cimg_mp_check_constant(arg3,3,3);
21872                 p1 = (unsigned int)mem[arg3];
21873               }
21874               pos = vector(p1);
21875               CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
21876               return_new_comp = true;
21877               _cimg_mp_return(pos);
21878             }
21879             break;
21880 
21881           case 'w' :
21882             if (*ss1=='(') { // Image width
21883               _cimg_mp_op("Function 'w()'");
21884               if (*ss2=='#') { // Index specified
21885                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
21886                 _cimg_mp_check_list(false);
21887               } else { if (ss2!=se1) break; p1 = ~0U; }
21888               _cimg_mp_scalar1(mp_image_w,p1);
21889             }
21890 
21891             if (*ss1=='h' && *ss2=='(') { // Image width*height
21892               _cimg_mp_op("Function 'wh()'");
21893               if (*ss3=='#') { // Index specified
21894                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
21895                 _cimg_mp_check_list(false);
21896               } else { if (ss3!=se1) break; p1 = ~0U; }
21897               _cimg_mp_scalar1(mp_image_wh,p1);
21898             }
21899 
21900             if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
21901               _cimg_mp_op("Function 'whd()'");
21902               if (*ss4=='#') { // Index specified
21903                 p1 = compile(ss5,se1,depth1,0,bloc_flags);
21904                 _cimg_mp_check_list(false);
21905               } else { if (ss4!=se1) break; p1 = ~0U; }
21906               _cimg_mp_scalar1(mp_image_whd,p1);
21907             }
21908 
21909             if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
21910               _cimg_mp_op("Function 'whds()'");
21911               if (*ss5=='#') { // Index specified
21912                 p1 = compile(ss6,se1,depth1,0,bloc_flags);
21913                 _cimg_mp_check_list(false);
21914               } else { if (ss5!=se1) break; p1 = ~0U; }
21915               _cimg_mp_scalar1(mp_image_whds,p1);
21916             }
21917 
21918             if (!std::strncmp(ss,"while(",6)) { // While...do
21919               _cimg_mp_op("Function 'while()'");
21920               s0 = *ss5=='('?ss6:ss8;
21921               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21922               p1 = code._width;
21923               arg1 = compile(s0,s1,depth1,0,bloc_flags);
21924               p2 = code._width;
21925               arg6 = mempos;
21926               pos = compile(++s1,se1,depth1,0,bloc_flags);
21927               _cimg_mp_check_type(arg1,1,1,0);
21928               arg2 = _cimg_mp_size(pos);
21929               CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2,
21930                                    pos>=arg6 && !_cimg_mp_is_constant(pos),
21931                                    arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1);
21932               _cimg_mp_return(pos);
21933             }
21934             break;
21935 
21936           case 'x' :
21937             if (!std::strncmp(ss,"xor(",4)) { // Xor
21938               _cimg_mp_op("Function 'xor()'");
21939               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21940               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21941               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
21942               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
21943               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
21944               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
21945               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
21946               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
21947                 _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]);
21948               _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
21949             }
21950             break;
21951           }
21952 
21953           if (!std::strncmp(ss,"max(",4) || !std::strncmp(ss,"min(",4) ||
21954               !std::strncmp(ss,"maxabs(",7) || !std::strncmp(ss,"minabs(",7) ||
21955               !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
21956               !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
21957               !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
21958               !std::strncmp(ss,"prod(",5) ||
21959               !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
21960               !std::strncmp(ss,"argminabs(",10) || !std::strncmp(ss,"argmaxabs(",10) ||
21961               !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
21962             _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
21963                                   ss[3]=='k'?"Function 'argkth()'":
21964                                   ss[4]=='i' && ss[6]=='('?"Function 'argmin()'":
21965                                   ss[4]=='i'?"Function argminabs()'":
21966                                   ss[6]=='('?"Function 'argmax()'":
21967                                   "Function 'argmaxabs()'"):
21968                         *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
21969                         *ss=='k'?"Function 'kth()'":
21970                         *ss=='p'?"Function 'prod()'":
21971                         *ss=='v'?"Function 'var()'":
21972                         ss[1]=='i'?(ss[3]=='('?"Function 'min()'":
21973                                     "Function 'minabs()'"):
21974                         ss[1]=='a'?(ss[3]=='('?"Function 'max()'":
21975                                     "Function 'maxabs()'"):
21976                         "Function 'med()'");
21977             op = *ss=='a'?(ss[1]=='v'?mp_avg:
21978                            ss[3]=='k'?mp_argkth:
21979                            ss[4]=='i' && ss[6]=='('?mp_argmin:
21980                            ss[4]=='i'?mp_argminabs:
21981                            ss[6]=='('?mp_argmax:mp_argmaxabs):
21982               *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
21983               *ss=='k'?mp_kth:
21984               *ss=='p'?mp_prod:
21985               *ss=='v'?mp_var:
21986               ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
21987               ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
21988               mp_median;
21989             is_sth = true; // Tell if all arguments are constant
21990             pos = scalar();
21991             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
21992             for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
21993               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21994                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21995               arg2 = compile(s,ns,depth1,0,bloc_flags);
21996               if (_cimg_mp_is_vector(arg2))
21997                 CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
21998                                        arg2 + (ulongT)_cimg_mp_size(arg2)).
21999                   move_to(l_opcode);
22000               else CImg<ulongT>::vector(arg2).move_to(l_opcode);
22001               is_sth&=_cimg_mp_is_constant(arg2);
22002               s = ns;
22003             }
22004             (l_opcode>'y').move_to(opcode);
22005             opcode[2] = opcode._height;
22006             if (is_sth) _cimg_mp_constant(op(*this));
22007             opcode.move_to(code);
22008             return_new_comp = true;
22009             _cimg_mp_return(pos);
22010           }
22011 
22012           // No corresponding built-in function -> Look for a user-defined macro call.
22013           s0 = strchr(ss,'(');
22014           if (s0) {
22015             variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
22016 
22017             // Count number of specified arguments.
22018             p1 = 0;
22019             for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
22020               while (*s && cimg::is_blank(*s)) ++s;
22021               if (*s==')' && !p1) break;
22022               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22023                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
22024             }
22025 
22026             arg3 = 0; // Number of possible name matches
22027             cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
22028                                           macro_def[l].back()==(char)p1) {
22029               p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
22030               CImg<charT> _expr = macro_body[l]; // Expression to be substituted
22031 
22032               p1 = 1; // Index of current parsed argument
22033               for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
22034                 while (*s && cimg::is_blank(*s)) ++s;
22035                 if (*s==')' && p1==1) break; // Function has no arguments
22036                 if (p1>p2) { ++p1; break; }
22037                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22038                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
22039                 variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
22040                 arg2 = 0;
22041                 cimg_forX(_expr,k) {
22042                   if (_expr[k]==(char)p1) { // Perform argument substitution
22043                     arg1 = _expr._width;
22044                     _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
22045                     std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
22046                     std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
22047                     k+=variable_name._width - 2;
22048                   }
22049                   ++arg2;
22050                 }
22051               }
22052 
22053               // Recompute 'pexpr' and 'level' for evaluating substituted expression.
22054               CImg<charT> _pexpr(_expr._width);
22055               ns = _pexpr._data;
22056               for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
22057                 if (!cimg::is_blank(*ps)) c1 = *ps;
22058                 *(ns++) = c1;
22059               }
22060               *ns = 0;
22061 
22062               CImg<uintT> _level = get_level(_expr);
22063               expr.swap(_expr);
22064               pexpr.swap(_pexpr);
22065               level.swap(_level);
22066               s0 = user_macro;
22067               user_macro = macro_def[l];
22068               pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,bloc_flags);
22069               user_macro = s0;
22070               level.swap(_level);
22071               pexpr.swap(_pexpr);
22072               expr.swap(_expr);
22073               _cimg_mp_return(pos);
22074             }
22075 
22076             if (arg3) { // Macro name matched but number of arguments does not
22077               CImg<uintT> sig_nargs(arg3);
22078               arg1 = 0;
22079               cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
22080                 sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
22081               _cimg_mp_strerr;
22082               cimg::strellipsize(variable_name,64);
22083               if (sig_nargs._width>1) {
22084                 sig_nargs.sort();
22085                 arg1 = sig_nargs.back();
22086                 --sig_nargs._width;
22087                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
22088                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
22089                                             "does not match macro declaration (defined for %s or %u arguments), "
22090                                             "in expression '%s%s%s'.",
22091                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
22092                                             p1,sig_nargs.value_string()._data,arg1,
22093                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22094               } else
22095                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
22096                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
22097                                             "does not match macro declaration (defined for %u argument%s), "
22098                                             "in expression '%s%s%s'.",
22099                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
22100                                             p1,*sig_nargs,*sig_nargs!=1?"s":"",
22101                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22102             }
22103           }
22104         } // if (se1==')')
22105 
22106         // Char / string initializer.
22107         if (*se1=='\'' &&
22108             ((se1>ss && *ss=='\'') ||
22109             (se1>ss1 && *ss=='_' && *ss1=='\''))) {
22110           if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
22111           else { _cimg_mp_op("String initializer"); s1 = ss1; }
22112           arg1 = (unsigned int)(se1 - s1); // Original string length
22113           if (arg1) {
22114             CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
22115             cimg::strunescape(variable_name);
22116             arg1 = (unsigned int)std::strlen(variable_name);
22117           }
22118           if (!arg1) _cimg_mp_return(0); // Empty string -> 0
22119           if (*ss=='_') {
22120             if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name);
22121             _cimg_mp_strerr;
22122             cimg::strellipsize(variable_name,64);
22123             throw CImgArgumentException("[" cimg_appname "_math_parser] "
22124                                         "CImg<%s>::%s: %s: Literal %s contains more than one byte, "
22125                                         "in expression '%s%s%s'.",
22126                                         pixel_type(),_cimg_mp_calling_function,s_op,
22127                                         ss1,
22128                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22129           }
22130           pos = vector(arg1);
22131           CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
22132           CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
22133           std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
22134           (l_opcode>'y').move_to(code);
22135           return_new_comp = true;
22136           _cimg_mp_return(pos);
22137         }
22138 
22139         // Vector initializer [ ... ].
22140         if (*ss=='[' && *se1==']') {
22141           _cimg_mp_op("Vector initializer");
22142           s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1;
22143           s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2;
22144           if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
22145             arg1 = (unsigned int)(s2 - s1 - 1); // Original string length
22146             if (arg1) {
22147               CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
22148               cimg::strunescape(variable_name);
22149               arg1 = (unsigned int)std::strlen(variable_name);
22150             }
22151             if (!arg1) _cimg_mp_return(0); // Empty string -> 0
22152             pos = vector(arg1);
22153             CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
22154             CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
22155             std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
22156             (l_opcode>'y').move_to(code);
22157           } else { // Vector values provided as list of items
22158             arg1 = 0; // Number of specified values
22159             if (*ss1!=']') for (s = ss1; s<se; ++s) {
22160                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22161                                (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
22162                 arg2 = compile(s,ns,depth1,0,bloc_flags);
22163                 if (_cimg_mp_is_vector(arg2)) {
22164                   arg3 = _cimg_mp_size(arg2);
22165                   CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
22166                   arg1+=arg3;
22167                 } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
22168                 s = ns;
22169               }
22170             if (!arg1) _cimg_mp_return(0);
22171             pos = vector(arg1);
22172             l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
22173             (l_opcode>'y').move_to(opcode);
22174             opcode[2] = opcode._height;
22175             opcode.move_to(code);
22176           }
22177           return_new_comp = true;
22178           _cimg_mp_return(pos);
22179         }
22180 
22181         // Variables related to the input list of images.
22182         if (*ss1=='#' && ss2<se) {
22183           arg1 = compile(ss2,se,depth1,0,bloc_flags);
22184           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22185           switch (*ss) {
22186           case 'w' : // w#ind
22187             if (!listin) _cimg_mp_return(0);
22188             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width);
22189             _cimg_mp_scalar1(mp_list_width,arg1);
22190           case 'h' : // h#ind
22191             if (!listin) _cimg_mp_return(0);
22192             if (p1!=~0U) _cimg_mp_constant(listin[p1]._height);
22193             _cimg_mp_scalar1(mp_list_height,arg1);
22194           case 'd' : // d#ind
22195             if (!listin) _cimg_mp_return(0);
22196             if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth);
22197             _cimg_mp_scalar1(mp_list_depth,arg1);
22198           case 'r' : // r#ind
22199             if (!listin) _cimg_mp_return(0);
22200             if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared);
22201             _cimg_mp_scalar1(mp_list_is_shared,arg1);
22202           case 's' : // s#ind
22203             if (!listin) _cimg_mp_return(0);
22204             if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum);
22205             _cimg_mp_scalar1(mp_list_spectrum,arg1);
22206           case 'i' : // i#ind
22207             if (!listin) _cimg_mp_return(0);
22208             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
22209                              0,_cimg_mp_boundary);
22210           case 'I' : // I#ind
22211             p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
22212             if (!p2) _cimg_mp_return(0);
22213             pos = vector(p2);
22214             CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
22215             return_new_comp = true;
22216             _cimg_mp_return(pos);
22217           case 'R' : // R#ind
22218             if (!listin) _cimg_mp_return(0);
22219             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
22220                              0,_cimg_mp_boundary);
22221           case 'G' : // G#ind
22222             if (!listin) _cimg_mp_return(0);
22223             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
22224                              0,_cimg_mp_boundary);
22225           case 'B' : // B#ind
22226             if (!listin) _cimg_mp_return(0);
22227             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
22228                              0,_cimg_mp_boundary);
22229           case 'A' : // A#ind
22230             if (!listin) _cimg_mp_return(0);
22231             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
22232                              0,_cimg_mp_boundary);
22233           }
22234         }
22235 
22236         if (*ss1 && *ss2=='#' && ss3<se) {
22237           arg1 = compile(ss3,se,depth1,0,bloc_flags);
22238           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22239           if (*ss=='w' && *ss1=='h') { // wh#ind
22240             if (!listin) _cimg_mp_return(0);
22241             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
22242             _cimg_mp_scalar1(mp_list_wh,arg1);
22243           }
22244           arg2 = ~0U;
22245 
22246           if (*ss=='i') {
22247             if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
22248               if (!listin) _cimg_mp_return(0);
22249               _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
22250                                0,_cimg_mp_boundary);
22251             }
22252 
22253             if (*ss1=='c') { // ic#ind
22254               if (!listin) _cimg_mp_return(0);
22255               if (_cimg_mp_is_constant(arg1)) {
22256                 if (!list_median) list_median.assign(listin._width);
22257                 if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
22258                 _cimg_mp_constant(*list_median[p1]);
22259               }
22260               _cimg_mp_scalar1(mp_list_median,arg1);
22261             }
22262 
22263             if (*ss1=='n') { // in#ind
22264               if (!listin) _cimg_mp_return(0);
22265               if (_cimg_mp_is_constant(arg1)) {
22266                 if (!list_norm) list_norm.assign(listin._width);
22267                 if (!list_norm[p1]) CImg<doubleT>::vector(listin[p1].magnitude()).move_to(list_norm[p1]);
22268                 _cimg_mp_constant(*list_norm[p1]);
22269               }
22270               _cimg_mp_scalar1(mp_list_norm,arg1);
22271             }
22272 
22273             switch (*ss1) {
22274             case 'm' : arg2 = 0; break; // im#ind
22275             case 'M' : arg2 = 1; break; // iM#ind
22276             case 'a' : arg2 = 2; break; // ia#ind
22277             case 'v' : arg2 = 3; break; // iv#ind
22278             case 's' : arg2 = 12; break; // is#ind
22279             case 'p' : arg2 = 13; break; // ip#ind
22280             }
22281           } else if (*ss1=='m') switch (*ss) {
22282             case 'x' : arg2 = 4; break; // xm#ind
22283             case 'y' : arg2 = 5; break; // ym#ind
22284             case 'z' : arg2 = 6; break; // zm#ind
22285             case 'c' : arg2 = 7; break; // cm#ind
22286             } else if (*ss1=='M') switch (*ss) {
22287             case 'x' : arg2 = 8; break; // xM#ind
22288             case 'y' : arg2 = 9; break; // yM#ind
22289             case 'z' : arg2 = 10; break; // zM#ind
22290             case 'c' : arg2 = 11; break; // cM#ind
22291             }
22292           if (arg2!=~0U) {
22293             if (!listin) _cimg_mp_return(0);
22294             if (_cimg_mp_is_constant(arg1)) {
22295               if (!list_stats) list_stats.assign(listin._width);
22296               if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
22297               _cimg_mp_constant(list_stats(p1,arg2));
22298             }
22299             _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
22300           }
22301         }
22302 
22303         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
22304           arg1 = compile(ss4,se,depth1,0,bloc_flags);
22305           if (!listin) _cimg_mp_return(0);
22306           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22307           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
22308           _cimg_mp_scalar1(mp_list_whd,arg1);
22309         }
22310         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
22311           arg1 = compile(ss5,se,depth1,0,bloc_flags);
22312           if (!listin) _cimg_mp_return(0);
22313           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22314           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
22315           _cimg_mp_scalar1(mp_list_whds,arg1);
22316         }
22317 
22318         if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
22319         if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
22320 
22321 #ifdef cimg_mp_operator_dollar
22322         // External variable '$varname'.
22323         variable_name.assign(ss,(unsigned int)(se + 1 - ss)).back() = 0;
22324         if (*ss=='$' && is_varname(variable_name._data + 1))
22325           _cimg_mp_constant(cimg_mp_operator_dollar(variable_name._data + 1));
22326 #endif
22327 
22328         // No known item found, assuming this is an already initialized variable.
22329         if (is_varname(variable_name)) { // Valid variable name
22330           get_variable_pos(variable_name,arg1,arg2);
22331           arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
22332           if (arg1!=~0U) _cimg_mp_return(arg1);
22333         }
22334 
22335         // Reached an unknown item -> error.
22336         c1 = *se1;
22337         _cimg_mp_strerr;
22338         cimg::strellipsize(variable_name,64);
22339         if (is_sth)
22340           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22341                                       "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.",
22342                                       pixel_type(),_cimg_mp_calling_function,
22343                                       variable_name._data,
22344                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22345         s1 = std::strchr(ss,'(');
22346         s_op = s1 && c1==')'?"function call":"item";
22347         throw CImgArgumentException("[" cimg_appname "_math_parser] "
22348                                     "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.",
22349                                     pixel_type(),_cimg_mp_calling_function,
22350                                     s_op,variable_name._data,
22351                                     s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22352       }
22353 
22354       // Evaluation procedure.
22355       double operator()(const double x, const double y, const double z, const double c) {
22356         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22357         for (p_code = code; p_code<p_code_end; ++p_code) {
22358           opcode._data = p_code->_data;
22359           const ulongT target = opcode[1];
22360           mem[target] = _cimg_mp_defunc(*this);
22361         }
22362         return *result;
22363       }
22364 
22365       // Evaluation procedure (return output values in vector 'output').
22366       template<typename t>
22367       void operator()(const double x, const double y, const double z, const double c, t *const output) {
22368         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22369         for (p_code = code; p_code<p_code_end; ++p_code) {
22370           opcode._data = p_code->_data;
22371           const ulongT target = opcode[1];
22372           mem[target] = _cimg_mp_defunc(*this);
22373         }
22374         if (result_dim) {
22375           const double *ptrs = result + 1;
22376           t *ptrd = output;
22377           for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
22378         } else *output = (t)*result;
22379       }
22380 
22381       // Evaluation procedure for begin_t() bloc.
22382       void begin_t() {
22383         if (!code_begin_t) return;
22384         mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22385         p_code_end = code_begin_t.end();
22386         for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
22387           opcode._data = p_code->_data;
22388           const ulongT target = opcode[1];
22389           mem[target] = _cimg_mp_defunc(*this);
22390         }
22391         p_code_end = code.end();
22392       }
22393 
22394       // Evaluation procedure for end_t() bloc.
22395       void end_t() {
22396         if (!code_end_t) return;
22397         if (imgin) {
22398           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22399           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22400           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22401           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22402         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22403         p_code_end = code_end_t.end();
22404         for (p_code = code_end_t; p_code<p_code_end; ++p_code) {
22405           opcode._data = p_code->_data;
22406           const ulongT target = opcode[1];
22407           mem[target] = _cimg_mp_defunc(*this);
22408         }
22409       }
22410 
22411       // Evaluation procedure the end() bloc.
22412       void end() {
22413         if (!code_end) return;
22414         if (imgin) {
22415           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22416           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22417           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22418           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22419         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22420         p_code_end = code_end.end();
22421         for (p_code = code_end; p_code<p_code_end; ++p_code) {
22422           opcode._data = p_code->_data;
22423           const ulongT target = opcode[1];
22424           mem[target] = _cimg_mp_defunc(*this);
22425         }
22426       }
22427 
22428       // Merge inter-thread variables.
22429       // (argument 'mp' is the master instance).
22430       void merge(_cimg_math_parser& mp) {
22431         if (&mp==this) return;
22432         cimg_rofY(mp.memmerge,k) {
22433           const unsigned int
22434             pos = (unsigned int)mp.memmerge(0,k),
22435             siz = (unsigned int)mp.memmerge(1,k),
22436             iop = (unsigned int)mp.memmerge(2,k);
22437           if (!siz) switch (iop) { // Scalar value
22438             case 0 : mp.mem[pos] = mem[pos]; break;                       // Assignment
22439             case 1 : mp.mem[pos]+=mem[pos]; break;                        // Operator+
22440             case 2 : mp.mem[pos]-=mem[pos]; break;                        // Operator-
22441             case 3 : mp.mem[pos]*=mem[pos]; break;                        // Operator*
22442             case 4 : mp.mem[pos]/=mem[pos]; break;                        // Operator/
22443             case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min'
22444             case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max'
22445             } else switch (iop) { // Vector value
22446             case 0 :
22447               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true) = CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22448               break;
22449             case 1 :
22450               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22451               break;
22452             case 2 :
22453               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22454               break;
22455             case 3 :
22456               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22457               break;
22458             case 4 :
22459               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22460               break;
22461             case 5 :
22462               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22463               break;
22464             case 6 :
22465               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22466               break;
22467             }
22468         }
22469       }
22470 
22471       // Return specified argument number as a string.
22472       static const char *s_argth(const unsigned int n_arg) {
22473         const char
22474           *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth",
22475                         "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
22476                         "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" };
22477         return _s_arg[n_arg<sizeof(_s_arg)/sizeof(char*)?n_arg:sizeof(_s_arg)/sizeof(char*)-1];
22478       }
22479 
22480       // Return a string that defines the calling function + the user-defined function scope.
22481       CImg<charT> s_calling_function() const {
22482         CImg<charT> res;
22483         const unsigned int
22484           l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
22485           l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
22486         if (l2) {
22487           res.assign(l1 + l2 + 48);
22488           cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
22489         } else {
22490           res.assign(l1 + l2 + 4);
22491           cimg_snprintf(res,res._width,"%s()",calling_function);
22492         }
22493         return res;
22494       }
22495 
22496       // Return type of a memory element as a string.
22497       CImg<charT> s_type(const unsigned int arg) const {
22498         CImg<charT> res;
22499         if (_cimg_mp_is_vector(arg)) { // Vector
22500           CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
22501           cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
22502         } else if (_cimg_mp_is_constant(arg)) CImg<charT>::string("const scalar").move_to(res); // Const scalar
22503         else CImg<charT>::string("scalar").move_to(res); // Scalar
22504         return res;
22505       }
22506 
22507       // Count parentheses/brackets level of each character of the expression.
22508       CImg<uintT> get_level(CImg<charT>& _expr) const {
22509         bool is_escaped = false, next_is_escaped = false;
22510         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22511         CImg<uintT> res(_expr._width - 1);
22512         unsigned int *pd = res._data;
22513         int _level = 0;
22514         for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
22515           if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
22516           if (!is_escaped && *ps=='\'') { // Non-escaped character
22517             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22518             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22519             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22520           }
22521           *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
22522                                    *ps=='(' || *ps=='['?_level++:
22523                                    *ps==')' || *ps==']'?--_level:
22524                                    _level);
22525           mode = next_mode;
22526           is_escaped = next_is_escaped;
22527           next_is_escaped = false;
22528         }
22529         if (mode) {
22530           cimg::strellipsize(_expr,64);
22531           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22532                                       "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
22533                                       pixel_type(),_cimg_mp_calling_function,
22534                                       _expr._data);
22535         }
22536         if (_level) {
22537           cimg::strellipsize(_expr,64);
22538           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22539                                       "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
22540                                       pixel_type(),_cimg_mp_calling_function,
22541                                       _expr._data);
22542         }
22543         return res;
22544       }
22545 
22546       // Find and return index of current image 'imgin' within image list 'listin'.
22547       unsigned int get_mem_img_index() {
22548         if (mem_img_index==~0U) {
22549           if (&imgout>listout.data() && &imgout<listout.end())
22550             mem_img_index = constant((double)(&imgout - listout.data()));
22551           else {
22552             unsigned int pos = ~0U;
22553             cimglist_for(listout,l)
22554               if (imgout._data==listout[l]._data && imgout.is_sameXYZC(listout[l])) { pos = l; break; }
22555             if (pos!=~0U) mem_img_index = constant((double)pos);
22556           }
22557         }
22558         return mem_img_index;
22559       }
22560 
22561       // Return indices for accessing math parser variables.
22562       void get_variable_pos(const char *variable_name, unsigned int &pos, unsigned int &rpos) {
22563         char c1, c2, c3, c4;
22564         pos = rpos = ~0U;
22565         if (!variable_name || !*variable_name) return;
22566 
22567         unsigned int rp = variable_name[1]?~0U:*variable_name; // One-char variable
22568         if (variable_name[1] && !variable_name[2]) { // Two-chars variable
22569           c1 = variable_name[0];
22570           c2 = variable_name[1];
22571           if (c1=='w' && c2=='h') rp = 0; // wh
22572           else if (c1=='p' && c2=='i') rp = 3; // pi
22573           else if (c1=='i') {
22574             if (c2>='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9
22575             else if (c2=='m') rp = 4; // im
22576             else if (c2=='M') rp = 5; // iM
22577             else if (c2=='a') rp = 6; // ia
22578             else if (c2=='v') rp = 7; // iv
22579             else if (c2=='s') rp = 8; // is
22580             else if (c2=='p') rp = 9; // ip
22581             else if (c2=='c') rp = 10; // ic
22582             else if (c2=='n') rp = 11; // in
22583           } else if (c2=='m') {
22584             if (c1=='x') rp = 12; // xm
22585             else if (c1=='y') rp = 13; // ym
22586             else if (c1=='z') rp = 14; // zm
22587             else if (c1=='c') rp = 15; // cm
22588           } else if (c2=='M') {
22589             if (c1=='x') rp = 16; // xM
22590             else if (c1=='y') rp = 17; // yM
22591             else if (c1=='z') rp = 18; // zM
22592             else if (c1=='c') rp = 19; // cM
22593           }
22594         } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
22595           c1 = variable_name[0];
22596           c2 = variable_name[1];
22597           c3 = variable_name[2];
22598           if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd
22599         } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
22600                    !variable_name[4]) { // Four-chars variable
22601           c1 = variable_name[0];
22602           c2 = variable_name[1];
22603           c3 = variable_name[2];
22604           c4 = variable_name[3];
22605           if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds
22606         } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation
22607         else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary
22608 
22609         if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels
22610 
22611         // Multi-char variable name : check for existing variable with same name
22612         cimglist_for(variable_def,i)
22613           if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; }
22614       }
22615 
22616       // Tell for each character of an expression if it is inside a string or not.
22617       CImg<boolT> is_inside_string(CImg<charT>& _expr) const {
22618         bool is_escaped = false, next_is_escaped = false;
22619         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22620         CImg<boolT> res = CImg<charT>::string(_expr);
22621         bool *pd = res._data;
22622         for (const char *ps = _expr._data; *ps; ++ps) {
22623           if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
22624           if (!is_escaped && *ps=='\'') { // Non-escaped character
22625             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22626             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22627             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22628           }
22629           *(pd++) = mode>=1 || is_escaped;
22630           mode = next_mode;
22631           is_escaped = next_is_escaped;
22632           next_is_escaped = false;
22633         }
22634         return res;
22635       }
22636 
22637       // Return true if specified argument can be a part of an allowed  variable name.
22638       bool is_varchar(const char c) const {
22639         return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
22640       }
22641 
22642       // Return true if specified argument can be considered as a variable name.
22643       bool is_varname(const char *const str, const unsigned int length=~0U) const {
22644         if (*str>='0' && *str<='9') return false;
22645         for (unsigned int l = 0; l<length && str[l]; ++l)
22646           if (!is_varchar(str[l])) return false;
22647         return true;
22648       }
22649 
22650       // Return true if all values of a vector are computation values.
22651       bool is_comp_vector(const unsigned int arg) const {
22652         unsigned int siz = _cimg_mp_size(arg);
22653         if (siz>128) return false;
22654         const int *ptr = memtype.data(arg + 1);
22655         bool is_tmp = true;
22656         while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
22657         return is_tmp;
22658       }
22659 
22660       // Check if a memory slot is a positive integer constant scalar value.
22661       // 'mode' can be:
22662       // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
22663       void check_constant(const unsigned int arg, const unsigned int n_arg,
22664                           const unsigned int mode,
22665                           char *const ss, char *const se, const char saved_char) {
22666         _cimg_mp_check_type(arg,n_arg,1,0);
22667         if (!_cimg_mp_is_constant(arg)) {
22668           const char *const s_arg = s_argth(n_arg);
22669           char *s0; _cimg_mp_strerr;
22670           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22671                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a constant, "
22672                                       "in expression '%s%s%s'.",
22673                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22674                                       s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,
22675                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22676         }
22677         const double val = mem[arg];
22678 
22679         if (!((!mode || (double)(int)mem[arg]==mem[arg]) &&
22680               (mode<2 || mem[arg]>=(mode==3)))) {
22681           const char *const s_arg = s_argth(n_arg);
22682           char *s0; _cimg_mp_strerr;
22683           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22684                                       "CImg<%s>::%s: %s%s %s%s (of type '%s' and value '%g') is not a%s constant, "
22685                                       "in expression '%s%s%s'.",
22686                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22687                                       s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,val,
22688                                       !mode?"":mode==1?"n integer":
22689                                       mode==2?" positive integer":" strictly positive integer",
22690                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22691         }
22692       }
22693 
22694       // Check if an image index is a constant value.
22695       void check_constant_index(const unsigned int arg,
22696                                 char *const ss, char *const se, const char saved_char) {
22697         if (arg!=~0U && !_cimg_mp_is_constant(arg)) {
22698           char *s0; _cimg_mp_strerr;
22699           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22700                                       "CImg<%s>::%s: %s%s Specified image index is not a constant, "
22701                                       "in expression '%s%s%s'.",
22702                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22703                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22704         }
22705       }
22706 
22707       // Check a matrix is square.
22708       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
22709                                char *const ss, char *const se, const char saved_char) {
22710         _cimg_mp_check_type(arg,n_arg,2,0);
22711         const unsigned int
22712           siz = _cimg_mp_size(arg),
22713           n = (unsigned int)cimg::round(std::sqrt((float)siz));
22714         if (n*n!=siz) {
22715           const char *s_arg;
22716           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22717           else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One";
22718           char *s0; _cimg_mp_strerr;
22719           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22720                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') "
22721                                       "cannot be considered as a square matrix, in expression '%s%s%s'.",
22722                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22723                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22724                                       s_type(arg)._data,
22725                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22726         }
22727       }
22728 
22729       // Check type compatibility for one argument.
22730       // Bits of 'mode' tells what types are allowed:
22731       // { 1 = scalar | 2 = vectorN }.
22732       // If 'N' is not zero, it also restricts the vectors to be of size N only.
22733       void check_type(const unsigned int arg, const unsigned int n_arg,
22734                       const unsigned int mode, const unsigned int N,
22735                       char *const ss, char *const se, const char saved_char) {
22736         const bool
22737           is_scalar = _cimg_mp_is_scalar(arg),
22738           is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
22739         bool cond = false;
22740         if (mode&1) cond|=is_scalar;
22741         if (mode&2) cond|=is_vector;
22742         if (!cond) {
22743           const char *s_arg;
22744           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22745           else s_arg = s_argth(n_arg);
22746           CImg<charT> sb_type(32);
22747           if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
22748           else if (mode==2) {
22749             if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
22750             else cimg_snprintf(sb_type,sb_type._width,"'vector'");
22751           } else {
22752             if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
22753             else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
22754           }
22755           char *s0; _cimg_mp_strerr;
22756           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22757                                       "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
22758                                       "in expression '%s%s%s'.",
22759                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22760                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22761                                       s_type(arg)._data,sb_type._data,
22762                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22763         }
22764       }
22765 
22766       // Check that listin or listout are not empty.
22767       void check_list(const bool is_out,
22768                       char *const ss, char *const se, const char saved_char) {
22769         if ((!is_out && !listin) || (is_out && !listout)) {
22770           char *s0; _cimg_mp_strerr;
22771           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22772                                       "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
22773                                       "in expression '%s%s%s'.",
22774                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22775                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22776         }
22777       }
22778 
22779       // Insert constant value in memory.
22780       unsigned int constant(const double val) {
22781 
22782         // Search for built-in constant.
22783         if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
22784         if (val==(double)(int)val) {
22785           if (val>=0 && val<=10) return (unsigned int)val;
22786           if (val<0 && val>=-5) return (unsigned int)(10 - val);
22787         }
22788         if (val==0.5) return 16;
22789 
22790         // Search for constant already requested before (in const cache).
22791         unsigned int ind = ~0U;
22792         if (constcache_size<1024) {
22793           if (!constcache_size) {
22794             constcache_vals.assign(16,1,1,1,0);
22795             constcache_inds.assign(16,1,1,1,0);
22796             *constcache_vals = val;
22797             constcache_size = 1;
22798             ind = 0;
22799           } else { // Dichotomic search
22800             const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
22801             if (val_beg>=val) ind = 0;
22802             else if (val_end==val) ind = constcache_size - 1;
22803             else if (val_end<val) ind = constcache_size;
22804             else {
22805               unsigned int i0 = 1, i1 = constcache_size - 2;
22806               while (i0<=i1) {
22807                 const unsigned int mid = (i0 + i1)/2;
22808                 if (constcache_vals[mid]==val) { i0 = mid; break; }
22809                 else if (constcache_vals[mid]<val) i0 = mid + 1;
22810                 else i1 = mid - 1;
22811               }
22812               ind = i0;
22813             }
22814 
22815             if (ind>=constcache_size || constcache_vals[ind]!=val) {
22816               ++constcache_size;
22817               if (constcache_size>constcache_vals._width) {
22818                 constcache_vals.resize(-200,1,1,1,0);
22819                 constcache_inds.resize(-200,1,1,1,0);
22820               }
22821               const int l = constcache_size - (int)ind - 1;
22822               if (l>0) {
22823                 std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
22824                 std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
22825               }
22826               constcache_vals[ind] = val;
22827               constcache_inds[ind] = 0;
22828             }
22829           }
22830           if (constcache_inds[ind]) return constcache_inds[ind];
22831         }
22832 
22833         // Insert new constant in memory if necessary.
22834         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
22835         const unsigned int pos = mempos++;
22836         mem[pos] = val;
22837         memtype[pos] = 1; // Set constant property
22838         if (ind!=~0U) constcache_inds[ind] = pos;
22839         return pos;
22840       }
22841 
22842       // Insert new scalar in memory.
22843       unsigned int scalar() {
22844         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
22845         return mempos++;
22846       }
22847 
22848       // Insert new vector of specified size in memory.
22849       unsigned int vector(const unsigned int siz) {
22850         if (mempos + siz>=mem._width) {
22851           mem.resize(2*mem._width + siz,1,1,1,0);
22852           memtype.resize(mem._width,1,1,1,0);
22853         }
22854         const unsigned int pos = mempos++;
22855         mem[pos] = cimg::type<double>::nan();
22856         memtype[pos] = siz + 1;
22857         mempos+=siz;
22858         return pos;
22859       }
22860 
22861       // Insert new initialized vector.
22862       unsigned int vector(const unsigned int siz, const double value) {
22863         const unsigned int pos = vector(siz);
22864         double *ptr = &mem[pos] + 1;
22865         for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
22866         return pos;
22867       }
22868 
22869       // Insert new copy of specified vector in memory.
22870       unsigned int vector_copy(const unsigned int arg) {
22871         const unsigned int
22872           siz = _cimg_mp_size(arg),
22873           pos = vector(siz);
22874         CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
22875         return pos;
22876       }
22877 
22878       // Set reserved status to all values of a vector.
22879       void set_reserved_vector(const unsigned int arg) {
22880         unsigned int siz = _cimg_mp_size(arg);
22881         int *ptr = memtype.data(arg + 1);
22882         while (siz-->0) *(ptr++) = -1;
22883       }
22884 
22885       unsigned int scalar0(const mp_func op) {
22886         const unsigned int pos = scalar();
22887         CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
22888         return_new_comp = true;
22889         return pos;
22890       }
22891 
22892       unsigned int scalar1(const mp_func op, const unsigned int arg1) {
22893         const unsigned int pos =
22894           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:
22895           ((return_new_comp = true), scalar());
22896         CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
22897         return pos;
22898       }
22899 
22900       unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
22901         const unsigned int pos =
22902           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22903           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22904           ((return_new_comp = true), scalar());
22905         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
22906         return pos;
22907       }
22908 
22909       unsigned int scalar3(const mp_func op,
22910                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
22911         const unsigned int pos =
22912           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22913           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22914           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22915           ((return_new_comp = true), scalar());
22916         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
22917         return pos;
22918       }
22919 
22920       unsigned int scalar4(const mp_func op,
22921                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22922                            const unsigned int arg4) {
22923         const unsigned int pos =
22924           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22925           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22926           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22927           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22928           ((return_new_comp = true), scalar());
22929         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
22930         return pos;
22931       }
22932 
22933       unsigned int scalar5(const mp_func op,
22934                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22935                            const unsigned int arg4, const unsigned int arg5) {
22936         const unsigned int pos =
22937           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22938           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22939           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22940           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22941           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
22942           ((return_new_comp = true), scalar());
22943         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
22944         return pos;
22945       }
22946 
22947       unsigned int scalar6(const mp_func op,
22948                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22949                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
22950         const unsigned int pos =
22951           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22952           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22953           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22954           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22955           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
22956           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
22957           ((return_new_comp = true), scalar());
22958         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
22959         return pos;
22960       }
22961 
22962       unsigned int scalar7(const mp_func op,
22963                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22964                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
22965                            const unsigned int arg7) {
22966         const unsigned int pos =
22967           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22968           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22969           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22970           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22971           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
22972           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
22973           arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:
22974           ((return_new_comp = true), scalar());
22975         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
22976         return pos;
22977       }
22978 
22979       void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
22980         const unsigned int siz = _cimg_mp_size(pos);
22981         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
22982         else {
22983           code.insert(siz);
22984           for (unsigned int k = 1; k<=siz; ++k)
22985             CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
22986         }
22987       }
22988 
22989       void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
22990         const unsigned int siz = _cimg_mp_size(pos);
22991         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
22992         else {
22993           code.insert(siz);
22994           for (unsigned int k = 1; k<=siz; ++k)
22995             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
22996         }
22997       }
22998 
22999       unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
23000         const unsigned int
23001           siz = _cimg_mp_size(arg1),
23002           pos = is_comp_vector(arg1)?arg1:
23003           ((return_new_comp = true), vector(siz));
23004         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
23005         else {
23006           code.insert(siz);
23007           for (unsigned int k = 1; k<=siz; ++k)
23008             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
23009         }
23010         return pos;
23011       }
23012 
23013       unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23014         const unsigned int
23015           siz = _cimg_mp_size(arg1),
23016           pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
23017           ((return_new_comp = true), vector(siz));
23018         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23019         else {
23020           code.insert(siz);
23021           for (unsigned int k = 1; k<=siz; ++k)
23022             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
23023         }
23024         return pos;
23025       }
23026 
23027       unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23028         const unsigned int
23029           siz = _cimg_mp_size(arg1),
23030           pos = is_comp_vector(arg1)?arg1:
23031           ((return_new_comp = true), vector(siz));
23032         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23033         else {
23034           code.insert(siz);
23035           for (unsigned int k = 1; k<=siz; ++k)
23036             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
23037         }
23038         return pos;
23039       }
23040 
23041       unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23042         const unsigned int
23043           siz = _cimg_mp_size(arg2),
23044           pos = is_comp_vector(arg2)?arg2:
23045           ((return_new_comp = true), vector(siz));
23046         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23047         else {
23048           code.insert(siz);
23049           for (unsigned int k = 1; k<=siz; ++k)
23050             CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
23051         }
23052         return pos;
23053       }
23054 
23055       unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
23056                                const unsigned int arg3) {
23057         const unsigned int
23058           siz = _cimg_mp_size(arg1),
23059           pos = is_comp_vector(arg1)?arg1:
23060           ((return_new_comp = true), vector(siz));
23061         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
23062         else {
23063           code.insert(siz);
23064           for (unsigned int k = 1; k<=siz; ++k)
23065             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
23066         }
23067         return pos;
23068       }
23069 
23070       // Evaluation functions, known by the parser.
23071       // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
23072       // so we can store pointers to them directly in the opcode vectors.
23073 #ifdef _mp_arg
23074 #undef _mp_arg
23075 #endif
23076 #define _mp_arg(x) mp.mem[mp.opcode[x]]
23077 
23078       static double mp_abs(_cimg_math_parser& mp) {
23079         return cimg::abs(_mp_arg(2));
23080       }
23081 
23082       static double mp_add(_cimg_math_parser& mp) {
23083         return _mp_arg(2) + _mp_arg(3);
23084       }
23085 
23086       static double mp_acos(_cimg_math_parser& mp) {
23087         return std::acos(_mp_arg(2));
23088       }
23089 
23090       static double mp_acosh(_cimg_math_parser& mp) {
23091         return cimg::acosh(_mp_arg(2));
23092       }
23093 
23094       static double mp_asinh(_cimg_math_parser& mp) {
23095         return cimg::asinh(_mp_arg(2));
23096       }
23097 
23098       static double mp_atanh(_cimg_math_parser& mp) {
23099         return cimg::atanh(_mp_arg(2));
23100       }
23101 
23102       static double mp_arg(_cimg_math_parser& mp) {
23103         const int _ind = (int)_mp_arg(4);
23104         const unsigned int
23105           nb_args = (unsigned int)mp.opcode[2] - 4,
23106           ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
23107           siz = (unsigned int)mp.opcode[3];
23108         if (siz>0) {
23109           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
23110           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
23111           return cimg::type<double>::nan();
23112         }
23113         if (ind>=nb_args) return 0;
23114         return _mp_arg(ind + 4);
23115       }
23116 
23117       static double mp_arg0(_cimg_math_parser& mp) {
23118         const int _ind = (int)_mp_arg(4);
23119         const unsigned int
23120           nb_args = (unsigned int)mp.opcode[2] - 4,
23121           ind = _ind<0?_ind + nb_args:_ind + 1U,
23122           siz = (unsigned int)mp.opcode[3];
23123         if (siz>0) {
23124           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
23125           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
23126           return cimg::type<double>::nan();
23127         }
23128         if (ind>=nb_args) return 0;
23129         return _mp_arg(ind + 4);
23130       }
23131 
23132       static double mp_argkth(_cimg_math_parser& mp) {
23133         const unsigned int i_end = (unsigned int)mp.opcode[2];
23134         const double val = mp_kth(mp);
23135         for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.;
23136         return 1.;
23137       }
23138 
23139       static double mp_argmin(_cimg_math_parser& mp) {
23140         const unsigned int i_end = (unsigned int)mp.opcode[2];
23141         double val = _mp_arg(3);
23142         unsigned int argval = 0;
23143         for (unsigned int i = 4; i<i_end; ++i) {
23144           const double _val = _mp_arg(i);
23145           if (_val<val) { val = _val; argval = i - 3; }
23146         }
23147         return (double)argval;
23148       }
23149 
23150       static double mp_argminabs(_cimg_math_parser& mp) {
23151         const unsigned int i_end = (unsigned int)mp.opcode[2];
23152         double val = _mp_arg(3), absval = cimg::abs(val);
23153         unsigned int argval = 0;
23154         for (unsigned int i = 4; i<i_end; ++i) {
23155           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
23156           if (_absval<absval) { val = _val; absval = _absval; argval = i - 3; }
23157         }
23158         return (double)argval;
23159       }
23160 
23161       static double mp_argmax(_cimg_math_parser& mp) {
23162         const unsigned int i_end = (unsigned int)mp.opcode[2];
23163         double val = _mp_arg(3);
23164         unsigned int argval = 0;
23165         for (unsigned int i = 4; i<i_end; ++i) {
23166           const double _val = _mp_arg(i);
23167           if (_val>val) { val = _val; argval = i - 3; }
23168         }
23169         return (double)argval;
23170       }
23171 
23172       static double mp_argmaxabs(_cimg_math_parser& mp) {
23173         const unsigned int i_end = (unsigned int)mp.opcode[2];
23174         double val = _mp_arg(3), absval = cimg::abs(val);
23175         unsigned int argval = 0;
23176         for (unsigned int i = 4; i<i_end; ++i) {
23177           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
23178           if (_absval>absval) { val = _val; absval = _absval; argval = i - 3; }
23179         }
23180         return (double)argval;
23181       }
23182 
23183       static double mp_asin(_cimg_math_parser& mp) {
23184         return std::asin(_mp_arg(2));
23185       }
23186 
23187       static double mp_atan(_cimg_math_parser& mp) {
23188         return std::atan(_mp_arg(2));
23189       }
23190 
23191       static double mp_atan2(_cimg_math_parser& mp) {
23192         return std::atan2(_mp_arg(2),_mp_arg(3));
23193       }
23194 
23195       static double mp_avg(_cimg_math_parser& mp) {
23196         const unsigned int i_end = (unsigned int)mp.opcode[2];
23197         double val = _mp_arg(3);
23198         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
23199         return val/(i_end - 3);
23200       }
23201 
23202       static double mp_bitwise_and(_cimg_math_parser& mp) {
23203         return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
23204       }
23205 
23206       static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
23207         return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
23208       }
23209 
23210       static double mp_bitwise_not(_cimg_math_parser& mp) {
23211         // Limit result to 32bits such that it can be entirely represented as a 'double'.
23212         return (double)~(unsigned int)_mp_arg(2);
23213       }
23214 
23215       static double mp_bitwise_or(_cimg_math_parser& mp) {
23216         return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
23217       }
23218 
23219       static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
23220         return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
23221       }
23222 
23223       static double mp_bitwise_xor(_cimg_math_parser& mp) {
23224         return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
23225       }
23226 
23227       static double mp_bool(_cimg_math_parser& mp) {
23228         return (double)(bool)_mp_arg(2);
23229       }
23230 
23231       static double mp_break(_cimg_math_parser& mp) {
23232         mp.break_type = 1;
23233         mp.p_code = mp.p_break - 1;
23234         return cimg::type<double>::nan();
23235       }
23236 
23237       static double mp_breakpoint(_cimg_math_parser& mp) {
23238         cimg_abort_init;
23239         cimg_abort_test;
23240         cimg::unused(mp);
23241         return cimg::type<double>::nan();
23242       }
23243 
23244 #ifdef cimg_mp_func_run
23245       static double mp_run(_cimg_math_parser& mp) {
23246         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
23247         CImgList<charT> _str;
23248         CImg<charT> it;
23249         for (unsigned int n = 0; n<nb_args; ++n) {
23250           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
23251           if (siz) { // Vector argument -> string
23252             const double *ptr = &_mp_arg(3 + 2*n) + 1;
23253             unsigned int l = 0;
23254             while (l<siz && ptr[l]) ++l;
23255             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
23256           } else { // Scalar argument -> number
23257             it.assign(24);
23258             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
23259             CImg<charT>::string(it,false,true).move_to(_str);
23260           }
23261         }
23262         CImg(1,1,1,1,0).move_to(_str);
23263         CImg<charT> str = _str>'x';
23264         cimg_mp_func_run(str._data);
23265         return cimg::type<double>::nan();
23266       }
23267 #endif
23268 
23269       static double mp_cbrt(_cimg_math_parser& mp) {
23270         return cimg::cbrt(_mp_arg(2));
23271       }
23272 
23273       static double mp_ceil(_cimg_math_parser& mp) {
23274         return std::ceil(_mp_arg(2));
23275       }
23276 
23277       static double mp_complex_abs(_cimg_math_parser& mp) {
23278         return cimg::_hypot(_mp_arg(2),_mp_arg(3));
23279       }
23280 
23281       static double mp_complex_conj(_cimg_math_parser& mp) {
23282         const double real = _mp_arg(2), imag = _mp_arg(3);
23283         double *ptrd = &_mp_arg(1) + 1;
23284         ptrd[0] = real;
23285         ptrd[1] = -imag;
23286         return cimg::type<double>::nan();
23287       }
23288 
23289       static double mp_complex_div_sv(_cimg_math_parser& mp) {
23290         const double
23291           *ptr2 = &_mp_arg(3) + 1,
23292           r1 = _mp_arg(2),
23293           r2 = *(ptr2++), i2 = *ptr2;
23294         double *ptrd = &_mp_arg(1) + 1;
23295         const double denom = r2*r2 + i2*i2;
23296         *(ptrd++) = r1*r2/denom;
23297         *ptrd =  -r1*i2/denom;
23298         return cimg::type<double>::nan();
23299       }
23300 
23301       static double mp_complex_div_vv(_cimg_math_parser& mp) {
23302         const double
23303           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23304           r1 = *(ptr1++), i1 = *ptr1,
23305           r2 = *(ptr2++), i2 = *ptr2;
23306         double *ptrd = &_mp_arg(1) + 1;
23307         const double denom = r2*r2 + i2*i2;
23308         *(ptrd++) = (r1*r2 + i1*i2)/denom;
23309         *ptrd = (r2*i1 - r1*i2)/denom;
23310         return cimg::type<double>::nan();
23311       }
23312 
23313       static double mp_complex_exp(_cimg_math_parser& mp) {
23314         const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real);
23315         double *ptrd = &_mp_arg(1) + 1;
23316         ptrd[0] = exp_real*std::cos(imag);
23317         ptrd[1] = exp_real*std::sin(imag);
23318         return cimg::type<double>::nan();
23319       }
23320 
23321       static double mp_complex_log(_cimg_math_parser& mp) {
23322         const double real = _mp_arg(2), imag = _mp_arg(3);
23323         double *ptrd = &_mp_arg(1) + 1;
23324         ptrd[0] = 0.5*std::log(real*real + imag*imag);
23325         ptrd[1] = std::atan2(imag,real);
23326         return cimg::type<double>::nan();
23327       }
23328 
23329       static double mp_complex_mul(_cimg_math_parser& mp) {
23330         const double
23331           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23332           r1 = *(ptr1++), i1 = *ptr1,
23333           r2 = *(ptr2++), i2 = *ptr2;
23334         double *ptrd = &_mp_arg(1) + 1;
23335         *(ptrd++) = r1*r2 - i1*i2;
23336         *(ptrd++) = r1*i2 + r2*i1;
23337         return cimg::type<double>::nan();
23338       }
23339 
23340       static void _mp_complex_pow(const double r1, const double i1,
23341                                   const double r2, const double i2,
23342                                   double *ptrd) {
23343         double ro, io;
23344         if (cimg::abs(i2)<1e-15) { // Exponent is real
23345           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
23346             if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
23347             else ro = io = 0;
23348           } else {
23349             const double
23350               mod1_2 = r1*r1 + i1*i1,
23351               phi1 = std::atan2(i1,r1),
23352               modo = std::pow(mod1_2,0.5*r2),
23353               phio = r2*phi1;
23354             ro = modo*std::cos(phio);
23355             io = modo*std::sin(phio);
23356           }
23357         } else { // Exponent is complex
23358           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
23359           const double
23360             mod1_2 = r1*r1 + i1*i1,
23361             phi1 = std::atan2(i1,r1),
23362             modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
23363             phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
23364           ro = modo*std::cos(phio);
23365           io = modo*std::sin(phio);
23366         }
23367         *(ptrd++) = ro;
23368         *ptrd = io;
23369       }
23370 
23371       static double mp_complex_pow_ss(_cimg_math_parser& mp) {
23372         const double val1 = _mp_arg(2), val2 = _mp_arg(3);
23373         double *ptrd = &_mp_arg(1) + 1;
23374         _mp_complex_pow(val1,0,val2,0,ptrd);
23375         return cimg::type<double>::nan();
23376       }
23377 
23378       static double mp_complex_pow_sv(_cimg_math_parser& mp) {
23379         const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
23380         double *ptrd = &_mp_arg(1) + 1;
23381         _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
23382         return cimg::type<double>::nan();
23383       }
23384 
23385       static double mp_complex_pow_vs(_cimg_math_parser& mp) {
23386         const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
23387         double *ptrd = &_mp_arg(1) + 1;
23388         _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
23389         return cimg::type<double>::nan();
23390       }
23391 
23392       static double mp_complex_pow_vv(_cimg_math_parser& mp) {
23393         const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
23394         double *ptrd = &_mp_arg(1) + 1;
23395         _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
23396         return cimg::type<double>::nan();
23397       }
23398 
23399       static double mp_complex_cos(_cimg_math_parser& mp) {
23400         const double real = _mp_arg(2), imag = _mp_arg(3);
23401         double *ptrd = &_mp_arg(1) + 1;
23402         ptrd[0] = std::cos(real)*std::cosh(imag);
23403         ptrd[1] = -std::sin(real)*std::sinh(imag);
23404         return cimg::type<double>::nan();
23405       }
23406 
23407       static double mp_complex_sin(_cimg_math_parser& mp) {
23408         const double real = _mp_arg(2), imag = _mp_arg(3);
23409         double *ptrd = &_mp_arg(1) + 1;
23410         ptrd[0] = std::sin(real)*std::cosh(imag);
23411         ptrd[1] = std::cos(real)*std::sinh(imag);
23412         return cimg::type<double>::nan();
23413       }
23414 
23415       static double mp_complex_tan(_cimg_math_parser& mp) {
23416         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
23417         double *ptrd = &_mp_arg(1) + 1;
23418         ptrd[0] = std::sin(2*real)/denom;
23419         ptrd[1] = std::sinh(2*imag)/denom;
23420         return cimg::type<double>::nan();
23421       }
23422 
23423       static double mp_complex_cosh(_cimg_math_parser& mp) {
23424         const double real = _mp_arg(2), imag = _mp_arg(3);
23425         double *ptrd = &_mp_arg(1) + 1;
23426         ptrd[0] = std::cosh(real)*std::cos(imag);
23427         ptrd[1] = std::sinh(real)*std::sin(imag);
23428         return cimg::type<double>::nan();
23429       }
23430 
23431       static double mp_complex_sinh(_cimg_math_parser& mp) {
23432         const double real = _mp_arg(2), imag = _mp_arg(3);
23433         double *ptrd = &_mp_arg(1) + 1;
23434         ptrd[0] = std::sinh(real)*std::cos(imag);
23435         ptrd[1] = std::cosh(real)*std::sin(imag);
23436         return cimg::type<double>::nan();
23437       }
23438 
23439       static double mp_complex_tanh(_cimg_math_parser& mp) {
23440         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag);
23441         double *ptrd = &_mp_arg(1) + 1;
23442         ptrd[0] = std::sinh(2*real)/denom;
23443         ptrd[1] = std::sin(2*imag)/denom;
23444         return cimg::type<double>::nan();
23445       }
23446 
23447       static double mp_continue(_cimg_math_parser& mp) {
23448         mp.break_type = 2;
23449         mp.p_code = mp.p_break - 1;
23450         return cimg::type<double>::nan();
23451       }
23452 
23453       static double mp_convolve(_cimg_math_parser &mp) {
23454         return _mp_correlate(mp,true);
23455       }
23456 
23457       static double mp_copy(_cimg_math_parser& mp) {
23458         return _mp_arg(2);
23459       }
23460 
23461       static double mp_correlate(_cimg_math_parser &mp) {
23462         return _mp_correlate(mp,false);
23463       }
23464 
23465       static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) {
23466         double *ptrd = &_mp_arg(1) + 1;
23467         const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1;
23468         const unsigned int
23469           wA = (unsigned int)mp.opcode[3],
23470           hA = (unsigned int)mp.opcode[4],
23471           dA = (unsigned int)mp.opcode[5],
23472           sA = (unsigned int)mp.opcode[6],
23473           wM = (unsigned int)mp.opcode[8],
23474           hM = (unsigned int)mp.opcode[9],
23475           dM = (unsigned int)mp.opcode[10],
23476           sM = (unsigned int)mp.opcode[11],
23477           boundary_conditions = (unsigned int)_mp_arg(12),
23478           channel_mode = (unsigned int)mp.opcode[14];
23479         const bool
23480           is_normalized = (bool)_mp_arg(13),
23481           interpolation_type = (bool)_mp_arg(30);
23482         const int
23483           xcenter = mp.opcode[15]!=~0U?(int)_mp_arg(15):(int)(~0U>>1),
23484           ycenter = mp.opcode[16]!=~0U?(int)_mp_arg(16):(int)(~0U>>1),
23485           zcenter = mp.opcode[17]!=~0U?(int)_mp_arg(17):(int)(~0U>>1),
23486           xstart = (int)mp.opcode[18],
23487           ystart = (int)mp.opcode[19],
23488           zstart = (int)mp.opcode[20],
23489           xend = (int)mp.opcode[21],
23490           yend = (int)mp.opcode[22],
23491           zend = (int)mp.opcode[23];
23492         const float
23493           xstride = (float)_mp_arg(24),
23494           ystride = (float)_mp_arg(25),
23495           zstride = (float)_mp_arg(26),
23496           xdilation = (float)_mp_arg(27),
23497           ydilation = (float)_mp_arg(28),
23498           zdilation = (float)_mp_arg(29);
23499         CImg<doubleT> res;
23500         if (is_convolve) res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23501                            get_convolve(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23502                                         boundary_conditions,is_normalized,channel_mode,
23503                                         xcenter,ycenter,zcenter,
23504                                         xstart,ystart,zstart,
23505                                         xend,yend,zend,
23506                                         xstride,ystride,zstride,
23507                                         xdilation,ydilation,zdilation,
23508                                         interpolation_type);
23509         else res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23510                get_correlate(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23511                              boundary_conditions,is_normalized,channel_mode,
23512                              xcenter,ycenter,zcenter,
23513                              xstart,ystart,zstart,
23514                              xend,yend,zend,
23515                              xstride,ystride,zstride,
23516                              xdilation,ydilation,zdilation,
23517                              interpolation_type);
23518         CImg<doubleT>(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res;
23519         return cimg::type<double>::nan();
23520       }
23521 
23522       static double mp_cos(_cimg_math_parser& mp) {
23523         return std::cos(_mp_arg(2));
23524       }
23525 
23526       static double mp_cosh(_cimg_math_parser& mp) {
23527         return std::cosh(_mp_arg(2));
23528       }
23529 
23530       static double mp_critical(_cimg_math_parser& mp) {
23531         const ulongT g_target = mp.opcode[1];
23532         cimg_pragma_openmp(critical(mp_critical))
23533         {
23534           for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
23535                mp.p_code<p_end; ++mp.p_code) { // Evaluate body
23536             mp.opcode._data = mp.p_code->_data;
23537             const ulongT target = mp.opcode[1];
23538             mp.mem[target] = _cimg_mp_defunc(mp);
23539           }
23540         }
23541         --mp.p_code;
23542         return mp.mem[g_target];
23543       }
23544 
23545       static double mp_crop(_cimg_math_parser& mp) {
23546         double *ptrd = &_mp_arg(1) + 1;
23547         const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
23548         const unsigned int
23549           dx = (unsigned int)mp.opcode[7],
23550           dy = (unsigned int)mp.opcode[8],
23551           dz = (unsigned int)mp.opcode[9],
23552           dc = (unsigned int)mp.opcode[10];
23553         const unsigned int boundary_conditions = (unsigned int)_mp_arg(11);
23554         unsigned int ind = (unsigned int)mp.opcode[2];
23555         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23556         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
23557         if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
23558         else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
23559                                                                  x + dx - 1,y + dy - 1,
23560                                                                  z + dz - 1,c + dc - 1,
23561                                                                  boundary_conditions);
23562         return cimg::type<double>::nan();
23563       }
23564 
23565       static double mp_cross(_cimg_math_parser& mp) {
23566         CImg<doubleT>
23567           vout(&_mp_arg(1) + 1,1,3,1,1,true),
23568           v1(&_mp_arg(2) + 1,1,3,1,1,true),
23569           v2(&_mp_arg(3) + 1,1,3,1,1,true);
23570         (vout = v1).cross(v2);
23571         return cimg::type<double>::nan();
23572       }
23573 
23574       static double mp_cut(_cimg_math_parser& mp) {
23575         double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
23576         return val<cmin?cmin:val>cmax?cmax:val;
23577       }
23578 
23579       static double mp_date(_cimg_math_parser& mp) {
23580         const unsigned int
23581           siz_out = (unsigned int)mp.opcode[2],
23582           siz_arg1 = (unsigned int)mp.opcode[4],
23583           siz_arg2 = (unsigned int)mp.opcode[6];
23584         double *ptr_out = &_mp_arg(1) + (siz_out?1:0);
23585         const double
23586           *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0),
23587           *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1;
23588 
23589         if (!ptr_arg2) { // No filename specified
23590           if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1);
23591           if (siz_arg1==~0U) for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = k;
23592           else for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23593           cimg::date(ptr_out,siz_out);
23594           return cimg::type<double>::nan();
23595         }
23596 
23597         // Filename specified.
23598         CImg<charT> ss(siz_arg2 + 1);
23599         cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i];
23600         ss.back() = 0;
23601         if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1);
23602         for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23603         cimg::fdate(ss,ptr_out,siz_out);
23604         return cimg::type<double>::nan();
23605       }
23606 
23607       static double mp_debug(_cimg_math_parser& mp) {
23608         CImg<charT> expr(mp.opcode[2] - 4);
23609         {
23610           const ulongT *ptrs = mp.opcode._data + 4;
23611           cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23612         }
23613         cimg::strellipsize(expr);
23614         const ulongT g_target = mp.opcode[1];
23615 
23616 #if cimg_use_openmp==0
23617         const unsigned int n_thread = 0;
23618 #else
23619         const unsigned int n_thread = omp_get_thread_num();
23620 #endif
23621         cimg_pragma_openmp(critical(mp_debug))
23622         {
23623           std::fprintf(cimg::output(),
23624                        "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23625                        "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
23626                        (void*)&mp,n_thread,mp.debug_indent,' ',
23627                        expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
23628           std::fflush(cimg::output());
23629           mp.debug_indent+=3;
23630         }
23631         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[3];
23632         CImg<ulongT> _op;
23633         for ( ; mp.p_code<p_end; ++mp.p_code) {
23634           const CImg<ulongT> &op = *mp.p_code;
23635           mp.opcode._data = op._data;
23636 
23637           _op.assign(1,op._height - 1);
23638           const ulongT *ptrs = op._data + 1;
23639           for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
23640             *ptrd = *(ptrs++);
23641 
23642           const ulongT target = mp.opcode[1];
23643           mp.mem[target] = _cimg_mp_defunc(mp);
23644           cimg_pragma_openmp(critical(mp_debug))
23645           {
23646             std::fprintf(cimg::output(),
23647                          "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23648                          "Opcode %p = [ %p,%s ] -> mem[%u] = %.17g",
23649                          (void*)&mp,n_thread,mp.debug_indent,' ',
23650                          (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
23651                          (unsigned int)target,mp.mem[target]);
23652             std::fflush(cimg::output());
23653           }
23654         }
23655         cimg_pragma_openmp(critical(mp_debug))
23656         {
23657           mp.debug_indent-=3;
23658           std::fprintf(cimg::output(),
23659             "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23660             "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)",
23661             (void*)&mp,n_thread,mp.debug_indent,' ',
23662             expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
23663           std::fflush(cimg::output());
23664         }
23665         --mp.p_code;
23666         return mp.mem[g_target];
23667       }
23668 
23669       static double mp_decrement(_cimg_math_parser& mp) {
23670         return _mp_arg(2) - 1;
23671       }
23672 
23673       static double mp_deg2rad(_cimg_math_parser& mp) {
23674         return _mp_arg(2)*cimg::PI/180;
23675       }
23676 
23677       static double mp_det(_cimg_math_parser& mp) {
23678         const double *ptrs = &_mp_arg(2) + 1;
23679         const unsigned int k = (unsigned int)mp.opcode[3];
23680         return CImg<doubleT>(ptrs,k,k,1,1,true).det();
23681       }
23682 
23683       static double mp_diag(_cimg_math_parser& mp) {
23684         const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3;
23685         double *ptrd = &_mp_arg(1) + 1;
23686         std::memset(ptrd,0,siz*siz*sizeof(double));
23687         for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; }
23688         return cimg::type<double>::nan();
23689       }
23690 
23691       static double mp_display_memory(_cimg_math_parser& mp) {
23692         cimg::unused(mp);
23693         std::fputc('\n',cimg::output());
23694         CImg<charT> title(128);
23695         cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width);
23696         mp.mem.display(title);
23697         return cimg::type<double>::nan();
23698       }
23699 
23700       static double mp_display(_cimg_math_parser& mp) {
23701         const unsigned int
23702           _siz = (unsigned int)mp.opcode[3],
23703           siz = _siz?_siz:1;
23704         const double *const ptr = &_mp_arg(1) + (_siz?1:0);
23705         const int
23706           w = (int)_mp_arg(4),
23707           h = (int)_mp_arg(5),
23708           d = (int)_mp_arg(6),
23709           s = (int)_mp_arg(7);
23710         CImg<doubleT> img;
23711         if (w>0 && h>0 && d>0 && s>0) {
23712           if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
23713           else img.assign(ptr,siz).resize(w,h,d,s,-1);
23714         } else img.assign(ptr,1,siz,1,1,true);
23715 
23716         CImg<charT> expr(mp.opcode[2] - 8);
23717         const ulongT *ptrs = mp.opcode._data + 8;
23718         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23719         ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
23720         cimg::strellipsize(expr);
23721         std::fputc('\n',cimg::output());
23722         img.display(expr._data);
23723         return cimg::type<double>::nan();
23724       }
23725 
23726       static double mp_div(_cimg_math_parser& mp) {
23727         return _mp_arg(2)/_mp_arg(3);
23728       }
23729 
23730       static double mp_dot(_cimg_math_parser& mp) {
23731         const unsigned int siz = (unsigned int)mp.opcode[4];
23732         return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
23733           dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
23734       }
23735 
23736       static double mp_do(_cimg_math_parser& mp) {
23737         const ulongT
23738           mem_body = mp.opcode[1],
23739           mem_cond = mp.opcode[2];
23740         const CImg<ulongT>
23741           *const p_body = ++mp.p_code,
23742           *const p_cond = p_body + mp.opcode[3],
23743           *const p_end = p_cond + mp.opcode[4];
23744         const unsigned int vsiz = (unsigned int)mp.opcode[5];
23745         if (mp.opcode[6]) { // Set default value for result and condition if necessary
23746           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
23747           else mp.mem[mem_body] = cimg::type<double>::nan();
23748         }
23749         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
23750 
23751         const unsigned int _break_type = mp.break_type;
23752         mp.break_type = 0;
23753         do {
23754           for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
23755             mp.opcode._data = mp.p_code->_data;
23756             const ulongT target = mp.opcode[1];
23757             mp.mem[target] = _cimg_mp_defunc(mp);
23758           }
23759           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23760           for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
23761             mp.opcode._data = mp.p_code->_data;
23762             const ulongT target = mp.opcode[1];
23763             mp.mem[target] = _cimg_mp_defunc(mp);
23764           }
23765           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23766         } while (mp.mem[mem_cond]);
23767         mp.break_type = _break_type;
23768         mp.p_code = p_end - 1;
23769         return mp.mem[mem_body];
23770       }
23771 
23772       static double mp_draw(_cimg_math_parser& mp) {
23773         const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
23774         unsigned int ind = (unsigned int)mp.opcode[3];
23775         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
23776         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23777         unsigned int
23778           dx = (unsigned int)mp.opcode[8],
23779           dy = (unsigned int)mp.opcode[9],
23780           dz = (unsigned int)mp.opcode[10],
23781           dc = (unsigned int)mp.opcode[11];
23782         dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
23783         dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
23784         dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
23785         dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
23786 
23787         const ulongT sizS = mp.opcode[2];
23788         if (sizS<(ulongT)dx*dy*dz*dc)
23789           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
23790                                       "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
23791                                       "(%lu values) do not match.",
23792                                       mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
23793         CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
23794         const float opacity = (float)_mp_arg(12);
23795 
23796         if (img._data) {
23797           if (mp.opcode[13]!=~0U) { // Opacity mask specified
23798             const ulongT sizM = mp.opcode[14];
23799             if (sizM<(ulongT)dx*dy*dz)
23800               throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
23801                                           "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
23802                                           "(%lu values) do not match.",
23803                                           mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
23804             const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
23805             img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
23806           } else img.draw_image(x,y,z,c,S,opacity);
23807         }
23808         return cimg::type<double>::nan();
23809       }
23810 
23811       static double mp_echo(_cimg_math_parser& mp) {
23812         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
23813         if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type<double>::nan(); } // No arguments
23814         CImgList<charT> _str;
23815         CImg<charT> it;
23816         for (unsigned int n = 0; n<nb_args; ++n) {
23817           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
23818           if (siz) { // Vector argument -> string
23819             const double *ptr = &_mp_arg(3 + 2*n) + 1;
23820             unsigned int l = 0;
23821             while (l<siz && ptr[l]) ++l;
23822             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
23823           } else { // Scalar argument -> number
23824             it.assign(24);
23825             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
23826             CImg<charT>::string(it,false,true).move_to(_str);
23827           }
23828         }
23829         CImg(1,1,1,1,0).move_to(_str);
23830         const CImg<charT> str = _str>'x';
23831         std::fprintf(cimg::output(),"\n%s",str._data);
23832         return cimg::type<double>::nan();
23833       }
23834 
23835       static double mp_ellipse(_cimg_math_parser& mp) {
23836         const unsigned int i_end = (unsigned int)mp.opcode[2];
23837         unsigned int ind = (unsigned int)mp.opcode[3];
23838         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
23839         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23840         CImg<T> color(img._spectrum,1,1,1,0);
23841         bool is_invalid_arguments = false, is_outlined = false;
23842         float r1 = 0, r2 = 0, angle = 0, opacity = 1;
23843         unsigned int i = 4, pattern = ~0U;
23844         int x0 = 0, y0 = 0;
23845         if (i>=i_end) is_invalid_arguments = true;
23846         else {
23847           x0 = (int)cimg::round(_mp_arg(i++));
23848           if (i>=i_end) is_invalid_arguments = true;
23849           else {
23850             y0 = (int)cimg::round(_mp_arg(i++));
23851             if (i>=i_end) is_invalid_arguments = true;
23852             else {
23853               r1 = (float)_mp_arg(i++);
23854               if (i>=i_end) r2 = r1;
23855               else {
23856                 r2 = (float)_mp_arg(i++);
23857                 if (i<i_end) {
23858                   angle = (float)(_mp_arg(i++)*180/cimg::PI);
23859                   if (i<i_end) {
23860                     opacity = (float)_mp_arg(i++);
23861                     if (r1<0 && r2<0) {
23862                       pattern = (unsigned int)_mp_arg(i++);
23863                       is_outlined = true;
23864                       r1 = -r1; r2 = -r2;
23865                     }
23866                     if (i<i_end) {
23867                       cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
23868                       else { color.resize(k,1,1,1,-1); break; }
23869                       color.resize(img._spectrum,1,1,1,0,2);
23870                     }
23871                   }
23872                 }
23873               }
23874             }
23875           }
23876         }
23877         if (!is_invalid_arguments) {
23878           if (is_outlined) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity,pattern);
23879           else img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
23880         } else {
23881           CImg<doubleT> args(i_end - 4);
23882           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
23883           if (ind==~0U)
23884             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
23885                                         "Invalid arguments '%s'. ",
23886                                         mp.imgin.pixel_type(),args.value_string()._data);
23887           else
23888             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
23889                                         "Invalid arguments '#%u%s%s'. ",
23890                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
23891         }
23892         return cimg::type<double>::nan();
23893       }
23894 
23895       static double mp_eq(_cimg_math_parser& mp) {
23896         return (double)(_mp_arg(2)==_mp_arg(3));
23897       }
23898 
23899       static double mp_erf(_cimg_math_parser& mp) {
23900         return std::erf(_mp_arg(2));
23901       }
23902 
23903       static double mp_erfinv(_cimg_math_parser& mp) {
23904         return cimg::erfinv(_mp_arg(2));
23905       }
23906 
23907       static double mp_exp(_cimg_math_parser& mp) {
23908         return std::exp(_mp_arg(2));
23909       }
23910 
23911       static double mp_expr(_cimg_math_parser& mp) {
23912         const unsigned int
23913           sizs = (unsigned int)mp.opcode[3],
23914           w = (unsigned int)mp.opcode[4],
23915           h = (unsigned int)mp.opcode[5],
23916           d = (unsigned int)mp.opcode[6],
23917           s = (unsigned int)mp.opcode[7],
23918           sizd = w*h*d*s;
23919         const double *ptrs = &_mp_arg(2) + 1;
23920         double *ptrd = &_mp_arg(1);
23921         CImg<charT> ss(sizs + 1);
23922         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
23923         ss.back() = 0;
23924         if (!sizd) return CImg<T>(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result
23925         CImg<doubleT>(++ptrd,w,h,d,s,true) = CImg<T>(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout);
23926         return cimg::type<double>::nan();
23927       }
23928 
23929       static double mp_eye(_cimg_math_parser& mp) {
23930         double *ptrd = &_mp_arg(1) + 1;
23931         const unsigned int k = (unsigned int)mp.opcode[2];
23932         CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
23933         return cimg::type<double>::nan();
23934       }
23935 
23936       static double mp_f2ui(_cimg_math_parser& mp) {
23937         return (double)cimg::float2uint((float)_mp_arg(2));
23938       }
23939 
23940       static double mp_factorial(_cimg_math_parser& mp) {
23941         return cimg::factorial((int)_mp_arg(2));
23942       }
23943 
23944       static double mp_fibonacci(_cimg_math_parser& mp) {
23945         return cimg::fibonacci((int)_mp_arg(2));
23946       }
23947 
23948       static double mp_fill(_cimg_math_parser& mp) {
23949         unsigned int siz = (unsigned int)mp.opcode[2];
23950         double
23951           *ptrd = &_mp_arg(1),
23952           *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
23953           *const ptrs = &_mp_arg(4);
23954         if (siz) ++ptrd; else ++siz; // Fill vector-valued slot
23955         const CImg<ulongT>
23956           *const p_body = ++mp.p_code,
23957           *const p_end = p_body + mp.opcode[5];
23958         const unsigned int _break_type = mp.break_type;
23959         mp.break_type = 0;
23960 
23961         unsigned int it = 0;
23962         if (ptrc) { // Version with loop variable (3 arguments)
23963           while (it<siz) {
23964             *ptrc = (double)it;
23965             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
23966               mp.opcode._data = mp.p_code->_data;
23967               const ulongT target = mp.opcode[1];
23968               mp.mem[target] = _cimg_mp_defunc(mp);
23969             }
23970             if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23971             else ptrd[it] = *ptrs;
23972             ++it;
23973           }
23974           *ptrc = (double)it;
23975         } else // Version without loop variable (2 arguments)
23976           while (it<siz) {
23977             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
23978               mp.opcode._data = mp.p_code->_data;
23979               const ulongT target = mp.opcode[1];
23980               mp.mem[target] = _cimg_mp_defunc(mp);
23981             }
23982             if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23983             else ptrd[it] = *ptrs;
23984             ++it;
23985           }
23986 
23987         mp.break_type = _break_type;
23988         mp.p_code = p_end - 1;
23989         return *ptrd;
23990       }
23991 
23992       static double mp_find(_cimg_math_parser& mp) {
23993         const int _step = (int)_mp_arg(6), step = _step?_step:-1;
23994         const ulongT siz = (ulongT)mp.opcode[3];
23995         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1);
23996         if (ind<0 || ind>=(longT)siz) return -1.;
23997         const double
23998           *const ptrb = &_mp_arg(2) + 1,
23999           *const ptre = ptrb + siz,
24000           val = _mp_arg(4),
24001           *ptr = ptrb + ind;
24002 
24003         // Forward search
24004         if (step>0) {
24005           while (ptr<ptre && *ptr!=val) ptr+=step;
24006           return ptr>=ptre?-1.:(double)(ptr - ptrb);
24007         }
24008 
24009         // Backward search.
24010         while (ptr>=ptrb && *ptr!=val) ptr+=step;
24011         return ptr<ptrb?-1.:(double)(ptr - ptrb);
24012       }
24013 
24014       static double mp_find_seq(_cimg_math_parser& mp) {
24015         const int _step = (int)_mp_arg(7), step = _step?_step:-1;
24016         const ulongT
24017           siz1 = (ulongT)mp.opcode[3],
24018           siz2 = (ulongT)mp.opcode[5];
24019         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):step>0?0:siz1 - 1);
24020         if (ind<0 || ind>=(longT)siz1) return -1.;
24021         const double
24022           *const ptr1b = &_mp_arg(2) + 1,
24023           *const ptr1e = ptr1b + siz1,
24024           *const ptr2b = &_mp_arg(4) + 1,
24025           *const ptr2e = ptr2b + siz2,
24026           *ptr1 = ptr1b + ind,
24027           *p1 = 0,
24028           *p2 = 0;
24029 
24030         // Forward search.
24031         if (step>0) {
24032           do {
24033             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
24034             if (ptr1>=ptr1e) return -1.;
24035             p1 = ptr1 + 1;
24036             p2 = ptr2b + 1;
24037             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24038           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
24039           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24040         }
24041 
24042         // Backward search.
24043         do {
24044           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
24045           if (ptr1<ptr1b) return -1.;
24046           p1 = ptr1 + 1;
24047           p2 = ptr2b + 1;
24048           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24049         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
24050         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24051       }
24052 
24053       static double mp_floor(_cimg_math_parser& mp) {
24054         return std::floor(_mp_arg(2));
24055       }
24056 
24057       static double mp_for(_cimg_math_parser& mp) {
24058         const ulongT
24059           mem_body = mp.opcode[1],
24060           mem_cond = mp.opcode[3];
24061         const CImg<ulongT>
24062           *const p_init = ++mp.p_code,
24063           *const p_cond = p_init + mp.opcode[4],
24064           *const p_body = p_cond + mp.opcode[5],
24065           *const p_post = p_body + mp.opcode[6],
24066           *const p_end = p_post + mp.opcode[7];
24067         const unsigned int vsiz = (unsigned int)mp.opcode[2];
24068         bool is_cond = false;
24069         if (mp.opcode[8]) { // Set default value for result and condition if necessary
24070           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
24071           else mp.mem[mem_body] = cimg::type<double>::nan();
24072         }
24073         if (mp.opcode[9]) mp.mem[mem_cond] = 0;
24074         const unsigned int _break_type = mp.break_type;
24075         mp.break_type = 0;
24076 
24077         for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
24078           mp.opcode._data = mp.p_code->_data;
24079           const ulongT target = mp.opcode[1];
24080           mp.mem[target] = _cimg_mp_defunc(mp);
24081         }
24082 
24083         if (!mp.break_type) do {
24084             for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
24085               mp.opcode._data = mp.p_code->_data;
24086               const ulongT target = mp.opcode[1];
24087               mp.mem[target] = _cimg_mp_defunc(mp);
24088             }
24089             if (mp.break_type==1) break;
24090 
24091             is_cond = (bool)mp.mem[mem_cond];
24092             if (is_cond && !mp.break_type) {
24093               for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
24094                 mp.opcode._data = mp.p_code->_data;
24095                 const ulongT target = mp.opcode[1];
24096                 mp.mem[target] = _cimg_mp_defunc(mp);
24097               }
24098               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24099 
24100               for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
24101                 mp.opcode._data = mp.p_code->_data;
24102                 const ulongT target = mp.opcode[1];
24103                 mp.mem[target] = _cimg_mp_defunc(mp);
24104               }
24105               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24106             }
24107           } while (is_cond);
24108 
24109         mp.break_type = _break_type;
24110         mp.p_code = p_end - 1;
24111         return mp.mem[mem_body];
24112       }
24113 
24114       static double mp_fsize(_cimg_math_parser& mp) {
24115         const double *ptrs = &_mp_arg(2) + 1;
24116         const ulongT siz = (ulongT)mp.opcode[3];
24117         CImg<charT> ss(siz + 1);
24118         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24119         ss.back() = 0;
24120         return (double)cimg::fsize(ss);
24121       }
24122 
24123       static double mp_g(_cimg_math_parser& mp) {
24124         cimg::unused(mp);
24125         return cimg::grand(&mp.rng);
24126       }
24127 
24128       static double mp_gauss(_cimg_math_parser& mp) {
24129         const double x = _mp_arg(2), s = _mp_arg(3);
24130         return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
24131       }
24132 
24133 #ifdef cimg_mp_func_get
24134       static double mp_get(_cimg_math_parser& mp) {
24135         const double *ptrs = &_mp_arg(2) + 1;
24136         double *ptrd = &_mp_arg(1);
24137         const unsigned int
24138           sizs = (unsigned int)mp.opcode[3],
24139           sizd = (unsigned int)mp.opcode[4];
24140         const bool to_string = (bool)mp.opcode[5];
24141         CImg<charT> ss(sizs + 1);
24142         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
24143         ss.back() = 0;
24144         if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data);
24145         else cimg_mp_func_get(ptrd,0,to_string,ss._data);
24146         return cimg::type<double>::nan();
24147       }
24148 #endif
24149 
24150       static double mp_gcd(_cimg_math_parser& mp) {
24151         return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
24152       }
24153 
24154 #ifdef cimg_mp_func_name
24155       static double mp_name(_cimg_math_parser& mp) {
24156         double *const ptr = &_mp_arg(1) + 1;
24157         const unsigned int siz = (unsigned int)mp.opcode[3];
24158         unsigned int ind = (unsigned int)mp.opcode[2];
24159         if (ind==~0U) std::memset(ptr,0,siz*sizeof(double));
24160         else {
24161           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24162           cimg_mp_func_name(ind,ptr,siz);
24163         }
24164         return cimg::type<double>::nan();
24165       }
24166 #endif
24167 
24168       static double mp_gt(_cimg_math_parser& mp) {
24169         return (double)(_mp_arg(2)>_mp_arg(3));
24170       }
24171 
24172       static double mp_gte(_cimg_math_parser& mp) {
24173         return (double)(_mp_arg(2)>=_mp_arg(3));
24174       }
24175 
24176       static double mp_i(_cimg_math_parser& mp) {
24177         return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
24178                                        (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
24179       }
24180 
24181       static double mp_if(_cimg_math_parser& mp) {
24182         const bool is_cond = (bool)_mp_arg(2);
24183         const ulongT
24184           mem_left = mp.opcode[3],
24185           mem_right = mp.opcode[4];
24186         const CImg<ulongT>
24187           *const p_right = ++mp.p_code + mp.opcode[5],
24188           *const p_end = p_right + mp.opcode[6];
24189         const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
24190         if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
24191             mp.opcode._data = mp.p_code->_data;
24192             const ulongT target = mp.opcode[1];
24193             mp.mem[target] = _cimg_mp_defunc(mp);
24194           }
24195         else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
24196             mp.opcode._data = mp.p_code->_data;
24197             const ulongT target = mp.opcode[1];
24198             mp.mem[target] = _cimg_mp_defunc(mp);
24199           }
24200         if (mp.p_code==mp.p_break) --mp.p_code;
24201         else mp.p_code = p_end - 1;
24202         if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
24203         return mp.mem[is_cond?mem_left:mem_right];
24204       }
24205 
24206       static double mp_image_d(_cimg_math_parser& mp) {
24207         unsigned int ind = (unsigned int)mp.opcode[2];
24208         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24209         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24210         return (double)img.depth();
24211       }
24212 
24213       static double mp_image_display(_cimg_math_parser& mp) {
24214         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24215         cimg::mutex(6);
24216         CImg<T> &img = mp.listout[ind];
24217         CImg<charT> title(256);
24218         std::fputc('\n',cimg::output());
24219         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
24220         img.display(title);
24221         cimg::mutex(6,0);
24222         return cimg::type<double>::nan();
24223       }
24224 
24225       static double mp_image_h(_cimg_math_parser& mp) {
24226         unsigned int ind = (unsigned int)mp.opcode[2];
24227         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24228         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24229         return (double)img.height();
24230       }
24231 
24232       static double mp_image_median(_cimg_math_parser& mp) {
24233         unsigned int ind = (unsigned int)mp.opcode[2];
24234         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24235         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24236         return (double)img.median();
24237       }
24238 
24239       static double mp_image_norm(_cimg_math_parser& mp) {
24240         unsigned int ind = (unsigned int)mp.opcode[2];
24241         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24242         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24243         return (double)img.magnitude();
24244       }
24245 
24246       static double mp_image_print(_cimg_math_parser& mp) {
24247         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24248         cimg::mutex(6);
24249         CImg<T> &img = mp.listout[ind];
24250         CImg<charT> title(256);
24251         std::fputc('\n',cimg::output());
24252         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
24253         img.print(title);
24254         cimg::mutex(6,0);
24255         return cimg::type<double>::nan();
24256       }
24257 
24258       static double mp_image_resize(_cimg_math_parser& mp) {
24259         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24260         cimg::mutex(6);
24261         CImg<T> &img = mp.listout[ind];
24262         const double
24263           _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
24264           _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
24265           _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
24266           _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
24267         const unsigned int
24268           w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
24269           h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
24270           d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
24271           s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
24272           interp = (int)_mp_arg(7);
24273         if (mp.is_fill && img._data==mp.imgout._data) {
24274           cimg::mutex(6,0);
24275           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
24276                                       "Cannot both fill and resize image (%u,%u,%u,%u) "
24277                                       "to new dimensions (%u,%u,%u,%u).",
24278                                       img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
24279         }
24280         const unsigned int
24281           boundary = (int)_mp_arg(8);
24282         const float
24283           cx = (float)_mp_arg(9),
24284           cy = (float)_mp_arg(10),
24285           cz = (float)_mp_arg(11),
24286           cc = (float)_mp_arg(12);
24287         img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
24288         cimg::mutex(6,0);
24289         return cimg::type<double>::nan();
24290       }
24291 
24292       static double mp_image_s(_cimg_math_parser& mp) {
24293         unsigned int ind = (unsigned int)mp.opcode[2];
24294         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24295         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24296         return (double)img.spectrum();
24297       }
24298 
24299       static double mp_image_sort(_cimg_math_parser& mp) {
24300         const bool is_increasing = (bool)_mp_arg(3);
24301         const unsigned int
24302           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
24303           axis = (unsigned int)_mp_arg(4);
24304         cimg::mutex(6);
24305         CImg<T> &img = mp.listout[ind];
24306         img.sort(is_increasing,
24307                  axis==0 || axis=='x'?'x':
24308                  axis==1 || axis=='y'?'y':
24309                  axis==2 || axis=='z'?'z':
24310                  axis==3 || axis=='c'?'c':0);
24311         cimg::mutex(6,0);
24312         return cimg::type<double>::nan();
24313       }
24314 
24315       static double mp_image_stats(_cimg_math_parser& mp) {
24316         double *ptrd = &_mp_arg(1) + 1;
24317         unsigned int ind = (unsigned int)mp.opcode[2];
24318         if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
24319         else {
24320           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24321           CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
24322         }
24323         return cimg::type<double>::nan();
24324       }
24325 
24326       static double mp_image_w(_cimg_math_parser& mp) {
24327         unsigned int ind = (unsigned int)mp.opcode[2];
24328         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24329         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24330         return (double)img.width();
24331       }
24332 
24333       static double mp_image_wh(_cimg_math_parser& mp) {
24334         unsigned int ind = (unsigned int)mp.opcode[2];
24335         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24336         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24337         return (double)img.width()*img.height();
24338       }
24339 
24340       static double mp_image_whd(_cimg_math_parser& mp) {
24341         unsigned int ind = (unsigned int)mp.opcode[2];
24342         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24343         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24344         return (double)img.width()*img.height()*img.depth();
24345       }
24346 
24347       static double mp_image_whds(_cimg_math_parser& mp) {
24348         unsigned int ind = (unsigned int)mp.opcode[2];
24349         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24350         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24351         return (double)img.width()*img.height()*img.depth()*img.spectrum();
24352       }
24353 
24354       static double mp_increment(_cimg_math_parser& mp) {
24355         return _mp_arg(2) + 1;
24356       }
24357 
24358       static double mp_inrange(_cimg_math_parser& mp) {
24359         const unsigned int sizd = (unsigned int)mp.opcode[2];
24360         const bool
24361           include_m = (bool)_mp_arg(9),
24362           include_M = (bool)_mp_arg(10);
24363         if (!sizd) { // Scalar result
24364           const double val = _mp_arg(3);
24365           const double m = _mp_arg(5), M = _mp_arg(7);
24366           if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24367           else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24368         }
24369 
24370         // Vector result
24371         const unsigned int
24372           siz1 = (unsigned int)mp.opcode[4],
24373           siz2 = (unsigned int)mp.opcode[6],
24374           siz3 = (unsigned int)mp.opcode[8],
24375           off1 = siz1?1:0,
24376           off2 = siz2?1:0,
24377           off3 = siz3?1:0;
24378         double *ptrd = &_mp_arg(1) + 1;
24379         const double
24380           *ptr1 = &_mp_arg(3) + off1,
24381           *ptr2 = &_mp_arg(5) + off2,
24382           *ptr3 = &_mp_arg(7) + off3;
24383         for (unsigned int k = 0; k<sizd; ++k) {
24384           const double val = *ptr1;
24385           const double m = *ptr2, M = *ptr3;
24386           if (M>=m)
24387             ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24388           else
24389             ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24390           ptr1+=off1;
24391           ptr2+=off2;
24392           ptr3+=off3;
24393         }
24394         return cimg::type<double>::nan();
24395       }
24396 
24397       static double mp_int(_cimg_math_parser& mp) {
24398         return (double)(longT)_mp_arg(2);
24399       }
24400 
24401       static double mp_ioff(_cimg_math_parser& mp) {
24402         const unsigned int
24403           boundary_conditions = (unsigned int)_mp_arg(3);
24404         const CImg<T> &img = mp.imgin;
24405         const longT
24406           off = (longT)_mp_arg(2),
24407           whds = (longT)img.size();
24408         if (off>=0 && off<whds) return (double)img[off];
24409         if (img._data) switch (boundary_conditions) {
24410           case 3 : { // Mirror
24411             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24412             return (double)img[moff<whds?moff:whds2 - moff - 1];
24413           }
24414           case 2 : // Periodic
24415             return (double)img[cimg::mod(off,whds)];
24416           case 1 : // Neumann
24417             return (double)img[off<0?0:whds - 1];
24418           default : // Dirichlet
24419             return 0;
24420           }
24421         return 0;
24422       }
24423 
24424       static double mp_isbool(_cimg_math_parser& mp) {
24425         const double val = _mp_arg(2);
24426         return (double)(val==0. || val==1.);
24427       }
24428 
24429       static double mp_isdir(_cimg_math_parser& mp) {
24430         const double *ptrs = &_mp_arg(2) + 1;
24431         const ulongT siz = (ulongT)mp.opcode[3];
24432         CImg<charT> ss(siz + 1);
24433         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24434         ss.back() = 0;
24435         return (double)cimg::is_directory(ss);
24436       }
24437 
24438       static double mp_isin(_cimg_math_parser& mp) {
24439         const unsigned int i_end = (unsigned int)mp.opcode[2];
24440         const double val = _mp_arg(3);
24441         for (unsigned int i = 4; i<i_end; ++i)
24442           if (val==_mp_arg(i)) return 1.;
24443         return 0.;
24444       }
24445 
24446       static double mp_isinf(_cimg_math_parser& mp) {
24447         return (double)cimg::type<double>::is_inf(_mp_arg(2));
24448       }
24449 
24450       static double mp_isint(_cimg_math_parser& mp) {
24451         return (double)((double)(longT)_mp_arg(2)==_mp_arg(2));
24452       }
24453 
24454       static double mp_isfile(_cimg_math_parser& mp) {
24455         const double *ptrs = &_mp_arg(2) + 1;
24456         const ulongT siz = (ulongT)mp.opcode[3];
24457         CImg<charT> ss(siz + 1);
24458         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24459         ss.back() = 0;
24460         return (double)cimg::is_file(ss);
24461       }
24462 
24463       static double mp_isnan(_cimg_math_parser& mp) {
24464         return (double)cimg::type<double>::is_nan(_mp_arg(2));
24465       }
24466 
24467       static double mp_ixyzc(_cimg_math_parser& mp) {
24468         const unsigned int
24469           interpolation = (unsigned int)_mp_arg(6),
24470           boundary_conditions = (unsigned int)_mp_arg(7);
24471         const CImg<T> &img = mp.imgin;
24472         const double
24473           x = _mp_arg(2), y = _mp_arg(3),
24474           z = _mp_arg(4), c = _mp_arg(5);
24475         switch (interpolation) {
24476         case 2 : // Cubic interpolation
24477           switch (boundary_conditions) {
24478           case 3 : { // Mirror
24479             const float
24480               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24481               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24482               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24483             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24484                                             my<img.height()?my:h2 - my - 1,
24485                                             mz<img.depth()?mz:d2 - mz - 1,
24486                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24487           }
24488           case 2 : // Periodic
24489             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24490                                               (int)cimg::mod(c,(double)img._spectrum));
24491           case 1 : // Neumann
24492             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24493                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24494           default : // Dirichlet
24495             if (c<0 || c>=img._spectrum) return (T)0;
24496             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24497           }
24498         case 1 : // Linear interpolation
24499           switch (boundary_conditions) {
24500           case 3 : { // Mirror
24501             const float
24502               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24503               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24504               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24505             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24506                                              my<img.height()?my:h2 - my - 1,
24507                                              mz<img.depth()?mz:d2 - mz - 1,
24508                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24509           }
24510           case 2 : // Periodic
24511             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24512                                                (int)cimg::mod(c,(double)img._spectrum));
24513           case 1 : // Neumann
24514             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24515                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24516           default : // Dirichlet
24517             if (c<0 || c>=img._spectrum) return (T)0;
24518             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24519           }
24520         default : // Nearest neighbor interpolation
24521           switch (boundary_conditions) {
24522           case 3 : { // Mirror
24523             const int
24524               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24525               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24526               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24527             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24528                                my<img.height()?my:h2 - my - 1,
24529                                mz<img.depth()?mz:d2 - mz - 1,
24530                                mc<img.spectrum()?mc:s2 - mc - 1);
24531           }
24532           case 2 : // Periodic
24533             return (double)img((int)cimg::mod(x,(double)img._width),
24534                                (int)cimg::mod(y,(double)img._height),
24535                                (int)cimg::mod(z,(double)img._depth),
24536                                (int)cimg::mod(c,(double)img._spectrum));
24537           case 1 : // Neumann
24538             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24539           default : // Dirichlet
24540             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24541           }
24542         }
24543       }
24544 
24545       static double mp_joff(_cimg_math_parser& mp) {
24546         const unsigned int
24547           boundary_conditions = (unsigned int)_mp_arg(3);
24548         const int
24549           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24550           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24551         const CImg<T> &img = mp.imgin;
24552         const longT
24553           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
24554           whds = (longT)img.size();
24555         if (off>=0 && off<whds) return (double)img[off];
24556         if (img._data) switch (boundary_conditions) {
24557           case 3 : { // Mirror
24558             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24559             return (double)img[moff<whds?moff:whds2 - moff - 1];
24560           }
24561           case 2 : // Periodic
24562             return (double)img[cimg::mod(off,whds)];
24563           case 1 : // Neumann
24564             return (double)img[off<0?0:whds - 1];
24565           default : // Dirichlet
24566             return 0;
24567           }
24568         return 0;
24569       }
24570 
24571       static double mp_jxyzc(_cimg_math_parser& mp) {
24572         const unsigned int
24573           interpolation = (unsigned int)_mp_arg(6),
24574           boundary_conditions = (unsigned int)_mp_arg(7);
24575         const CImg<T> &img = mp.imgin;
24576         const double
24577           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24578           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
24579           x = ox + _mp_arg(2), y = oy + _mp_arg(3),
24580           z = oz + _mp_arg(4), c = oc + _mp_arg(5);
24581         switch (interpolation) {
24582         case 2 : // Cubic interpolation
24583           switch (boundary_conditions) {
24584           case 3 : { // Mirror
24585             const float
24586               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24587               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24588               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24589             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24590                                             my<img.height()?my:h2 - my - 1,
24591                                             mz<img.depth()?mz:d2 - mz - 1,
24592                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24593           }
24594           case 2 : // Periodic
24595             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24596                                               (int)cimg::mod(c,(double)img._spectrum));
24597           case 1 : // Neumann
24598             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24599                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24600           default : // Dirichlet
24601             if (c<0 || c>=img._spectrum) return (T)0;
24602             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24603           }
24604         case 1 : // Linear interpolation
24605           switch (boundary_conditions) {
24606           case 3 : { // Mirror
24607             const float
24608               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24609               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24610               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24611             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24612                                              my<img.height()?my:h2 - my - 1,
24613                                              mz<img.depth()?mz:d2 - mz - 1,
24614                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24615           }
24616           case 2 : // Periodic
24617             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24618                                                (int)cimg::mod(c,(double)img._spectrum));
24619           case 1 : // Neumann
24620             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24621                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24622           default : // Dirichlet
24623             if (c<0 || c>=img._spectrum) return (T)0;
24624             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24625           }
24626         default : // Nearest neighbor interpolation
24627           switch (boundary_conditions) {
24628           case 3 : { // Mirror
24629             const int
24630               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24631               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24632               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24633             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24634                                my<img.height()?my:h2 - my - 1,
24635                                mz<img.depth()?mz:d2 - mz - 1,
24636                                mc<img.spectrum()?mc:s2 - mc - 1);
24637           }
24638           case 2 : // Periodic
24639             return (double)img((int)cimg::mod(x,(double)img._width),
24640                                (int)cimg::mod(y,(double)img._height),
24641                                (int)cimg::mod(z,(double)img._depth),
24642                                (int)cimg::mod(c,(double)img._spectrum));
24643           case 1 : // Neumann
24644             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24645           default : // Dirichlet
24646             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24647           }
24648         }
24649       }
24650 
24651       static double mp_kth(_cimg_math_parser& mp) {
24652         const unsigned int i_end = (unsigned int)mp.opcode[2];
24653         CImg<doubleT> vals(i_end - 4);
24654         double *p = vals.data();
24655         for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
24656         longT ind = (longT)cimg::round(_mp_arg(3));
24657         if (ind<0) ind+=vals.width() + 1;
24658         ind = cimg::cut(ind,(longT)1,(longT)vals.width());
24659         return vals.kth_smallest((ulongT)(ind - 1));
24660       }
24661 
24662       static double mp_lerp(_cimg_math_parser& mp) {
24663         const double t = _mp_arg(4);
24664         return _mp_arg(2)*(1-t) + _mp_arg(3)*t;
24665       }
24666 
24667       static double mp_linear_add(_cimg_math_parser& mp) {
24668         return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
24669       }
24670 
24671       static double mp_linear_sub_left(_cimg_math_parser& mp) {
24672         return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
24673       }
24674 
24675       static double mp_linear_sub_right(_cimg_math_parser& mp) {
24676         return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
24677       }
24678 
24679       static double mp_list_depth(_cimg_math_parser& mp) {
24680         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24681         return (double)mp.listin[ind]._depth;
24682       }
24683 
24684       static double mp_list_find(_cimg_math_parser& mp) {
24685         const unsigned int
24686           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24687         const CImg<T> &img = mp.listin[indi];
24688         const int _step = (int)_mp_arg(5), step = _step?_step:-1;
24689         const ulongT siz = (ulongT)img.size();
24690         longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1);
24691         if (ind<0 || ind>=(longT)siz) return -1.;
24692         const T
24693           *const ptrb = img.data(),
24694           *const ptre = img.end(),
24695           *ptr = ptrb + ind;
24696         const double val = _mp_arg(3);
24697 
24698         // Forward search
24699         if (step>0) {
24700           while (ptr<ptre && (double)*ptr!=val) ptr+=step;
24701           return ptr>=ptre?-1.:(double)(ptr - ptrb);
24702         }
24703 
24704         // Backward search.
24705         while (ptr>=ptrb && (double)*ptr!=val) ptr+=step;
24706         return ptr<ptrb?-1.:(double)(ptr - ptrb);
24707       }
24708 
24709       static double mp_list_find_seq(_cimg_math_parser& mp) {
24710         const unsigned int
24711           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24712         const CImg<T> &img = mp.listin[indi];
24713         const int _step = (bool)_mp_arg(6), step = _step?_step:-1;
24714         const ulongT
24715           siz1 = (ulongT)img.size(),
24716           siz2 = (ulongT)mp.opcode[4];
24717         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1);
24718         if (ind<0 || ind>=(longT)siz1) return -1.;
24719         const T
24720           *const ptr1b = img.data(),
24721           *const ptr1e = ptr1b + siz1,
24722           *ptr1 = ptr1b + ind,
24723           *p1 = 0;
24724         const double
24725           *const ptr2b = &_mp_arg(3) + 1,
24726           *const ptr2e = ptr2b + siz2,
24727           *p2 = 0;
24728 
24729         // Forward search.
24730         if (step>0) {
24731           do {
24732             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
24733             if (ptr1>=ptr1e) return -1.;
24734             p1 = ptr1 + 1;
24735             p2 = ptr2b + 1;
24736             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24737           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
24738           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24739         }
24740 
24741         // Backward search.
24742         do {
24743           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
24744           if (ptr1<ptr1b) return -1.;
24745           p1 = ptr1 + 1;
24746           p2 = ptr2b + 1;
24747           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24748         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
24749         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24750       }
24751 
24752       static double mp_list_height(_cimg_math_parser& mp) {
24753         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24754         return (double)mp.listin[ind]._height;
24755       }
24756 
24757       static double mp_list_ioff(_cimg_math_parser& mp) {
24758         const unsigned int
24759           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24760           boundary_conditions = (unsigned int)_mp_arg(4);
24761         const CImg<T> &img = mp.listin[ind];
24762         const longT
24763           off = (longT)_mp_arg(3),
24764           whds = (longT)img.size();
24765         if (off>=0 && off<whds) return (double)img[off];
24766         if (img._data) switch (boundary_conditions) {
24767           case 3 : { // Mirror
24768             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24769             return (double)img[moff<whds?moff:whds2 - moff - 1];
24770           }
24771           case 2 : // Periodic
24772             return (double)img[cimg::mod(off,whds)];
24773           case 1 : // Neumann
24774             return (double)img[off<0?0:whds - 1];
24775           default : // Dirichlet
24776             return 0;
24777           }
24778         return 0;
24779       }
24780 
24781       static double mp_list_is_shared(_cimg_math_parser& mp) {
24782         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24783         return (double)mp.listin[ind]._is_shared;
24784       }
24785 
24786       static double mp_list_ixyzc(_cimg_math_parser& mp) {
24787         const unsigned int
24788           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24789           interpolation = (unsigned int)_mp_arg(7),
24790           boundary_conditions = (unsigned int)_mp_arg(8);
24791         const CImg<T> &img = mp.listin[ind];
24792         const double
24793           x = _mp_arg(3), y = _mp_arg(4),
24794           z = _mp_arg(5), c = _mp_arg(6);
24795         switch (interpolation) {
24796         case 2 : // Cubic interpolation
24797           switch (boundary_conditions) {
24798           case 3 : { // Mirror
24799             const float
24800               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24801               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24802               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24803             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24804                                             my<img.height()?my:h2 - my - 1,
24805                                             mz<img.depth()?mz:d2 - mz - 1,
24806                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24807           }
24808           case 2 : // Periodic
24809             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24810                                               (int)cimg::mod(c,(double)img._spectrum));
24811           case 1 : // Neumann
24812             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24813                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24814           default : // Dirichlet
24815             if (c<0 || c>=img._spectrum) return (T)0;
24816             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24817           }
24818         case 1 : // Linear interpolation
24819           switch (boundary_conditions) {
24820           case 3 : { // Mirror
24821             const float
24822               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24823               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24824               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24825             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24826                                              my<img.height()?my:h2 - my - 1,
24827                                              mz<img.depth()?mz:d2 - mz - 1,
24828                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24829           }
24830           case 2 : // Periodic
24831             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24832                                                (int)cimg::mod(c,(double)img._spectrum));
24833           case 1 : // Neumann
24834             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24835                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24836           default : // Dirichlet
24837             if (c<0 || c>=img._spectrum) return (T)0;
24838             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24839           }
24840         default : // Nearest neighbor interpolation
24841           switch (boundary_conditions) {
24842           case 3 : { // Mirror
24843             const int
24844               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24845               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24846               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24847             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24848                                my<img.height()?my:h2 - my - 1,
24849                                mz<img.depth()?mz:d2 - mz - 1,
24850                                mc<img.spectrum()?mc:s2 - mc - 1);
24851           }
24852           case 2 : // Periodic
24853             return (double)img((int)cimg::mod(x,(double)img._width),
24854                                (int)cimg::mod(y,(double)img._height),
24855                                (int)cimg::mod(z,(double)img._depth),
24856                                (int)cimg::mod(c,(double)img._spectrum));
24857           case 1 : // Neumann
24858             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24859           default : // Dirichlet
24860             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24861           }
24862         }
24863       }
24864 
24865       static double mp_list_joff(_cimg_math_parser& mp) {
24866         const unsigned int
24867           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24868           boundary_conditions = (unsigned int)_mp_arg(4);
24869         const int
24870           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24871           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24872         const CImg<T> &img = mp.listin[ind];
24873         const longT
24874           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
24875           whds = (longT)img.size();
24876         if (off>=0 && off<whds) return (double)img[off];
24877         if (img._data) switch (boundary_conditions) {
24878           case 3 : { // Mirror
24879             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24880             return (double)img[moff<whds?moff:whds2 - moff - 1];
24881           }
24882           case 2 : // Periodic
24883             return (double)img[cimg::mod(off,whds)];
24884           case 1 : // Neumann
24885             return (double)img[off<0?0:whds - 1];
24886           default : // Dirichlet
24887             return 0;
24888           }
24889         return 0;
24890       }
24891 
24892       static double mp_list_jxyzc(_cimg_math_parser& mp) {
24893         const unsigned int
24894           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24895           interpolation = (unsigned int)_mp_arg(7),
24896           boundary_conditions = (unsigned int)_mp_arg(8);
24897         const CImg<T> &img = mp.listin[ind];
24898         const double
24899           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24900           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
24901           x = ox + _mp_arg(3), y = oy + _mp_arg(4),
24902           z = oz + _mp_arg(5), c = oc + _mp_arg(6);
24903         switch (interpolation) {
24904         case 2 : // Cubic interpolation
24905           switch (boundary_conditions) {
24906           case 3 : { // Mirror
24907             const float
24908               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24909               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24910               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24911             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24912                                             my<img.height()?my:h2 - my - 1,
24913                                             mz<img.depth()?mz:d2 - mz - 1,
24914                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24915           }
24916           case 2 : // Periodic
24917             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24918                                               (int)cimg::mod(c,(double)img._spectrum));
24919           case 1 : // Neumann
24920             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24921                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24922           default : // Dirichlet
24923             if (c<0 || c>=img._spectrum) return (T)0;
24924             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24925           }
24926         case 1 : // Linear interpolation
24927           switch (boundary_conditions) {
24928           case 3 : { // Mirror
24929             const float
24930               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24931               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24932               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24933             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24934                                              my<img.height()?my:h2 - my - 1,
24935                                              mz<img.depth()?mz:d2 - mz - 1,
24936                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24937           }
24938           case 2 : // Periodic
24939             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24940                                                (int)cimg::mod(c,(double)img._spectrum));
24941           case 1 : // Neumann
24942             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24943                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24944           default : // Dirichlet
24945             if (c<0 || c>=img._spectrum) return (T)0;
24946             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24947           }
24948         default : // Nearest neighbor interpolation
24949           switch (boundary_conditions) {
24950           case 3 : { // Mirror
24951             const int
24952               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24953               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24954               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24955             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24956                                my<img.height()?my:h2 - my - 1,
24957                                mz<img.depth()?mz:d2 - mz - 1,
24958                                mc<img.spectrum()?mc:s2 - mc - 1);
24959           }
24960           case 2 : // Periodic
24961             return (double)img((int)cimg::mod(x,(double)img._width),
24962                                (int)cimg::mod(y,(double)img._height),
24963                                (int)cimg::mod(z,(double)img._depth),
24964                                (int)cimg::mod(c,(double)img._spectrum));
24965           case 1 : // Neumann
24966             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24967           default : // Dirichlet
24968             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24969           }
24970         }
24971       }
24972 
24973       static double mp_list_l(_cimg_math_parser& mp) {
24974         return (double)mp.listout.width();
24975       }
24976 
24977       static double mp_list_median(_cimg_math_parser& mp) {
24978         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24979         if (!mp.list_median) mp.list_median.assign(mp.listin._width);
24980         if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
24981         return *mp.list_median[ind];
24982       }
24983 
24984       static double mp_list_norm(_cimg_math_parser& mp) {
24985         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24986         if (!mp.list_norm) mp.list_norm.assign(mp.listin._width);
24987         if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]);
24988         return *mp.list_norm[ind];
24989       }
24990 
24991       static double mp_list_set_ioff(_cimg_math_parser& mp) {
24992         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24993         CImg<T> &img = mp.listout[ind];
24994         const longT
24995           off = (longT)_mp_arg(3),
24996           whds = (longT)img.size();
24997         const double val = _mp_arg(1);
24998         if (off>=0 && off<whds) img[off] = (T)val;
24999         return val;
25000       }
25001 
25002       static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
25003         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25004         CImg<T> &img = mp.listout[ind];
25005         const int
25006           x = (int)_mp_arg(3), y = (int)_mp_arg(4),
25007           z = (int)_mp_arg(5), c = (int)_mp_arg(6);
25008         const double val = _mp_arg(1);
25009         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25010             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25011           img(x,y,z,c) = (T)val;
25012         return val;
25013       }
25014 
25015       static double mp_list_set_joff(_cimg_math_parser& mp) {
25016         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25017         CImg<T> &img = mp.listout[ind];
25018         const int
25019           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25020           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25021         const longT
25022           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25023           whds = (longT)img.size();
25024         const double val = _mp_arg(1);
25025         if (off>=0 && off<whds) img[off] = (T)val;
25026         return val;
25027       }
25028 
25029       static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
25030         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25031         CImg<T> &img = mp.listout[ind];
25032         const double
25033           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
25034           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
25035         const int
25036           x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
25037           z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
25038         const double val = _mp_arg(1);
25039         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25040             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25041           img(x,y,z,c) = (T)val;
25042         return val;
25043       }
25044 
25045       static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
25046         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25047         CImg<T> &img = mp.listout[ind];
25048         const longT
25049           off = (longT)_mp_arg(3),
25050           whd = (longT)img.width()*img.height()*img.depth();
25051         const T val = (T)_mp_arg(1);
25052         if (off>=0 && off<whd) {
25053           T *ptrd = &img[off];
25054           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25055         }
25056         return _mp_arg(1);
25057       }
25058 
25059       static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
25060         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25061         CImg<T> &img = mp.listout[ind];
25062         const longT
25063           off = (longT)_mp_arg(3),
25064           whd = (longT)img.width()*img.height()*img.depth();
25065         const double *ptrs = &_mp_arg(1) + 1;
25066         if (off>=0 && off<whd) {
25067           const unsigned int vsiz = (unsigned int)mp.opcode[4];
25068           T *ptrd = &img[off];
25069           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25070         }
25071         return cimg::type<double>::nan();
25072       }
25073 
25074       static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
25075         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25076         CImg<T> &img = mp.listout[ind];
25077         const int
25078           x = (int)_mp_arg(3),
25079           y = (int)_mp_arg(4),
25080           z = (int)_mp_arg(5);
25081         const T val = (T)_mp_arg(1);
25082         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25083           T *ptrd = &img(x,y,z);
25084           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25085           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25086         }
25087         return _mp_arg(1);
25088       }
25089 
25090       static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
25091         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25092         CImg<T> &img = mp.listout[ind];
25093         const int
25094           x = (int)_mp_arg(3),
25095           y = (int)_mp_arg(4),
25096           z = (int)_mp_arg(5);
25097         const double *ptrs = &_mp_arg(1) + 1;
25098         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25099           const unsigned int vsiz = (unsigned int)mp.opcode[6];
25100           T *ptrd = &img(x,y,z);
25101           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25102           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25103         }
25104         return cimg::type<double>::nan();
25105       }
25106 
25107       static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
25108         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25109         CImg<T> &img = mp.listout[ind];
25110         const int
25111           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25112           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25113         const longT
25114           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25115           whd = (longT)img.width()*img.height()*img.depth();
25116         const T val = (T)_mp_arg(1);
25117         if (off>=0 && off<whd) {
25118           T *ptrd = &img[off];
25119           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25120         }
25121         return _mp_arg(1);
25122       }
25123 
25124       static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
25125         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25126         CImg<T> &img = mp.listout[ind];
25127         const int
25128           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25129           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25130         const longT
25131           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25132           whd = (longT)img.width()*img.height()*img.depth();
25133         const double *ptrs = &_mp_arg(1) + 1;
25134         if (off>=0 && off<whd) {
25135           const unsigned int vsiz = (unsigned int)mp.opcode[4];
25136           T *ptrd = &img[off];
25137           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25138         }
25139         return cimg::type<double>::nan();
25140       }
25141 
25142       static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
25143         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25144         CImg<T> &img = mp.listout[ind];
25145         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25146         const int
25147           x = (int)(ox + _mp_arg(3)),
25148           y = (int)(oy + _mp_arg(4)),
25149           z = (int)(oz + _mp_arg(5));
25150         const T val = (T)_mp_arg(1);
25151         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25152           T *ptrd = &img(x,y,z);
25153           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25154           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25155         }
25156         return _mp_arg(1);
25157       }
25158 
25159       static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
25160         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25161         CImg<T> &img = mp.listout[ind];
25162         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25163         const int
25164           x = (int)(ox + _mp_arg(3)),
25165           y = (int)(oy + _mp_arg(4)),
25166           z = (int)(oz + _mp_arg(5));
25167         const double *ptrs = &_mp_arg(1) + 1;
25168         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25169           const unsigned int vsiz = (unsigned int)mp.opcode[6];
25170           T *ptrd = &img(x,y,z);
25171           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25172           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25173         }
25174         return cimg::type<double>::nan();
25175       }
25176 
25177       static double mp_list_spectrum(_cimg_math_parser& mp) {
25178         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25179         return (double)mp.listin[ind]._spectrum;
25180       }
25181 
25182       static double mp_list_stats(_cimg_math_parser& mp) {
25183         const unsigned int
25184           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25185           k = (unsigned int)mp.opcode[3];
25186         if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
25187         if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
25188         return mp.list_stats(ind,k);
25189       }
25190 
25191       static double mp_list_wh(_cimg_math_parser& mp) {
25192         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25193         return (double)mp.listin[ind]._width*mp.listin[ind]._height;
25194       }
25195 
25196       static double mp_list_whd(_cimg_math_parser& mp) {
25197         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25198         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
25199       }
25200 
25201       static double mp_list_whds(_cimg_math_parser& mp) {
25202         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25203         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
25204       }
25205 
25206       static double mp_list_width(_cimg_math_parser& mp) {
25207         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25208         return (double)mp.listin[ind]._width;
25209       }
25210 
25211       static double mp_list_Ioff(_cimg_math_parser& mp) {
25212         double *ptrd = &_mp_arg(1) + 1;
25213         const unsigned int
25214           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25215           boundary_conditions = (unsigned int)_mp_arg(4),
25216           vsiz = (unsigned int)mp.opcode[5];
25217         const CImg<T> &img = mp.listin[ind];
25218         const longT
25219           off = (longT)_mp_arg(3),
25220           whd = (longT)img.width()*img.height()*img.depth();
25221         const T *ptrs;
25222         if (off>=0 && off<whd) {
25223           ptrs = &img[off];
25224           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25225           return cimg::type<double>::nan();
25226         }
25227         if (img._data) switch (boundary_conditions) {
25228           case 3 : { // Mirror
25229             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
25230             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
25231             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25232             return cimg::type<double>::nan();
25233           }
25234           case 2 : // Periodic
25235             ptrs = &img[cimg::mod(off,whd)];
25236             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25237             return cimg::type<double>::nan();
25238           case 1 : // Neumann
25239             ptrs = off<0?&img[0]:&img[whd - 1];
25240             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25241             return cimg::type<double>::nan();
25242           default : // Dirichlet
25243             std::memset(ptrd,0,vsiz*sizeof(double));
25244             return cimg::type<double>::nan();
25245           }
25246         std::memset(ptrd,0,vsiz*sizeof(double));
25247         return cimg::type<double>::nan();
25248       }
25249 
25250       static double mp_list_Ixyz(_cimg_math_parser& mp) {
25251         double *ptrd = &_mp_arg(1) + 1;
25252         const unsigned int
25253           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25254           interpolation = (unsigned int)_mp_arg(6),
25255           boundary_conditions = (unsigned int)_mp_arg(7),
25256           vsiz = (unsigned int)mp.opcode[8];
25257         const CImg<T> &img = mp.listin[ind];
25258         const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
25259         const ulongT whd = (ulongT)img._width*img._height*img._depth;
25260         const T *ptrs;
25261         switch (interpolation) {
25262         case 2 : // Cubic interpolation
25263           switch (boundary_conditions) {
25264           case 3 : { // Mirror
25265             const float
25266               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25267               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25268               cx = mx<img.width()?mx:w2 - mx - 1,
25269               cy = my<img.height()?my:h2 - my - 1,
25270               cz = mz<img.depth()?mz:d2 - mz - 1;
25271             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
25272           } break;
25273           case 2 : // Periodic
25274             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
25275             break;
25276           case 1 : // Neumann
25277             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
25278             break;
25279           default : // Dirichlet
25280             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25281           } break;
25282         case 1 : // Linear interpolation
25283           switch (boundary_conditions) {
25284           case 3 : { // Mirror
25285             const float
25286               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25287               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25288               cx = mx<img.width()?mx:w2 - mx - 1,
25289               cy = my<img.height()?my:h2 - my - 1,
25290               cz = mz<img.depth()?mz:d2 - mz - 1;
25291             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
25292           } break;
25293           case 2 : // Periodic
25294             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
25295             break;
25296           case 1 : // Neumann
25297             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
25298             break;
25299           default : // Dirichlet
25300             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25301           } break;
25302         default : // Nearest neighbor interpolation
25303           switch (boundary_conditions) {
25304           case 3 : { // Mirror
25305             const int
25306               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
25307               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
25308               cx = mx<img.width()?mx:w2 - mx - 1,
25309               cy = my<img.height()?my:h2 - my - 1,
25310               cz = mz<img.depth()?mz:d2 - mz - 1;
25311             ptrs = &img(cx,cy,cz);
25312             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25313           } break;
25314           case 2 : { // Periodic
25315             const int
25316               cx = (int)cimg::mod(x,(double)img._width),
25317               cy = (int)cimg::mod(y,(double)img._height),
25318               cz = (int)cimg::mod(z,(double)img._depth);
25319             ptrs = &img(cx,cy,cz);
25320             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25321           } break;
25322           case 1 : { // Neumann
25323             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25324             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25325           } break;
25326           default : // Dirichlet
25327             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25328               ptrs = &img((int)x,(int)y,(int)z);
25329               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25330             } else std::memset(ptrd,0,vsiz*sizeof(double));
25331           }
25332         }
25333         return cimg::type<double>::nan();
25334       }
25335 
25336       static double mp_list_Joff(_cimg_math_parser& mp) {
25337         double *ptrd = &_mp_arg(1) + 1;
25338         const unsigned int
25339           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25340           boundary_conditions = (unsigned int)_mp_arg(4),
25341           vsiz = (unsigned int)mp.opcode[5];
25342         const int
25343           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
25344         const CImg<T> &img = mp.listin[ind];
25345         const longT
25346           off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
25347           whd = (longT)img.width()*img.height()*img.depth();
25348         const T *ptrs;
25349         if (off>=0 && off<whd) {
25350           ptrs = &img[off];
25351           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25352           return cimg::type<double>::nan();
25353         }
25354         if (img._data) switch (boundary_conditions) {
25355           case 3 : { // Mirror
25356             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
25357             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
25358             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25359             return cimg::type<double>::nan();
25360           }
25361           case 2 : // Periodic
25362             ptrs = &img[cimg::mod(off,whd)];
25363             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25364             return cimg::type<double>::nan();
25365           case 1 : // Neumann
25366             ptrs = off<0?&img[0]:&img[whd - 1];
25367             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25368             return cimg::type<double>::nan();
25369           default : // Dirichlet
25370             std::memset(ptrd,0,vsiz*sizeof(double));
25371             return cimg::type<double>::nan();
25372           }
25373         std::memset(ptrd,0,vsiz*sizeof(double));
25374         return cimg::type<double>::nan();
25375       }
25376 
25377       static double mp_list_Jxyz(_cimg_math_parser& mp) {
25378         double *ptrd = &_mp_arg(1) + 1;
25379         const unsigned int
25380           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25381           interpolation = (unsigned int)_mp_arg(6),
25382           boundary_conditions = (unsigned int)_mp_arg(7),
25383           vsiz = (unsigned int)mp.opcode[8];
25384         const CImg<T> &img = mp.listin[ind];
25385         const double
25386           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
25387           x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
25388         const ulongT whd = (ulongT)img._width*img._height*img._depth;
25389         const T *ptrs;
25390         switch (interpolation) {
25391         case 2 : // Cubic interpolation
25392           switch (boundary_conditions) {
25393           case 3 : { // Mirror
25394             const float
25395               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25396               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25397               cx = mx<img.width()?mx:w2 - mx - 1,
25398               cy = my<img.height()?my:h2 - my - 1,
25399               cz = mz<img.depth()?mz:d2 - mz - 1;
25400             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
25401           } break;
25402           case 2 : // Periodic
25403             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
25404             break;
25405           case 1 : // Neumann
25406             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
25407             break;
25408           default : // Dirichlet
25409             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25410           } break;
25411         case 1 : // Linear interpolation
25412           switch (boundary_conditions) {
25413           case 3 : { // Mirror
25414             const float
25415               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25416               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25417               cx = mx<img.width()?mx:w2 - mx - 1,
25418               cy = my<img.height()?my:h2 - my - 1,
25419               cz = mz<img.depth()?mz:d2 - mz - 1;
25420             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
25421           } break;
25422           case 2 : // Periodic
25423             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
25424             break;
25425           case 1 : // Neumann
25426             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
25427             break;
25428           default : // Dirichlet
25429             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25430           } break;
25431         case 0 : // Nearest neighbor interpolation
25432           switch (boundary_conditions) {
25433           case 3 : { // Mirror
25434             const int
25435               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
25436               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
25437               cx = mx<img.width()?mx:w2 - mx - 1,
25438               cy = my<img.height()?my:h2 - my - 1,
25439               cz = mz<img.depth()?mz:d2 - mz - 1;
25440             ptrs = &img(cx,cy,cz);
25441             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25442           } break;
25443           case 2 : { // Periodic
25444             const int
25445               cx = (int)cimg::mod(x,(double)img._width),
25446               cy = (int)cimg::mod(y,(double)img._height),
25447               cz = (int)cimg::mod(z,(double)img._depth);
25448             ptrs = &img(cx,cy,cz);
25449             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25450           } break;
25451           case 1 : { // Neumann
25452             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25453             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25454           } break;
25455           default : // Dirichlet
25456             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25457               ptrs = &img((int)x,(int)y,(int)z);
25458               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25459             } else std::memset(ptrd,0,vsiz*sizeof(double));
25460           }
25461         }
25462         return cimg::type<double>::nan();
25463       }
25464 
25465       static double mp_log(_cimg_math_parser& mp) {
25466         return std::log(_mp_arg(2));
25467       }
25468 
25469       static double mp_log10(_cimg_math_parser& mp) {
25470         return std::log10(_mp_arg(2));
25471       }
25472 
25473       static double mp_log2(_cimg_math_parser& mp) {
25474         return cimg::log2(_mp_arg(2));
25475       }
25476 
25477       static double mp_logical_and(_cimg_math_parser& mp) {
25478         const bool val_left = (bool)_mp_arg(2);
25479         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25480         if (!val_left) { mp.p_code = p_end - 1; return 0; }
25481         const ulongT mem_right = mp.opcode[3];
25482         for ( ; mp.p_code<p_end; ++mp.p_code) {
25483           mp.opcode._data = mp.p_code->_data;
25484           const ulongT target = mp.opcode[1];
25485           mp.mem[target] = _cimg_mp_defunc(mp);
25486         }
25487         --mp.p_code;
25488         return (double)(bool)mp.mem[mem_right];
25489       }
25490 
25491       static double mp_logical_not(_cimg_math_parser& mp) {
25492         return (double)!_mp_arg(2);
25493       }
25494 
25495       static double mp_logical_or(_cimg_math_parser& mp) {
25496         const bool val_left = (bool)_mp_arg(2);
25497         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25498         if (val_left) { mp.p_code = p_end - 1; return 1; }
25499         const ulongT mem_right = mp.opcode[3];
25500         for ( ; mp.p_code<p_end; ++mp.p_code) {
25501           mp.opcode._data = mp.p_code->_data;
25502           const ulongT target = mp.opcode[1];
25503           mp.mem[target] = _cimg_mp_defunc(mp);
25504         }
25505         --mp.p_code;
25506         return (double)(bool)mp.mem[mem_right];
25507       }
25508 
25509       static double mp_lowercase(_cimg_math_parser& mp) {
25510         return cimg::lowercase(_mp_arg(2));
25511       }
25512 
25513       static double mp_lt(_cimg_math_parser& mp) {
25514         return (double)(_mp_arg(2)<_mp_arg(3));
25515       }
25516 
25517       static double mp_lte(_cimg_math_parser& mp) {
25518         return (double)(_mp_arg(2)<=_mp_arg(3));
25519       }
25520 
25521       static double mp_matrix_eig(_cimg_math_parser& mp) {
25522         double *ptrd = &_mp_arg(1) + 1;
25523         const double *ptr1 = &_mp_arg(2) + 1;
25524         const unsigned int k = (unsigned int)mp.opcode[3];
25525         CImg<doubleT> val, vec;
25526         CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
25527         CImg<doubleT>(ptrd,1,k,1,1,true) = val;
25528         CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
25529         return cimg::type<double>::nan();
25530       }
25531 
25532       static double mp_matrix_invert(_cimg_math_parser& mp) {
25533         double *const ptrd = &_mp_arg(1) + 1;
25534         const double *const ptr1 = &_mp_arg(2) + 1;
25535         const unsigned int k = (unsigned int)mp.opcode[3];
25536         const bool use_LU = (bool)_mp_arg(4);
25537         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert(use_LU);
25538         return cimg::type<double>::nan();
25539       }
25540 
25541       static double mp_matrix_mul(_cimg_math_parser& mp) {
25542         double *ptrd = &_mp_arg(1) + 1;
25543         const double
25544           *ptr1 = &_mp_arg(2) + 1,
25545           *ptr2 = &_mp_arg(3) + 1;
25546         const unsigned int
25547           k = (unsigned int)mp.opcode[4],
25548           l = (unsigned int)mp.opcode[5],
25549           m = (unsigned int)mp.opcode[6];
25550         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
25551         return cimg::type<double>::nan();
25552       }
25553 
25554       static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) {
25555         double *ptrd = &_mp_arg(1) + 1;
25556         const double *ptr1 = &_mp_arg(2) + 1;
25557         const unsigned int
25558           k = (unsigned int)mp.opcode[3],
25559           l = (unsigned int)mp.opcode[4];
25560         const bool use_LU = (bool)_mp_arg(5);
25561         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU);
25562         return cimg::type<double>::nan();
25563       }
25564 
25565       static double mp_matrix_svd(_cimg_math_parser& mp) {
25566         double *ptrd = &_mp_arg(1) + 1;
25567         const double *ptr1 = &_mp_arg(2) + 1;
25568         const unsigned int
25569           k = (unsigned int)mp.opcode[3],
25570           l = (unsigned int)mp.opcode[4];
25571         CImg<doubleT> U, S, V;
25572         CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
25573         CImg<doubleT>(ptrd,k,l,1,1,true) = U;
25574         CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
25575         CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
25576         return cimg::type<double>::nan();
25577       }
25578 
25579       static double mp_max(_cimg_math_parser& mp) {
25580         const unsigned int i_end = (unsigned int)mp.opcode[2];
25581         double val = _mp_arg(3);
25582         for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
25583         return val;
25584       }
25585 
25586       static double mp_maxabs(_cimg_math_parser& mp) {
25587         const unsigned int i_end = (unsigned int)mp.opcode[2];
25588         double val = _mp_arg(3), absval = cimg::abs(val);
25589         for (unsigned int i = 4; i<i_end; ++i) {
25590           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
25591           if (_absval>absval) { val = _val; absval = _absval; }
25592         }
25593         return val;
25594       }
25595 
25596       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
25597                                         const longT siz, const long inc) {
25598         const longT
25599           off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
25600           eoff = off + (siz - 1)*inc;
25601         if (off<0 || eoff>=mp.mem.width())
25602           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25603                                       "Out-of-bounds variable pointer "
25604                                       "(length: %ld, increment: %ld, offset start: %ld, "
25605                                       "offset end: %ld, offset max: %u).",
25606                                       mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
25607         return &mp.mem[off];
25608       }
25609 
25610       static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
25611                                       const longT siz, const long inc, const bool is_out) {
25612         const unsigned ind = (unsigned int)p_ref[1];
25613         const CImg<T> &img = is_out?
25614           (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]):
25615           (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]);
25616         const bool is_relative = (bool)p_ref[2];
25617         int ox, oy, oz, oc;
25618         longT off = 0;
25619         if (is_relative) {
25620           ox = (int)mp.mem[_cimg_mp_slot_x];
25621           oy = (int)mp.mem[_cimg_mp_slot_y];
25622           oz = (int)mp.mem[_cimg_mp_slot_z];
25623           oc = (int)mp.mem[_cimg_mp_slot_c];
25624           off = img.offset(ox,oy,oz,oc);
25625         }
25626         if ((*p_ref)%2) {
25627           const int
25628             x = (int)mp.mem[p_ref[3]],
25629             y = (int)mp.mem[p_ref[4]],
25630             z = (int)mp.mem[p_ref[5]],
25631             c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
25632           off+=img.offset(x,y,z,c);
25633         } else off+=(longT)mp.mem[p_ref[3]];
25634         const longT eoff = off + (siz - 1)*inc;
25635         if (off<0 || eoff>=(longT)img.size())
25636           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25637                                       "Out-of-bounds image pointer "
25638                                       "(length: %ld, increment: %ld, offset start: %ld, "
25639                                       "offset end: %ld, offset max: %lu).",
25640                                       mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
25641         return (float*)&img[off];
25642       }
25643 
25644       static double mp_memcopy(_cimg_math_parser& mp) {
25645         longT siz = (longT)_mp_arg(4);
25646         const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
25647         const float
25648           _opacity = (float)_mp_arg(7),
25649           opacity = (float)cimg::abs(_opacity),
25650           omopacity = 1 - std::max(_opacity,0.f);
25651         if (siz>0) {
25652           const bool
25653             is_doubled = mp.opcode[8]<=1,
25654             is_doubles = mp.opcode[15]<=1;
25655           if (is_doubled && is_doubles) { // (double*) <- (double*)
25656             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25657             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25658             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25659               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
25660               else std::memmove(ptrd,ptrs,siz*sizeof(double));
25661             } else {
25662               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25663                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25664                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25665               } else { // Overlapping buffers
25666                 CImg<doubleT> buf((unsigned int)siz);
25667                 cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
25668                 ptrs = buf;
25669                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25670                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25671               }
25672             }
25673           } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
25674             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25675             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25676             if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25677             else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25678           } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
25679             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25680             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25681             if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25682             else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
25683           } else { // (float*) <- (float*)
25684             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25685             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25686             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25687               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
25688               else std::memmove(ptrd,ptrs,siz*sizeof(float));
25689             } else {
25690               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25691                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25692                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25693               } else { // Overlapping buffers
25694                 CImg<floatT> buf((unsigned int)siz);
25695                 cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
25696                 ptrs = buf;
25697                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25698                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25699               }
25700             }
25701           }
25702         }
25703         return _mp_arg(1);
25704       }
25705 
25706       static double mp_min(_cimg_math_parser& mp) {
25707         const unsigned int i_end = (unsigned int)mp.opcode[2];
25708         double val = _mp_arg(3);
25709         for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
25710         return val;
25711       }
25712 
25713       static double mp_minabs(_cimg_math_parser& mp) {
25714         const unsigned int i_end = (unsigned int)mp.opcode[2];
25715         double val = _mp_arg(3), absval = cimg::abs(val);
25716         for (unsigned int i = 4; i<i_end; ++i) {
25717           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
25718           if (_absval<absval) { val = _val; absval = _absval; }
25719         }
25720         return val;
25721       }
25722 
25723       static double mp_minus(_cimg_math_parser& mp) {
25724         return -_mp_arg(2);
25725       }
25726 
25727       static double mp_median(_cimg_math_parser& mp) {
25728         const unsigned int i_end = (unsigned int)mp.opcode[2];
25729         switch (i_end - 3) {
25730         case 1 : return _mp_arg(3);
25731         case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
25732         case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
25733         case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
25734         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));
25735         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),
25736                                      _mp_arg(10),_mp_arg(11));
25737         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),
25738                                       _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
25739         }
25740         CImg<doubleT> vals(i_end - 3);
25741         double *p = vals.data();
25742         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
25743         return vals.median();
25744       }
25745 
25746       static double mp_modulo(_cimg_math_parser& mp) {
25747         return cimg::mod(_mp_arg(2),_mp_arg(3));
25748       }
25749 
25750       static double mp_mproj(_cimg_math_parser& mp) {
25751         double *ptrd = &_mp_arg(1) + 1;
25752         const double
25753           *ptrS = &_mp_arg(2) + 1,
25754           *ptrD = &_mp_arg(5) + 1;
25755         const unsigned int
25756           wS = (unsigned int)mp.opcode[3],
25757           hS = (unsigned int)mp.opcode[4],
25758           wD = (unsigned int)mp.opcode[6];
25759         const int
25760           method = std::max(0,(int)_mp_arg(7)),
25761           max_iter = std::max(0,(int)_mp_arg(8));
25762         const double
25763           max_residual = std::max(0.,_mp_arg(9));
25764 
25765         CImg<doubleT>(ptrd,wS,wD,1,1,true) = CImg<doubleT>(ptrS,wS,hS,1,1,false).
25766           project_matrix(CImg<doubleT>(ptrD,wD,hS,1,1,true),method,max_iter,max_residual);
25767         return cimg::type<double>::nan();
25768       }
25769 
25770       static double mp_mul(_cimg_math_parser& mp) {
25771         return _mp_arg(2)*_mp_arg(3);
25772       }
25773 
25774       static double mp_mul2(_cimg_math_parser& mp) {
25775         return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
25776       }
25777 
25778       static double mp_neq(_cimg_math_parser& mp) {
25779         return (double)(_mp_arg(2)!=_mp_arg(3));
25780       }
25781 
25782       static double mp_norm0(_cimg_math_parser& mp) {
25783         const unsigned int i_end = (unsigned int)mp.opcode[2];
25784         switch (i_end - 3) {
25785         case 1 : return _mp_arg(3)!=0;
25786         case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
25787         }
25788         double res = 0;
25789         for (unsigned int i = 3; i<i_end; ++i)
25790           res+=_mp_arg(i)==0?0:1;
25791         return res;
25792       }
25793 
25794       static double mp_norm1(_cimg_math_parser& mp) {
25795         const unsigned int i_end = (unsigned int)mp.opcode[2];
25796         switch (i_end - 3) {
25797         case 1 : return cimg::abs(_mp_arg(3));
25798         case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
25799         }
25800         double res = 0;
25801         for (unsigned int i = 3; i<i_end; ++i)
25802           res+=cimg::abs(_mp_arg(i));
25803         return res;
25804       }
25805 
25806       static double mp_norm2(_cimg_math_parser& mp) {
25807         const unsigned int i_end = (unsigned int)mp.opcode[2];
25808         switch (i_end - 3) {
25809         case 1 : return cimg::abs(_mp_arg(3));
25810         case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
25811         }
25812         double res = 0;
25813         for (unsigned int i = 3; i<i_end; ++i)
25814           res+=cimg::sqr(_mp_arg(i));
25815         return std::sqrt(res);
25816       }
25817 
25818       static double mp_norminf(_cimg_math_parser& mp) {
25819         const unsigned int i_end = (unsigned int)mp.opcode[2];
25820         switch (i_end - 3) {
25821         case 1 : return cimg::abs(_mp_arg(3));
25822         case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
25823         }
25824         double res = 0;
25825         for (unsigned int i = 3; i<i_end; ++i) {
25826           const double val = cimg::abs(_mp_arg(i));
25827           if (val>res) res = val;
25828         }
25829         return res;
25830       }
25831 
25832       static double mp_normp(_cimg_math_parser& mp) {
25833         const unsigned int i_end = (unsigned int)mp.opcode[2];
25834         if (i_end==4) return cimg::abs(_mp_arg(3));
25835         const double p = (double)mp.opcode[3];
25836         double res = 0;
25837         for (unsigned int i = 4; i<i_end; ++i)
25838           res+=std::pow(cimg::abs(_mp_arg(i)),p);
25839         res = std::pow(res,1/p);
25840         return res>0?res:0.;
25841       }
25842 
25843       static double mp_permutations(_cimg_math_parser& mp) {
25844         return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
25845       }
25846 
25847       static double mp_polygon(_cimg_math_parser& mp) {
25848         const unsigned int i_end = (unsigned int)mp.opcode[2];
25849         unsigned int ind = (unsigned int)mp.opcode[3];
25850         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
25851         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
25852         bool is_invalid_arguments = i_end<=4, is_outlined = false;
25853         if (!is_invalid_arguments) {
25854           int nbv = (int)_mp_arg(4);
25855           if (!nbv) is_invalid_arguments = true;
25856           else {
25857             if (nbv<0) { nbv = -nbv; is_outlined = true; }
25858             CImg<intT> points(nbv,2,1,1,0);
25859             CImg<T> color(img._spectrum,1,1,1,0);
25860             float opacity = 1;
25861             unsigned int i = 5, pattern=~0U;
25862             cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
25863             else { is_invalid_arguments = true; break; }
25864             if (!is_invalid_arguments) {
25865               if (i<i_end) opacity = (float)_mp_arg(i++);
25866               if (is_outlined && i<i_end) pattern = (unsigned int)_mp_arg(i++);
25867               cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
25868               else { color.resize(k,1,1,1,-1); break; }
25869               color.resize(img._spectrum,1,1,1,0,2);
25870               if (is_outlined) img.draw_polygon(points,color._data,opacity,pattern);
25871               else img.draw_polygon(points,color._data,opacity);
25872             }
25873           }
25874         }
25875         if (is_invalid_arguments) {
25876           CImg<doubleT> args(i_end - 4);
25877           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
25878           if (ind==~0U)
25879             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
25880                                         "Invalid arguments '%s'. ",
25881                                         mp.imgin.pixel_type(),args.value_string()._data);
25882           else
25883             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
25884                                         "Invalid arguments '#%u%s%s'. ",
25885                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
25886         }
25887         return cimg::type<double>::nan();
25888       }
25889 
25890       static double mp_pow(_cimg_math_parser& mp) {
25891         const double v = _mp_arg(2), p = _mp_arg(3);
25892         return std::pow(v,p);
25893       }
25894 
25895       static double mp_pow0_25(_cimg_math_parser& mp) {
25896         const double val = _mp_arg(2);
25897         return std::sqrt(std::sqrt(val));
25898       }
25899 
25900       static double mp_pow3(_cimg_math_parser& mp) {
25901         const double val = _mp_arg(2);
25902         return val*val*val;
25903       }
25904 
25905       static double mp_pow4(_cimg_math_parser& mp) {
25906         const double val = _mp_arg(2);
25907         return val*val*val*val;
25908       }
25909 
25910       static double mp_print(_cimg_math_parser& mp) {
25911           const double val = _mp_arg(1);
25912           const bool print_char = (bool)mp.opcode[3];
25913           cimg_pragma_openmp(critical(mp_print))
25914           {
25915             CImg<charT> _expr(mp.opcode[2] - 4);
25916             const ulongT *ptrs = mp.opcode._data + 4;
25917             cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
25918             cimg::strellipsize(_expr);
25919             cimg::mutex(6);
25920             if (print_char)
25921               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'",
25922                            _expr._data,val,(int)val);
25923             else
25924               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g",
25925                            _expr._data,val);
25926             std::fflush(cimg::output());
25927             cimg::mutex(6,0);
25928           }
25929           return val;
25930       }
25931 
25932       static double mp_prod(_cimg_math_parser& mp) {
25933         const unsigned int i_end = (unsigned int)mp.opcode[2];
25934         double val = _mp_arg(3);
25935         for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
25936         return val;
25937       }
25938 
25939       static double mp_rad2deg(_cimg_math_parser& mp) {
25940         return _mp_arg(2)*180/cimg::PI;
25941       }
25942 
25943       static double mp_repeat(_cimg_math_parser& mp) {
25944         const double nb_it = _mp_arg(2);
25945         double
25946           *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
25947           *const ptrs = &_mp_arg(1);
25948         const CImg<ulongT>
25949           *const p_body = ++mp.p_code,
25950           *const p_end = p_body + mp.opcode[4];
25951 
25952         if (nb_it>0) {
25953           const unsigned int _break_type = mp.break_type;
25954           mp.break_type = 0;
25955 
25956           double it = 0;
25957           if (ptrc) { // Version with loop variable (3 arguments)
25958             while (it<nb_it) {
25959               *ptrc = it;
25960               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
25961                 mp.opcode._data = mp.p_code->_data;
25962                 const ulongT target = mp.opcode[1];
25963                 mp.mem[target] = _cimg_mp_defunc(mp);
25964               }
25965               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
25966               ++it;
25967             }
25968             *ptrc = it;
25969           } else // Version without loop variable (2 arguments)
25970             while (it<nb_it) {
25971               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
25972                 mp.opcode._data = mp.p_code->_data;
25973                 const ulongT target = mp.opcode[1];
25974                 mp.mem[target] = _cimg_mp_defunc(mp);
25975               }
25976               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
25977               ++it;
25978             }
25979           mp.break_type = _break_type;
25980         }
25981 
25982         mp.p_code = p_end - 1;
25983         return *ptrs;
25984       }
25985 
25986       static double mp_rol(_cimg_math_parser& mp) {
25987         return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
25988       }
25989 
25990       static double mp_ror(_cimg_math_parser& mp) {
25991         return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
25992       }
25993 
25994       static double mp_rot2d(_cimg_math_parser& mp) {
25995         double *ptrd = &_mp_arg(1) + 1;
25996         const float
25997           theta = (float)_mp_arg(2),
25998           ca = std::cos(theta),
25999           sa = std::sin(theta);
26000         *(ptrd++) = ca;
26001         *(ptrd++) = -sa;
26002         *(ptrd++) = sa;
26003         *ptrd = ca;
26004         return cimg::type<double>::nan();
26005       }
26006 
26007       static double mp_rot3d(_cimg_math_parser& mp) {
26008         double *ptrd = &_mp_arg(1) + 1;
26009         const float
26010           x = (float)_mp_arg(2),
26011           y = (float)_mp_arg(3),
26012           z = (float)_mp_arg(4),
26013           theta = (float)_mp_arg(5);
26014         CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta*180/cimg::PI);
26015         return cimg::type<double>::nan();
26016       }
26017 
26018       static double mp_round(_cimg_math_parser& mp) {
26019         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
26020       }
26021 
26022       static double mp_self_add(_cimg_math_parser& mp) {
26023         return _mp_arg(1)+=_mp_arg(2);
26024       }
26025 
26026       static double mp_self_bitwise_and(_cimg_math_parser& mp) {
26027         double &val = _mp_arg(1);
26028         return val = (double)((longT)val & (longT)_mp_arg(2));
26029       }
26030 
26031       static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
26032         double &val = _mp_arg(1);
26033         return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
26034       }
26035 
26036       static double mp_self_bitwise_or(_cimg_math_parser& mp) {
26037         double &val = _mp_arg(1);
26038         return val = (double)((longT)val | (longT)_mp_arg(2));
26039       }
26040 
26041       static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
26042         double &val = _mp_arg(1);
26043         return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
26044       }
26045 
26046       static double mp_self_decrement(_cimg_math_parser& mp) {
26047         return --_mp_arg(1);
26048       }
26049 
26050       static double mp_self_increment(_cimg_math_parser& mp) {
26051         return ++_mp_arg(1);
26052       }
26053 
26054       static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
26055         unsigned int
26056           ptrd = (unsigned int)mp.opcode[1] + 1,
26057           siz = (unsigned int)mp.opcode[2];
26058         mp_func op = (mp_func)mp.opcode[3];
26059         CImg<ulongT> l_opcode(1,3);
26060         l_opcode[2] = mp.opcode[4]; // Scalar argument
26061         l_opcode.swap(mp.opcode);
26062         ulongT &target = mp.opcode[1];
26063         while (siz-->0) { target = ptrd++; (*op)(mp); }
26064         l_opcode.swap(mp.opcode);
26065         return cimg::type<double>::nan();
26066       }
26067 
26068       static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
26069         unsigned int
26070           ptrd = (unsigned int)mp.opcode[1] + 1,
26071           siz = (unsigned int)mp.opcode[2],
26072           ptrs = (unsigned int)mp.opcode[4] + 1;
26073         mp_func op = (mp_func)mp.opcode[3];
26074         CImg<ulongT> l_opcode(1,4);
26075         l_opcode.swap(mp.opcode);
26076         ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
26077         while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
26078         l_opcode.swap(mp.opcode);
26079         return cimg::type<double>::nan();
26080       }
26081 
26082       static double mp_self_mul(_cimg_math_parser& mp) {
26083         return _mp_arg(1)*=_mp_arg(2);
26084       }
26085 
26086       static double mp_self_div(_cimg_math_parser& mp) {
26087         return _mp_arg(1)/=_mp_arg(2);
26088       }
26089 
26090       static double mp_self_modulo(_cimg_math_parser& mp) {
26091         double &val = _mp_arg(1);
26092         return val = cimg::mod(val,_mp_arg(2));
26093       }
26094 
26095       static double mp_self_pow(_cimg_math_parser& mp) {
26096         double &val = _mp_arg(1);
26097         return val = std::pow(val,_mp_arg(2));
26098       }
26099 
26100       static double mp_self_sub(_cimg_math_parser& mp) {
26101         return _mp_arg(1)-=_mp_arg(2);
26102       }
26103 
26104 #ifdef cimg_mp_func_set
26105       static double mp_set(_cimg_math_parser& mp) {
26106         const double *ptrs = &_mp_arg(1);
26107         double *ptrd = &_mp_arg(3) + 1;
26108         const unsigned int
26109           sizs = (unsigned int)mp.opcode[2],
26110           sizd = (unsigned int)mp.opcode[4];
26111         CImg<charT> sd(sizd + 1);
26112         cimg_for_inX(sd,0,sd.width() - 1,i) sd[i] = (char)ptrd[i];
26113         sd.back() = 0;
26114         if (sizs) cimg_mp_func_set(ptrs + 1,sizs,sd._data);
26115         else cimg_mp_func_set(ptrs,0,sd._data);
26116         return *ptrs;
26117       }
26118 #endif
26119 
26120       static double mp_set_ioff(_cimg_math_parser& mp) {
26121         CImg<T> &img = mp.imgout;
26122         const longT
26123           off = (longT)_mp_arg(2),
26124           whds = (longT)img.size();
26125         const double val = _mp_arg(1);
26126         if (off>=0 && off<whds) img[off] = (T)val;
26127         return val;
26128       }
26129 
26130       static double mp_set_ixyzc(_cimg_math_parser& mp) {
26131         CImg<T> &img = mp.imgout;
26132         const int
26133           x = (int)_mp_arg(2), y = (int)_mp_arg(3),
26134           z = (int)_mp_arg(4), c = (int)_mp_arg(5);
26135         const double val = _mp_arg(1);
26136         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
26137             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
26138           img(x,y,z,c) = (T)val;
26139         return val;
26140       }
26141 
26142       static double mp_set_joff(_cimg_math_parser& mp) {
26143         CImg<T> &img = mp.imgout;
26144         const int
26145           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26146           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26147         const longT
26148           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26149           whds = (longT)img.size();
26150         const double val = _mp_arg(1);
26151         if (off>=0 && off<whds) img[off] = (T)val;
26152         return val;
26153       }
26154 
26155       static double mp_set_jxyzc(_cimg_math_parser& mp) {
26156         CImg<T> &img = mp.imgout;
26157         const double
26158           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
26159           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
26160         const int
26161           x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
26162           z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
26163         const double val = _mp_arg(1);
26164         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
26165             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
26166           img(x,y,z,c) = (T)val;
26167         return val;
26168       }
26169 
26170       static double mp_set_Ioff_s(_cimg_math_parser& mp) {
26171         CImg<T> &img = mp.imgout;
26172         const longT
26173           off = (longT)_mp_arg(2),
26174           whd = (longT)img.width()*img.height()*img.depth();
26175         const T val = (T)_mp_arg(1);
26176         if (off>=0 && off<whd) {
26177           T *ptrd = &img[off];
26178           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26179         }
26180         return _mp_arg(1);
26181       }
26182 
26183       static double mp_set_Ioff_v(_cimg_math_parser& mp) {
26184         CImg<T> &img = mp.imgout;
26185         const longT
26186           off = (longT)_mp_arg(2),
26187           whd = (longT)img.width()*img.height()*img.depth();
26188         const double *ptrs = &_mp_arg(1) + 1;
26189         if (off>=0 && off<whd) {
26190           const unsigned int vsiz = (unsigned int)mp.opcode[3];
26191           T *ptrd = &img[off];
26192           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26193         }
26194         return cimg::type<double>::nan();
26195       }
26196 
26197       static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
26198         CImg<T> &img = mp.imgout;
26199         const int
26200           x = (int)_mp_arg(2),
26201           y = (int)_mp_arg(3),
26202           z = (int)_mp_arg(4);
26203         const T val = (T)_mp_arg(1);
26204         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26205           T *ptrd = &img(x,y,z);
26206           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26207           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26208         }
26209         return _mp_arg(1);
26210       }
26211 
26212       static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
26213         CImg<T> &img = mp.imgout;
26214         const int
26215           x = (int)_mp_arg(2),
26216           y = (int)_mp_arg(3),
26217           z = (int)_mp_arg(4);
26218         const double *ptrs = &_mp_arg(1) + 1;
26219         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26220           const unsigned int vsiz = (unsigned int)mp.opcode[5];
26221           T *ptrd = &img(x,y,z);
26222           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26223           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26224         }
26225         return cimg::type<double>::nan();
26226       }
26227 
26228       static double mp_set_Joff_s(_cimg_math_parser& mp) {
26229         CImg<T> &img = mp.imgout;
26230         const int
26231           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26232           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26233         const longT
26234           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26235           whd = (longT)img.width()*img.height()*img.depth();
26236         const T val = (T)_mp_arg(1);
26237         if (off>=0 && off<whd) {
26238           T *ptrd = &img[off];
26239           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26240         }
26241         return _mp_arg(1);
26242       }
26243 
26244       static double mp_set_Joff_v(_cimg_math_parser& mp) {
26245         CImg<T> &img = mp.imgout;
26246         const int
26247           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26248           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26249         const longT
26250           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26251           whd = (longT)img.width()*img.height()*img.depth();
26252         const double *ptrs = &_mp_arg(1) + 1;
26253         if (off>=0 && off<whd) {
26254           const unsigned int vsiz = (unsigned int)mp.opcode[3];
26255           T *ptrd = &img[off];
26256           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26257         }
26258         return cimg::type<double>::nan();
26259       }
26260 
26261       static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
26262         CImg<T> &img = mp.imgout;
26263         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
26264         const int
26265           x = (int)(ox + _mp_arg(2)),
26266           y = (int)(oy + _mp_arg(3)),
26267           z = (int)(oz + _mp_arg(4));
26268         const T val = (T)_mp_arg(1);
26269         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26270           T *ptrd = &img(x,y,z);
26271           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26272           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26273         }
26274         return _mp_arg(1);
26275       }
26276 
26277       static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
26278         CImg<T> &img = mp.imgout;
26279         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
26280         const int
26281           x = (int)(ox + _mp_arg(2)),
26282           y = (int)(oy + _mp_arg(3)),
26283           z = (int)(oz + _mp_arg(4));
26284         const double *ptrs = &_mp_arg(1) + 1;
26285         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26286           const unsigned int vsiz = (unsigned int)mp.opcode[5];
26287           T *ptrd = &img(x,y,z);
26288           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26289           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26290         }
26291         return cimg::type<double>::nan();
26292       }
26293 
26294       static double mp_shift(_cimg_math_parser& mp) {
26295         double *const ptrd = &_mp_arg(1) + 1;
26296         const double *const ptrs = &_mp_arg(2) + 1;
26297         const unsigned int siz = (unsigned int)mp.opcode[3];
26298         const int
26299           shift = (int)_mp_arg(4),
26300           boundary_conditions = (int)_mp_arg(5);
26301         CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
26302         return cimg::type<double>::nan();
26303       }
26304 
26305       static double mp_sign(_cimg_math_parser& mp) {
26306         return cimg::sign(_mp_arg(2));
26307       }
26308 
26309       static double mp_sin(_cimg_math_parser& mp) {
26310         return std::sin(_mp_arg(2));
26311       }
26312 
26313       static double mp_sinc(_cimg_math_parser& mp) {
26314         return cimg::sinc(_mp_arg(2));
26315       }
26316 
26317       static double mp_sinh(_cimg_math_parser& mp) {
26318         return std::sinh(_mp_arg(2));
26319       }
26320 
26321       static double mp_solve(_cimg_math_parser& mp) {
26322         double *ptrd = &_mp_arg(1) + 1;
26323         const double
26324           *ptr1 = &_mp_arg(2) + 1,
26325           *ptr2 = &_mp_arg(3) + 1;
26326         const unsigned int
26327           k = (unsigned int)mp.opcode[4],
26328           l = (unsigned int)mp.opcode[5],
26329           m = (unsigned int)mp.opcode[6];
26330         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,false).solve(CImg<doubleT>(ptr1,k,l,1,1,true));
26331         return cimg::type<double>::nan();
26332       }
26333 
26334       static double mp_sort(_cimg_math_parser& mp) {
26335         double *const ptrd = &_mp_arg(1) + 1;
26336         const double *const ptrs = &_mp_arg(2) + 1;
26337         const bool is_increasing = (bool)_mp_arg(4);
26338         const unsigned int
26339           siz = (unsigned int)mp.opcode[3],
26340           nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5),
26341           siz_elt = (unsigned int)_mp_arg(6);
26342         const ulongT sn = siz_elt*nb_elts;
26343         if (sn>siz || siz_elt<1)
26344           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': "
26345                                       "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid "
26346                                       "for sorting a vector of size %u.",
26347                                       mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz);
26348         CImg<doubleT>(ptrd,siz_elt,nb_elts,1,1,true) = CImg<doubleT>(ptrs,siz_elt,nb_elts,1,1,true).
26349           get_sort(is_increasing,siz_elt>1?'y':0);
26350         if (sn<siz) CImg<doubleT>(ptrd + sn,siz - sn,1,1,1,true) = CImg<doubleT>(ptrs + sn,siz - sn,1,1,1,true);
26351         return cimg::type<double>::nan();
26352       }
26353 
26354       static double mp_sqr(_cimg_math_parser& mp) {
26355         return cimg::sqr(_mp_arg(2));
26356       }
26357 
26358       static double mp_sqrt(_cimg_math_parser& mp) {
26359         return std::sqrt(_mp_arg(2));
26360       }
26361 
26362       static double mp_srand(_cimg_math_parser& mp) {
26363         mp.rng = (cimg_uint64)_mp_arg(2);
26364         return cimg::type<double>::nan();
26365       }
26366 
26367       static double mp_srand0(_cimg_math_parser& mp) {
26368         cimg::srand(&mp.rng);
26369 
26370 #if cimg_use_openmp!=0
26371         mp.rng+=omp_get_thread_num();
26372 #endif
26373         return cimg::type<double>::nan();
26374       }
26375 
26376       static double mp_std(_cimg_math_parser& mp) {
26377         const unsigned int i_end = (unsigned int)mp.opcode[2];
26378         CImg<doubleT> vals(i_end - 3);
26379         double *p = vals.data();
26380         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26381         return std::sqrt(vals.variance());
26382       }
26383 
26384       static double mp_string_init(_cimg_math_parser& mp) {
26385         const unsigned char *ptrs = (unsigned char*)&mp.opcode[3];
26386         unsigned int
26387           ptrd = (unsigned int)mp.opcode[1] + 1,
26388           siz = (unsigned int)mp.opcode[2];
26389         while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
26390         return cimg::type<double>::nan();
26391       }
26392 
26393 #ifdef cimg_mp_func_store
26394       static double mp_store(_cimg_math_parser& mp) {
26395         const double
26396           *ptr1 = &_mp_arg(2),
26397           *ptr2 = &_mp_arg(4) + 1;
26398         const unsigned int
26399           siz1 = (unsigned int)mp.opcode[3],
26400           siz2 = (unsigned int)mp.opcode[5];
26401         const int
26402           w = (int)_mp_arg(6),
26403           h = (int)_mp_arg(7),
26404           d = (int)_mp_arg(8),
26405           s = (int)_mp_arg(9);
26406 
26407         const bool is_compressed = (bool)_mp_arg(10);
26408         if (w<0 || h<0 || d<0 || s<0)
26409           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
26410                                       "Specified image dimensions (%d,%d,%d,%d) are invalid.",
26411                                       pixel_type(),w,h,d,s);
26412         CImg<charT> ss(siz2 + 1);
26413         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i];
26414         ss.back() = 0;
26415         if (siz1) cimg_mp_func_store(ptr1 + 1,siz1,
26416                                      (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
26417                                      is_compressed,ss._data);
26418         else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
26419                                 is_compressed,ss._data);
26420         return cimg::type<double>::nan();
26421       }
26422 #endif
26423 
26424       static double mp_stov(_cimg_math_parser& mp) {
26425         const double *ptrs = &_mp_arg(2);
26426         const ulongT siz = (ulongT)mp.opcode[3];
26427         longT ind = (longT)_mp_arg(4);
26428         const bool is_strict = (bool)_mp_arg(5);
26429         double val = cimg::type<double>::nan();
26430         if (ind<0 || ind>=(longT)siz) return val;
26431         if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
26432 
26433         CImg<charT> ss(siz + 1 - ind);
26434         ptrs+=1 + ind;
26435         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
26436         ss.back() = 0;
26437 
26438         const char *s = ss._data;
26439         while (*s && *s<=32) ++s;
26440         const bool is_negative = *s=='-';
26441         if (is_negative || *s=='+') ++s;
26442         int err = 0;
26443         char sep;
26444 
26445         if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number
26446           unsigned int ival;
26447           err = cimg_sscanf(s + 2,"%x%c",&ival,&sep);
26448           if (err>0) val = (double)ival;
26449         } else if (*s>32) { // Decimal number
26450           err = cimg_sscanf(s,"%lf%c",&val,&sep);
26451 #if cimg_OS==2
26452           // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
26453           // to read those particular values.
26454           if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) {
26455             if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type<double>::inf(); err = 1 + (s[3]!=0); }
26456             else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type<double>::nan(); err = 1 + (s[3]!=0); }
26457           }
26458 #endif
26459         }
26460         if (err<=0 || (is_strict && err!=1)) return cimg::type<double>::nan();
26461         if (is_negative) val = -val;
26462         return val;
26463       }
26464 
26465       static double mp_string(_cimg_math_parser& mp) {
26466         double *const ptrd = &_mp_arg(1) + 1;
26467         const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2;
26468         CImgList<charT> _str;
26469         CImg<charT> it;
26470         for (unsigned int n = 0; n<nb_args; ++n) {
26471           const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
26472           if (siz) { // Vector argument -> string
26473             const double *ptr = &_mp_arg(4 + 2*n) + 1;
26474             unsigned int l = 0;
26475             while (l<siz && ptr[l]) ++l;
26476             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
26477           } else { // Scalar argument -> number
26478             it.assign(24);
26479             cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n));
26480             CImg<charT>::string(it,false,true).move_to(_str);
26481           }
26482         }
26483         const CImg<charT> str = _str>'x';
26484         const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]);
26485         std::memset(ptrd,0,mp.opcode[2]*sizeof(double));
26486         for (unsigned int k = 0; k<sizd; ++k) ptrd[k] = (double)str[k];
26487         return cimg::type<double>::nan();
26488       }
26489 
26490       static double mp_sub(_cimg_math_parser& mp) {
26491         return _mp_arg(2) - _mp_arg(3);
26492       }
26493 
26494       static double mp_sum(_cimg_math_parser& mp) {
26495         const unsigned int i_end = (unsigned int)mp.opcode[2];
26496         double val = _mp_arg(3);
26497         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
26498         return val;
26499       }
26500 
26501       static double mp_swap(_cimg_math_parser& mp) {
26502         const unsigned int siz = (unsigned int)mp.opcode[3];
26503         if (!siz) { // Scalar
26504           double &arg1 = _mp_arg(1), &arg2 = _mp_arg(2);
26505           cimg::swap(arg1,arg2);
26506         } else { // Vector
26507           double *const ptr1 = &_mp_arg(1) + 1, *const ptr2 = &_mp_arg(2) + 1;
26508           for (unsigned int k = 0; k<siz; ++k) cimg::swap(ptr1[k],ptr2[k]);
26509         }
26510         return _mp_arg(1);
26511       }
26512 
26513       static double mp_tan(_cimg_math_parser& mp) {
26514         return std::tan(_mp_arg(2));
26515       }
26516 
26517       static double mp_tanh(_cimg_math_parser& mp) {
26518         return std::tanh(_mp_arg(2));
26519       }
26520 
26521       static double mp_trace(_cimg_math_parser& mp) {
26522         const double *ptrs = &_mp_arg(2) + 1;
26523         const unsigned int k = (unsigned int)mp.opcode[3];
26524         return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
26525       }
26526 
26527       static double mp_transpose(_cimg_math_parser& mp) {
26528         double *ptrd = &_mp_arg(1) + 1;
26529         const double *ptrs = &_mp_arg(2) + 1;
26530         const unsigned int
26531           k = (unsigned int)mp.opcode[3],
26532           l = (unsigned int)mp.opcode[4];
26533         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
26534         return cimg::type<double>::nan();
26535       }
26536 
26537       static double mp_u(_cimg_math_parser& mp) {
26538         return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng);
26539       }
26540 
26541       static double mp_ui2f(_cimg_math_parser& mp) {
26542         return (double)cimg::uint2float((unsigned int)_mp_arg(2));
26543       }
26544 
26545       static double mp_uppercase(_cimg_math_parser& mp) {
26546         return cimg::uppercase(_mp_arg(2));
26547       }
26548 
26549       static double mp_var(_cimg_math_parser& mp) {
26550         const unsigned int i_end = (unsigned int)mp.opcode[2];
26551         CImg<doubleT> vals(i_end - 3);
26552         double *p = vals.data();
26553         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26554         return vals.variance();
26555       }
26556 
26557       static double mp_vector_copy(_cimg_math_parser& mp) {
26558         std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
26559         return cimg::type<double>::nan();
26560       }
26561 
26562       static double mp_vector_crop(_cimg_math_parser& mp) {
26563         double *ptrd = &_mp_arg(1) + 1;
26564         const double *ptrs = &_mp_arg(2) + 1;
26565         const longT
26566           length = (longT)mp.opcode[3],
26567           start = (longT)_mp_arg(4),
26568           sublength = (longT)mp.opcode[5],
26569           step = (longT)_mp_arg(6);
26570         if (start<0 || start + step*(sublength-1)>=length)
26571           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
26572                                       "Out-of-bounds sub-vector request "
26573                                       "(length: %ld, start: %ld, sub-length: %ld, step: %ld).",
26574                                       mp.imgin.pixel_type(),length,start,sublength,step);
26575         ptrs+=start;
26576         if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double));
26577         else for (longT k = 0; k<sublength; ++k) { *(ptrd++) = *ptrs; ptrs+=step; }
26578         return cimg::type<double>::nan();
26579       }
26580 
26581       static double mp_vector_init(_cimg_math_parser& mp) {
26582         unsigned int
26583           ptrs = 4U,
26584           ptrd = (unsigned int)mp.opcode[1] + 1,
26585           siz = (unsigned int)mp.opcode[3];
26586         switch (mp.opcode[2] - 4) {
26587         case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
26588         case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
26589         default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
26590         }
26591         return cimg::type<double>::nan();
26592       }
26593 
26594       static double mp_vector_eq(_cimg_math_parser& mp) {
26595         const double
26596           *ptr1 = &_mp_arg(2) + 1,
26597           *ptr2 = &_mp_arg(4) + 1;
26598         unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
26599         const int N = (int)_mp_arg(6);
26600         const bool case_sensitive = (bool)_mp_arg(7);
26601         bool still_equal = true;
26602         double value;
26603         if (!N) return true;
26604 
26605         // Compare all values.
26606         if (N<0) {
26607           if (p1>0 && p2>0) { // Vector == vector
26608             if (p1!=p2) return false;
26609             if (case_sensitive)
26610               while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
26611             else
26612               while (still_equal && p1--)
26613                 still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26614             return still_equal;
26615           } else if (p1>0 && !p2) { // Vector == scalar
26616             value = _mp_arg(4);
26617             if (!case_sensitive) value = cimg::lowercase(value);
26618             while (still_equal && p1--) still_equal = *(ptr1++)==value;
26619             return still_equal;
26620           } else if (!p1 && p2>0) { // Scalar == vector
26621             value = _mp_arg(2);
26622             if (!case_sensitive) value = cimg::lowercase(value);
26623             while (still_equal && p2--) still_equal = *(ptr2++)==value;
26624             return still_equal;
26625           } else { // Scalar == scalar
26626             if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26627             else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26628           }
26629         }
26630 
26631         // Compare only first N values.
26632         if (p1>0 && p2>0) { // Vector == vector
26633           n = cimg::min((unsigned int)N,p1,p2);
26634           if (case_sensitive)
26635             while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
26636           else
26637             while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26638           return still_equal;
26639         } else if (p1>0 && !p2) { // Vector == scalar
26640           n = std::min((unsigned int)N,p1);
26641           value = _mp_arg(4);
26642           if (!case_sensitive) value = cimg::lowercase(value);
26643           while (still_equal && n--) still_equal = *(ptr1++)==value;
26644           return still_equal;
26645         } else if (!p1 && p2>0) { // Scalar == vector
26646           n = std::min((unsigned int)N,p2);
26647           value = _mp_arg(2);
26648           if (!case_sensitive) value = cimg::lowercase(value);
26649           while (still_equal && n--) still_equal = *(ptr2++)==value;
26650           return still_equal;
26651         }  // Scalar == scalar
26652         if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26653         return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26654       }
26655 
26656       static double mp_vector_lerp(_cimg_math_parser& mp) {
26657         unsigned int siz = (unsigned int)mp.opcode[2];
26658         double *ptrd = &_mp_arg(1) + 1;
26659         const double
26660           *ptrs1 = &_mp_arg(3) + 1,
26661           *ptrs2 = &_mp_arg(4) + 1,
26662           t = _mp_arg(5);
26663         for (unsigned int k = 0; k<siz; ++k) ptrd[k] = ptrs1[k]*(1-t) + ptrs2[k]*t;
26664         return cimg::type<double>::nan();
26665       }
26666 
26667       static double mp_vector_off(_cimg_math_parser& mp) {
26668         const unsigned int
26669           ptr = (unsigned int)mp.opcode[2] + 1,
26670           siz = (unsigned int)mp.opcode[3];
26671         const int off = (int)_mp_arg(4);
26672         return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
26673       }
26674 
26675       static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
26676         unsigned int
26677           siz = (unsigned int)mp.opcode[2],
26678           ptrs = (unsigned int)mp.opcode[5] + 1;
26679         double *ptrd = &_mp_arg(1) + 1;
26680         mp_func op = (mp_func)mp.opcode[3];
26681         CImg<ulongT> l_opcode(4);
26682         l_opcode[2] = mp.opcode[4]; // Scalar argument1
26683         l_opcode.swap(mp.opcode);
26684         ulongT &argument2 = mp.opcode[3];
26685         while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
26686         l_opcode.swap(mp.opcode);
26687         return cimg::type<double>::nan();
26688       }
26689 
26690       static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
26691         unsigned int
26692           siz = (unsigned int)mp.opcode[2],
26693           ptrs = (unsigned int)mp.opcode[4] + 1;
26694         double *ptrd = &_mp_arg(1) + 1;
26695         mp_func op = (mp_func)mp.opcode[3];
26696         CImg<ulongT> l_opcode(1,3);
26697         l_opcode.swap(mp.opcode);
26698         ulongT &argument = mp.opcode[2];
26699         while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
26700         l_opcode.swap(mp.opcode);
26701         return cimg::type<double>::nan();
26702       }
26703 
26704       static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
26705         unsigned int
26706           siz = (unsigned int)mp.opcode[2],
26707           ptrs = (unsigned int)mp.opcode[4] + 1;
26708         double *ptrd = &_mp_arg(1) + 1;
26709         mp_func op = (mp_func)mp.opcode[3];
26710         CImg<ulongT> l_opcode(1,4);
26711         l_opcode[3] = mp.opcode[5]; // Scalar argument2
26712         l_opcode.swap(mp.opcode);
26713         ulongT &argument1 = mp.opcode[2];
26714         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
26715         l_opcode.swap(mp.opcode);
26716         return cimg::type<double>::nan();
26717       }
26718 
26719       static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
26720         unsigned int
26721           siz = (unsigned int)mp.opcode[2],
26722           ptrs = (unsigned int)mp.opcode[4] + 1;
26723         double *ptrd = &_mp_arg(1) + 1;
26724         mp_func op = (mp_func)mp.opcode[3];
26725         CImg<ulongT> l_opcode(1,5);
26726         l_opcode[3] = mp.opcode[5]; // Scalar argument2
26727         l_opcode[4] = mp.opcode[6]; // Scalar argument3
26728         l_opcode.swap(mp.opcode);
26729         ulongT &argument1 = mp.opcode[2];
26730         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
26731         l_opcode.swap(mp.opcode);
26732         return cimg::type<double>::nan();
26733       }
26734 
26735       static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
26736         unsigned int
26737           siz = (unsigned int)mp.opcode[2],
26738           ptrs1 = (unsigned int)mp.opcode[4] + 1,
26739           ptrs2 = (unsigned int)mp.opcode[5] + 1;
26740         double *ptrd = &_mp_arg(1) + 1;
26741         mp_func op = (mp_func)mp.opcode[3];
26742         CImg<ulongT> l_opcode(1,4);
26743         l_opcode.swap(mp.opcode);
26744         ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
26745         while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
26746         l_opcode.swap(mp.opcode);
26747         return cimg::type<double>::nan();
26748       }
26749 
26750       static double mp_vector_neq(_cimg_math_parser& mp) {
26751         return !mp_vector_eq(mp);
26752       }
26753 
26754       static double mp_vector_print(_cimg_math_parser& mp) {
26755         const bool print_string = (bool)mp.opcode[4];
26756         cimg_pragma_openmp(critical(mp_vector_print))
26757         {
26758           CImg<charT> _expr(mp.opcode[2] - 5);
26759           const ulongT *ptrs = mp.opcode._data + 5;
26760           cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
26761           cimg::strellipsize(_expr);
26762           unsigned int
26763             ptr = (unsigned int)mp.opcode[1] + 1,
26764             siz0 = (unsigned int)mp.opcode[3],
26765             siz = siz0;
26766           cimg::mutex(6);
26767           std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data);
26768           unsigned int count = 0;
26769           while (siz-->0) {
26770             if (count>=64 && siz>=64) {
26771               std::fprintf(cimg::output(),"...,");
26772               ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
26773               siz = 64;
26774             } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":"");
26775             ++count;
26776           }
26777           if (print_string) {
26778             CImg<charT> str(siz0 + 1);
26779             ptr = (unsigned int)mp.opcode[1] + 1;
26780             for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
26781             str[siz0] = 0;
26782             cimg::strellipsize(str,1024,false);
26783             std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
26784           } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
26785           std::fflush(cimg::output());
26786           cimg::mutex(6,0);
26787         }
26788         return cimg::type<double>::nan();
26789       }
26790 
26791       static double mp_vector_resize(_cimg_math_parser& mp) {
26792         double *const ptrd = &_mp_arg(1) + 1;
26793         const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
26794         const int
26795           interpolation = (int)_mp_arg(5),
26796           boundary_conditions = (int)_mp_arg(6);
26797         if (p2) { // Resize vector
26798           const double *const ptrs = &_mp_arg(3) + 1;
26799           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
26800             get_resize(p1,1,1,1,interpolation,boundary_conditions);
26801         } else { // Resize scalar
26802           const double value = _mp_arg(3);
26803           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
26804                                                                                   boundary_conditions);
26805         }
26806         return cimg::type<double>::nan();
26807       }
26808 
26809       static double mp_vector_reverse(_cimg_math_parser& mp) {
26810         double *const ptrd = &_mp_arg(1) + 1;
26811         const double *const ptrs = &_mp_arg(2) + 1;
26812         const unsigned int p1 = (unsigned int)mp.opcode[3];
26813         CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
26814         return cimg::type<double>::nan();
26815       }
26816 
26817       static double mp_vector_set_off(_cimg_math_parser& mp) {
26818         const unsigned int
26819           ptr = (unsigned int)mp.opcode[2] + 1,
26820           siz = (unsigned int)mp.opcode[3];
26821         const int off = (int)_mp_arg(4);
26822         if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1);
26823         return _mp_arg(1);
26824       }
26825 
26826 #define _cimg_mp_vfunc(func) \
26827       const longT sizd = (longT)mp.opcode[2];\
26828       const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
26829       double *const ptrd = &_mp_arg(1) + (sizd?1:0); \
26830       cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \
26831       { CImg<doubleT> vec(nbargs); double res; \
26832         cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \
26833           cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \
26834           func; ptrd[k] = res; \
26835       }} \
26836       return sizd?cimg::type<double>::nan():*ptrd;
26837 
26838       static double _mp_vargkth(CImg<doubleT>& vec) {
26839         const double val = (+vec).get_shared_points(1,vec.width() - 1).
26840           kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2));
26841         cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.;
26842         return 1.;
26843       }
26844 
26845       static double mp_vargkth(_cimg_math_parser& mp) {
26846         _cimg_mp_vfunc(res = _mp_vargkth(vec));
26847       }
26848 
26849       static double mp_vargmax(_cimg_math_parser& mp) {
26850         _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data()));
26851       }
26852 
26853       static double mp_vargmaxabs(_cimg_math_parser& mp) {
26854         _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data()));
26855       }
26856 
26857       static double mp_vargmin(_cimg_math_parser& mp) {
26858         _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data()));
26859       }
26860 
26861       static double mp_vargminabs(_cimg_math_parser& mp) {
26862         _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data()));
26863       }
26864 
26865       static double mp_vavg(_cimg_math_parser& mp) {
26866         _cimg_mp_vfunc(res = vec.mean());
26867       }
26868 
26869       static double mp_vkth(_cimg_math_parser& mp) {
26870         _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1).
26871                        kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)));
26872       }
26873 
26874       static double mp_vmax(_cimg_math_parser& mp) {
26875         _cimg_mp_vfunc(res = vec.max());
26876       }
26877 
26878       static double mp_vmaxabs(_cimg_math_parser& mp) {
26879         _cimg_mp_vfunc(res = vec.maxabs());
26880       }
26881 
26882       static double mp_vmedian(_cimg_math_parser& mp) {
26883         _cimg_mp_vfunc(res = vec.median());
26884       }
26885 
26886       static double mp_vmin(_cimg_math_parser& mp) {
26887         _cimg_mp_vfunc(res = vec.min());
26888       }
26889 
26890       static double mp_vminabs(_cimg_math_parser& mp) {
26891         _cimg_mp_vfunc(res = vec.minabs());
26892       }
26893 
26894       static double mp_vprod(_cimg_math_parser& mp) {
26895         _cimg_mp_vfunc(res = vec.product());
26896       }
26897 
26898       static double mp_vstd(_cimg_math_parser& mp) {
26899         _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3]));
26900       }
26901 
26902       static double mp_vsum(_cimg_math_parser& mp) {
26903         _cimg_mp_vfunc(res = vec.sum());
26904       }
26905 
26906       static double mp_vvar(_cimg_math_parser& mp) {
26907         _cimg_mp_vfunc(res = vec.get_stats()[3]);
26908       }
26909 
26910       static double mp_vtos(_cimg_math_parser& mp) {
26911         double *ptrd = &_mp_arg(1) + 1;
26912         const unsigned int
26913           sizd = (unsigned int)mp.opcode[2],
26914           sizs = (unsigned int)mp.opcode[4];
26915         std::memset(ptrd,0,sizd*sizeof(double));
26916         const int nb_digits = (int)_mp_arg(5);
26917         CImg<charT> format(8);
26918         switch (nb_digits) {
26919         case -1 : std::strcpy(format,"%g"); break;
26920         case 0 : std::strcpy(format,"%.17g"); break;
26921         default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
26922         }
26923         CImg<charT> str;
26924         if (sizs) { // Vector expression
26925           const double *ptrs = &_mp_arg(3) + 1;
26926           CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
26927         } else { // Scalar expression
26928           str.assign(sizd + 1);
26929           cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
26930         }
26931         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
26932         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
26933         return cimg::type<double>::nan();
26934      }
26935 
26936       static double mp_while(_cimg_math_parser& mp) {
26937         const ulongT
26938           mem_body = mp.opcode[1],
26939           mem_cond = mp.opcode[2];
26940         const CImg<ulongT>
26941           *const p_cond = ++mp.p_code,
26942           *const p_body = p_cond + mp.opcode[3],
26943           *const p_end = p_body + mp.opcode[4];
26944         const unsigned int vsiz = (unsigned int)mp.opcode[5];
26945         bool is_cond = false;
26946         if (mp.opcode[6]) { // Set default value for result and condition if necessary
26947           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
26948           else mp.mem[mem_body] = cimg::type<double>::nan();
26949         }
26950         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
26951         const unsigned int _break_type = mp.break_type;
26952         mp.break_type = 0;
26953         do {
26954           for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
26955             mp.opcode._data = mp.p_code->_data;
26956             const ulongT target = mp.opcode[1];
26957             mp.mem[target] = _cimg_mp_defunc(mp);
26958           }
26959           if (mp.break_type==1) break;
26960           is_cond = (bool)mp.mem[mem_cond];
26961           if (is_cond && !mp.break_type) // Evaluate body
26962             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
26963               mp.opcode._data = mp.p_code->_data;
26964               const ulongT target = mp.opcode[1];
26965               mp.mem[target] = _cimg_mp_defunc(mp);
26966             }
26967           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
26968         } while (is_cond);
26969 
26970         mp.break_type = _break_type;
26971         mp.p_code = p_end - 1;
26972         return mp.mem[mem_body];
26973       }
26974 
26975       static double mp_Ioff(_cimg_math_parser& mp) {
26976         double *ptrd = &_mp_arg(1) + 1;
26977         const unsigned int
26978           boundary_conditions = (unsigned int)_mp_arg(3),
26979           vsiz = (unsigned int)mp.opcode[4];
26980         const CImg<T> &img = mp.imgin;
26981         const longT
26982           off = (longT)_mp_arg(2),
26983           whd = (longT)img.width()*img.height()*img.depth();
26984         const T *ptrs;
26985         if (off>=0 && off<whd) {
26986           ptrs = &img[off];
26987           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26988           return cimg::type<double>::nan();
26989         }
26990         if (img._data) switch (boundary_conditions) {
26991           case 3 : { // Mirror
26992             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
26993             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
26994             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26995             return cimg::type<double>::nan();
26996           }
26997           case 2 : // Periodic
26998             ptrs = &img[cimg::mod(off,whd)];
26999             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27000             return cimg::type<double>::nan();
27001           case 1 : // Neumann
27002             ptrs = off<0?&img[0]:&img[whd - 1];
27003             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27004             return cimg::type<double>::nan();
27005           default : // Dirichlet
27006             std::memset(ptrd,0,vsiz*sizeof(double));
27007             return cimg::type<double>::nan();
27008           }
27009         std::memset(ptrd,0,vsiz*sizeof(double));
27010         return cimg::type<double>::nan();
27011       }
27012 
27013       static double mp_Ixyz(_cimg_math_parser& mp) {
27014         double *ptrd = &_mp_arg(1) + 1;
27015         const unsigned int
27016           interpolation = (unsigned int)_mp_arg(5),
27017           boundary_conditions = (unsigned int)_mp_arg(6),
27018           vsiz = (unsigned int)mp.opcode[7];
27019         const CImg<T> &img = mp.imgin;
27020         const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
27021         const ulongT whd = (ulongT)img._width*img._height*img._depth;
27022         const T *ptrs;
27023         switch (interpolation) {
27024         case 2 : // Cubic interpolation
27025           switch (boundary_conditions) {
27026           case 3 : { // Mirror
27027             const float
27028               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27029               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27030               cx = mx<img.width()?mx:w2 - mx - 1,
27031               cy = my<img.height()?my:h2 - my - 1,
27032               cz = mz<img.depth()?mz:d2 - mz - 1;
27033             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
27034           } break;
27035           case 2 : // Periodic
27036             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
27037             break;
27038           case 1 : // Neumann
27039             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
27040             break;
27041           default : // Dirichlet
27042             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27043           } break;
27044         case 1 : // Linear interpolation
27045           switch (boundary_conditions) {
27046           case 3 : { // Mirror
27047             const float
27048               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27049               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27050               cx = mx<img.width()?mx:w2 - mx - 1,
27051               cy = my<img.height()?my:h2 - my - 1,
27052               cz = mz<img.depth()?mz:d2 - mz - 1;
27053             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
27054           } break;
27055           case 2 : // Periodic
27056             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
27057             break;
27058           case 1 : // Neumann
27059             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
27060             break;
27061           default : // Dirichlet
27062             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27063           } break;
27064         default : // Nearest neighbor interpolation
27065           switch (boundary_conditions) {
27066           case 3 : { // Mirror
27067             const int
27068               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
27069               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
27070               cx = mx<img.width()?mx:w2 - mx - 1,
27071               cy = my<img.height()?my:h2 - my - 1,
27072               cz = mz<img.depth()?mz:d2 - mz - 1;
27073             ptrs = &img(cx,cy,cz);
27074             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27075           } break;
27076           case 2 : { // Periodic
27077             const int
27078               cx = (int)cimg::mod(x,(double)img._width),
27079               cy = (int)cimg::mod(y,(double)img._height),
27080               cz = (int)cimg::mod(z,(double)img._depth);
27081             ptrs = &img(cx,cy,cz);
27082             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27083           } break;
27084           case 1 : { // Neumann
27085             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
27086             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27087           } break;
27088           default : // Dirichlet
27089             if (img.containsXYZC((int)x,(int)y,(int)z)) {
27090               ptrs = &img((int)x,(int)y,(int)z);
27091               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27092             } else std::memset(ptrd,0,vsiz*sizeof(double));
27093           }
27094         }
27095         return cimg::type<double>::nan();
27096       }
27097 
27098       static double mp_Joff(_cimg_math_parser& mp) {
27099         double *ptrd = &_mp_arg(1) + 1;
27100         const unsigned int
27101           boundary_conditions = (unsigned int)_mp_arg(3),
27102           vsiz = (unsigned int)mp.opcode[4];
27103         const CImg<T> &img = mp.imgin;
27104         const int
27105           ox = (int)mp.mem[_cimg_mp_slot_x],
27106           oy = (int)mp.mem[_cimg_mp_slot_y],
27107           oz = (int)mp.mem[_cimg_mp_slot_z];
27108         const longT
27109           off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
27110           whd = (longT)img.width()*img.height()*img.depth();
27111         const T *ptrs;
27112         if (off>=0 && off<whd) {
27113           ptrs = &img[off];
27114           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27115           return cimg::type<double>::nan();
27116         }
27117         if (img._data) switch (boundary_conditions) {
27118           case 3 : { // Mirror
27119             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
27120             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
27121             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27122             return cimg::type<double>::nan();
27123           }
27124           case 2 : // Periodic
27125             ptrs = &img[cimg::mod(off,whd)];
27126             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27127             return cimg::type<double>::nan();
27128           case 1 : // Neumann
27129             ptrs = off<0?&img[0]:&img[whd - 1];
27130             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27131             return cimg::type<double>::nan();
27132           default : // Dirichlet
27133             std::memset(ptrd,0,vsiz*sizeof(double));
27134             return cimg::type<double>::nan();
27135           }
27136         std::memset(ptrd,0,vsiz*sizeof(double));
27137         return cimg::type<double>::nan();
27138       }
27139 
27140       static double mp_Jxyz(_cimg_math_parser& mp) {
27141         double *ptrd = &_mp_arg(1) + 1;
27142         const unsigned int
27143           interpolation = (unsigned int)_mp_arg(5),
27144           boundary_conditions = (unsigned int)_mp_arg(6),
27145           vsiz = (unsigned int)mp.opcode[7];
27146         const CImg<T> &img = mp.imgin;
27147         const double
27148           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
27149           x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
27150         const ulongT whd = (ulongT)img._width*img._height*img._depth;
27151         const T *ptrs;
27152         switch (interpolation) {
27153         case 2 : // Cubic interpolation
27154           switch (boundary_conditions) {
27155           case 3 : { // Mirror
27156             const float
27157               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27158               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27159               cx = mx<img.width()?mx:w2 - mx - 1,
27160               cy = my<img.height()?my:h2 - my - 1,
27161               cz = mz<img.depth()?mz:d2 - mz - 1;
27162             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
27163           } break;
27164           case 2 : // Periodic
27165             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
27166             break;
27167           case 1 : // Neumann
27168             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
27169             break;
27170           default : // Dirichlet
27171             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27172           } break;
27173         case 1 : // Linear interpolation
27174           switch (boundary_conditions) {
27175           case 3 : { // Mirror
27176             const float
27177               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27178               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27179               cx = mx<img.width()?mx:w2 - mx - 1,
27180               cy = my<img.height()?my:h2 - my - 1,
27181               cz = mz<img.depth()?mz:d2 - mz - 1;
27182             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
27183           } break;
27184           case 2 : // Periodic
27185             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
27186             break;
27187           case 1 : // Neumann
27188             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
27189             break;
27190           default : // Dirichlet
27191             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27192           } break;
27193         default : // Nearest neighbor interpolation
27194           switch (boundary_conditions) {
27195           case 3 : { // Mirror
27196             const int
27197               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
27198               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
27199               cx = mx<img.width()?mx:w2 - mx - 1,
27200               cy = my<img.height()?my:h2 - my - 1,
27201               cz = mz<img.depth()?mz:d2 - mz - 1;
27202             ptrs = &img(cx,cy,cz);
27203             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27204           } break;
27205           case 2 : { // Periodic
27206             const int
27207               cx = (int)cimg::mod(x,(double)img._width),
27208               cy = (int)cimg::mod(y,(double)img._height),
27209               cz = (int)cimg::mod(z,(double)img._depth);
27210             ptrs = &img(cx,cy,cz);
27211             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27212           } break;
27213           case 1 : { // Neumann
27214             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
27215             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27216           } break;
27217           default : // Dirichlet
27218             if (img.containsXYZC((int)x,(int)y,(int)z)) {
27219               ptrs = &img((int)x,(int)y,(int)z);
27220               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27221             } else std::memset(ptrd,0,vsiz*sizeof(double));
27222           }
27223         }
27224         return cimg::type<double>::nan();
27225       }
27226 
27227 #undef _mp_arg
27228 
27229     }; // struct _cimg_math_parser {}
27230 
27231 #define _cimg_create_pointwise_functions(name,func,min_size) \
27232     CImg<T>& name() { \
27233       if (is_empty()) return *this; \
27234       cimg_openmp_for(*this,func((typename cimg::superset<T,float>::type)*ptr),min_size); \
27235       return *this; \
27236     } \
27237     CImg<Tfloat> get_##name() const { \
27238       return CImg<Tfloat>(*this,false).name(); \
27239     }
27240 
27241     //! Compute the square value of each pixel value.
27242     /**
27243        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$.
27244        \note
27245        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27246        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27247        \par Example
27248        \code
27249        const CImg<float> img("reference.jpg");
27250        (img,img.get_sqr().normalize(0,255)).display();
27251        \endcode
27252        \image html ref_sqr.jpg
27253     **/
27254     _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
27255 
27256     //! Compute the square root of each pixel value.
27257     /**
27258        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$.
27259        \note
27260        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27261        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27262        \par Example
27263        \code
27264        const CImg<float> img("reference.jpg");
27265        (img,img.get_sqrt().normalize(0,255)).display();
27266        \endcode
27267        \image html ref_sqrt.jpg
27268     **/
27269     _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
27270 
27271     //! Compute the exponential of each pixel value.
27272     /**
27273        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$.
27274        \note
27275        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27276        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27277     **/
27278     _cimg_create_pointwise_functions(exp,std::exp,4096)
27279 
27280     //! Compute the error function of each pixel value.
27281     /**
27282        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its error function.
27283        \note
27284        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27285        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27286     **/
27287     _cimg_create_pointwise_functions(erf,std::erf,4096)
27288 
27289     //! Compute the logarithm of each pixel value.
27290     /**
27291        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
27292        \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
27293        \note
27294        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27295        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27296     **/
27297     _cimg_create_pointwise_functions(log,std::log,262144)
27298 
27299     //! Compute the base-2 logarithm of each pixel value.
27300     /**
27301        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
27302        \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
27303        \note
27304        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27305        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27306     **/
27307     _cimg_create_pointwise_functions(log2,cimg::log2,4096)
27308 
27309     //! Compute the base-10 logarithm of each pixel value.
27310     /**
27311        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
27312        \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
27313        \note
27314        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27315        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27316     **/
27317     _cimg_create_pointwise_functions(log10,std::log10,4096)
27318 
27319     //! Compute the absolute value of each pixel value.
27320     /**
27321        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$.
27322        \note
27323        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27324        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27325     **/
27326     _cimg_create_pointwise_functions(abs,cimg::abs,524288)
27327 
27328     //! Compute the sign of each pixel value.
27329     /**
27330        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
27331        \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
27332        \note
27333        - The sign is set to:
27334          - \c 1 if pixel value is strictly positive.
27335          - \c -1 if pixel value is strictly negative.
27336          - \c 0 if pixel value is equal to \c 0.
27337        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27338        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27339     **/
27340     _cimg_create_pointwise_functions(sign,cimg::sign,32768)
27341 
27342     //! Compute the cosine of each pixel value.
27343     /**
27344        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$.
27345        \note
27346        - Pixel values are regarded as being in \e radian.
27347        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27348        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27349     **/
27350     _cimg_create_pointwise_functions(cos,std::cos,8192)
27351 
27352     //! Compute the sine of each pixel value.
27353     /**
27354        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$.
27355        \note
27356        - Pixel values are regarded as being in \e radian.
27357        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27358        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27359     **/
27360     _cimg_create_pointwise_functions(sin,std::sin,8192)
27361 
27362     //! Compute the sinc of each pixel value.
27363     /**
27364        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
27365        \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
27366        \note
27367        - Pixel values are regarded as being exin \e radian.
27368        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27369        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27370     **/
27371     _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
27372 
27373     //! Compute the tangent of each pixel value.
27374     /**
27375        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$.
27376        \note
27377        - Pixel values are regarded as being exin \e radian.
27378        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27379        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27380     **/
27381     _cimg_create_pointwise_functions(tan,std::tan,2048)
27382 
27383     //! Compute the hyperbolic cosine of each pixel value.
27384     /**
27385        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
27386        \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
27387        \note
27388        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27389        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27390     **/
27391     _cimg_create_pointwise_functions(cosh,std::cosh,2048)
27392 
27393     //! Compute the hyperbolic sine of each pixel value.
27394     /**
27395        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
27396        \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
27397        \note
27398        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27399        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27400     **/
27401     _cimg_create_pointwise_functions(sinh,std::sinh,2048)
27402 
27403     //! Compute the hyperbolic tangent of each pixel value.
27404     /**
27405        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
27406        \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
27407        \note
27408        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27409        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27410     **/
27411     _cimg_create_pointwise_functions(tanh,std::tanh,2048)
27412 
27413     //! Compute the arccosine of each pixel value.
27414     /**
27415        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
27416        \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
27417        \note
27418        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27419        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27420     **/
27421     _cimg_create_pointwise_functions(acos,std::acos,8192)
27422 
27423     //! Compute the arcsine of each pixel value.
27424     /**
27425        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
27426        \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
27427        \note
27428        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27429        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27430     **/
27431     _cimg_create_pointwise_functions(asin,std::asin,8192)
27432 
27433     //! Compute the arctangent of each pixel value.
27434     /**
27435        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
27436        \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
27437        \note
27438        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27439        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27440     **/
27441     _cimg_create_pointwise_functions(atan,std::atan,8192)
27442 
27443     //! Compute the arctangent2 of each pixel value.
27444     /**
27445        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
27446        \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
27447        \param img Image whose pixel values specify the second argument of the \c atan2() function.
27448        \note
27449        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27450        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27451        \par Example
27452        \code
27453        const CImg<float>
27454           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'
27455           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'
27456           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value
27457        (img_x,img_y,img_atan2).display();
27458        \endcode
27459     **/
27460     template<typename t>
27461     CImg<T>& atan2(const CImg<t>& img) {
27462       const ulongT siz = size(), isiz = img.size();
27463       if (siz && isiz) {
27464         if (is_overlapped(img)) return atan2(+img);
27465         T *ptrd = _data, *const ptre = _data + siz;
27466         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27467           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27468             *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27469         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27470       }
27471       return *this;
27472     }
27473 
27474     //! Compute the arctangent2 of each pixel value \newinstance.
27475     template<typename t>
27476     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
27477       return CImg<Tfloat>(*this,false).atan2(img);
27478     }
27479 
27480     //! Compute the hyperbolic arccosine of each pixel value.
27481     /**
27482        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
27483        \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
27484        \note
27485        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27486        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27487     **/
27488     _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
27489 
27490     //! Compute the hyperbolic arcsine of each pixel value.
27491     /**
27492        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
27493        \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
27494        \note
27495        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27496        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27497     **/
27498     _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
27499 
27500     //! Compute the hyperbolic arctangent of each pixel value.
27501     /**
27502        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
27503        \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
27504        \note
27505        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27506        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27507     **/
27508     _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
27509 
27510     //! In-place pointwise multiplication.
27511     /**
27512        Compute the pointwise multiplication between the image instance and the specified input image \c img.
27513        \param img Input image, as the second operand of the multiplication.
27514        \note
27515        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
27516          instead of an addition.
27517        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
27518        \par Example
27519        \code
27520        CImg<float>
27521          img("reference.jpg"),
27522          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
27523        shade.normalize(0,1);
27524        (img,shade,img.get_mul(shade)).display();
27525        \endcode
27526     **/
27527     template<typename t>
27528     CImg<T>& mul(const CImg<t>& img) {
27529       const ulongT siz = size(), isiz = img.size();
27530       if (siz && isiz) {
27531         if (is_overlapped(img)) return mul(+img);
27532         T *ptrd = _data, *const ptre = _data + siz;
27533         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27534           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27535             *ptrd = (T)(*ptrd * *(ptrs++));
27536         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
27537       }
27538       return *this;
27539     }
27540 
27541     //! In-place pointwise multiplication \newinstance.
27542     template<typename t>
27543     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
27544       return CImg<_cimg_Tt>(*this,false).mul(img);
27545     }
27546 
27547     //! In-place pointwise division.
27548     /**
27549        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
27550     **/
27551     template<typename t>
27552     CImg<T>& div(const CImg<t>& img) {
27553       const ulongT siz = size(), isiz = img.size();
27554       if (siz && isiz) {
27555         if (is_overlapped(img)) return div(+img);
27556         T *ptrd = _data, *const ptre = _data + siz;
27557         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27558           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27559             *ptrd = (T)(*ptrd / *(ptrs++));
27560         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
27561       }
27562       return *this;
27563     }
27564 
27565     //! In-place pointwise division \newinstance.
27566     template<typename t>
27567     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
27568       return CImg<_cimg_Tt>(*this,false).div(img);
27569     }
27570 
27571     //! Raise each pixel value to a specified power.
27572     /**
27573        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$.
27574        \param p Exponent value.
27575        \note
27576        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27577        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27578        \par Example
27579        \code
27580        const CImg<float>
27581          img0("reference.jpg"),           // Load reference color image
27582          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8
27583          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5
27584        (img0,img1,img2).display();
27585        \endcode
27586     **/
27587     CImg<T>& pow(const double p) {
27588       if (is_empty()) return *this;
27589       if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; }
27590       if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; }
27591       if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; }
27592       if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; }
27593       if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; }
27594       if (p==0) return fill((T)1);
27595       if (p==0.5) return sqrt();
27596       if (p==1) return *this;
27597       if (p==2) return sqr();
27598       if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; }
27599       if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; }
27600       cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024);
27601       return *this;
27602     }
27603 
27604     //! Raise each pixel value to a specified power \newinstance.
27605     CImg<Tfloat> get_pow(const double p) const {
27606       return CImg<Tfloat>(*this,false).pow(p);
27607     }
27608 
27609     //! Raise each pixel value to a power, specified from an expression.
27610     /**
27611        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
27612     **/
27613     CImg<T>& pow(const char *const expression) {
27614       return pow((+*this)._fill(expression,true,1,0,0,"pow",this));
27615     }
27616 
27617     //! Raise each pixel value to a power, specified from an expression \newinstance.
27618     CImg<Tfloat> get_pow(const char *const expression) const {
27619       return CImg<Tfloat>(*this,false).pow(expression);
27620     }
27621 
27622     //! Raise each pixel value to a power, pointwisely specified from another image.
27623     /**
27624        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
27625     **/
27626     template<typename t>
27627     CImg<T>& pow(const CImg<t>& img) {
27628       const ulongT siz = size(), isiz = img.size();
27629       if (siz && isiz) {
27630         if (is_overlapped(img)) return pow(+img);
27631         T *ptrd = _data, *const ptre = _data + siz;
27632         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27633           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27634             *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27635         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27636       }
27637       return *this;
27638     }
27639 
27640     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
27641     template<typename t>
27642     CImg<Tfloat> get_pow(const CImg<t>& img) const {
27643       return CImg<Tfloat>(*this,false).pow(img);
27644     }
27645 
27646     //! Compute the bitwise left rotation of each pixel value.
27647     /**
27648        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
27649     **/
27650     CImg<T>& rol(const unsigned int n=1) {
27651       if (is_empty()) return *this;
27652       cimg_openmp_for(*this,cimg::rol(*ptr,n),32768);
27653       return *this;
27654     }
27655 
27656     //! Compute the bitwise left rotation of each pixel value \newinstance.
27657     CImg<T> get_rol(const unsigned int n=1) const {
27658       return (+*this).rol(n);
27659     }
27660 
27661     //! Compute the bitwise left rotation of each pixel value.
27662     /**
27663        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
27664     **/
27665     CImg<T>& rol(const char *const expression) {
27666       return rol((+*this)._fill(expression,true,1,0,0,"rol",this));
27667     }
27668 
27669     //! Compute the bitwise left rotation of each pixel value \newinstance.
27670     CImg<T> get_rol(const char *const expression) const {
27671       return (+*this).rol(expression);
27672     }
27673 
27674     //! Compute the bitwise left rotation of each pixel value.
27675     /**
27676        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
27677     **/
27678     template<typename t>
27679     CImg<T>& rol(const CImg<t>& img) {
27680       const ulongT siz = size(), isiz = img.size();
27681       if (siz && isiz) {
27682         if (is_overlapped(img)) return rol(+img);
27683         T *ptrd = _data, *const ptre = _data + siz;
27684         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27685           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27686             *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
27687         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
27688       }
27689       return *this;
27690     }
27691 
27692     //! Compute the bitwise left rotation of each pixel value \newinstance.
27693     template<typename t>
27694     CImg<T> get_rol(const CImg<t>& img) const {
27695       return (+*this).rol(img);
27696     }
27697 
27698     //! Compute the bitwise right rotation of each pixel value.
27699     /**
27700        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
27701     **/
27702     CImg<T>& ror(const unsigned int n=1) {
27703       if (is_empty()) return *this;
27704       cimg_openmp_for(*this,cimg::ror(*ptr,n),32768);
27705       return *this;
27706     }
27707 
27708     //! Compute the bitwise right rotation of each pixel value \newinstance.
27709     CImg<T> get_ror(const unsigned int n=1) const {
27710       return (+*this).ror(n);
27711     }
27712 
27713     //! Compute the bitwise right rotation of each pixel value.
27714     /**
27715        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
27716     **/
27717     CImg<T>& ror(const char *const expression) {
27718       return ror((+*this)._fill(expression,true,1,0,0,"ror",this));
27719     }
27720 
27721     //! Compute the bitwise right rotation of each pixel value \newinstance.
27722     CImg<T> get_ror(const char *const expression) const {
27723       return (+*this).ror(expression);
27724     }
27725 
27726     //! Compute the bitwise right rotation of each pixel value.
27727     /**
27728        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
27729     **/
27730     template<typename t>
27731     CImg<T>& ror(const CImg<t>& img) {
27732       const ulongT siz = size(), isiz = img.size();
27733       if (siz && isiz) {
27734         if (is_overlapped(img)) return ror(+img);
27735         T *ptrd = _data, *const ptre = _data + siz;
27736         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27737           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27738             *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
27739         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
27740       }
27741       return *this;
27742     }
27743 
27744     //! Compute the bitwise right rotation of each pixel value \newinstance.
27745     template<typename t>
27746     CImg<T> get_ror(const CImg<t>& img) const {
27747       return (+*this).ror(img);
27748     }
27749 
27750     //! Pointwise min operator between instance image and a value.
27751     /**
27752        \param val Value used as the reference argument of the min operator.
27753        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27754        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
27755      **/
27756     CImg<T>& min(const T& value) {
27757       if (is_empty()) return *this;
27758       cimg_openmp_for(*this,std::min(*ptr,value),65536);
27759       return *this;
27760     }
27761 
27762     //! Pointwise min operator between instance image and a value \newinstance.
27763     CImg<T> get_min(const T& value) const {
27764       return (+*this).min(value);
27765     }
27766 
27767     //! Pointwise min operator between two images.
27768     /**
27769        \param img Image used as the reference argument of the min operator.
27770        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27771        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27772      **/
27773     template<typename t>
27774     CImg<T>& min(const CImg<t>& img) {
27775       const ulongT siz = size(), isiz = img.size();
27776       if (siz && isiz) {
27777         if (is_overlapped(img)) return min(+img);
27778         T *ptrd = _data, *const ptre = _data + siz;
27779         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27780           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27781             *ptrd = std::min((T)*(ptrs++),*ptrd);
27782         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
27783       }
27784       return *this;
27785     }
27786 
27787     //! Pointwise min operator between two images \newinstance.
27788     template<typename t>
27789     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
27790       return CImg<_cimg_Tt>(*this,false).min(img);
27791     }
27792 
27793     //! Pointwise min operator between an image and an expression.
27794     /**
27795        \param expression Math formula as a C-string.
27796        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27797        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27798     **/
27799     CImg<T>& min(const char *const expression) {
27800       return min((+*this)._fill(expression,true,1,0,0,"min",this));
27801     }
27802 
27803     //! Pointwise min operator between an image and an expression \newinstance.
27804     CImg<Tfloat> get_min(const char *const expression) const {
27805       return CImg<Tfloat>(*this,false).min(expression);
27806     }
27807 
27808     //! Pointwise max operator between instance image and a value.
27809     /**
27810        \param val Value used as the reference argument of the max operator.
27811        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27812        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
27813      **/
27814     CImg<T>& max(const T& value) {
27815       if (is_empty()) return *this;
27816       cimg_openmp_for(*this,std::max(*ptr,value),65536);
27817       return *this;
27818     }
27819 
27820     //! Pointwise max operator between instance image and a value \newinstance.
27821     CImg<T> get_max(const T& value) const {
27822       return (+*this).max(value);
27823     }
27824 
27825     //! Pointwise max operator between two images.
27826     /**
27827        \param img Image used as the reference argument of the max operator.
27828        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27829        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27830      **/
27831     template<typename t>
27832     CImg<T>& max(const CImg<t>& img) {
27833       const ulongT siz = size(), isiz = img.size();
27834       if (siz && isiz) {
27835         if (is_overlapped(img)) return max(+img);
27836         T *ptrd = _data, *const ptre = _data + siz;
27837         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27838           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27839             *ptrd = std::max((T)*(ptrs++),*ptrd);
27840         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
27841       }
27842       return *this;
27843     }
27844 
27845     //! Pointwise max operator between two images \newinstance.
27846     template<typename t>
27847     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
27848       return CImg<_cimg_Tt>(*this,false).max(img);
27849     }
27850 
27851     //! Pointwise max operator between an image and an expression.
27852     /**
27853        \param expression Math formula as a C-string.
27854        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27855        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27856     **/
27857     CImg<T>& max(const char *const expression) {
27858       return max((+*this)._fill(expression,true,1,0,0,"max",this));
27859     }
27860 
27861     //! Pointwise max operator between an image and an expression \newinstance.
27862     CImg<Tfloat> get_max(const char *const expression) const {
27863       return CImg<Tfloat>(*this,false).max(expression);
27864     }
27865 
27866     //! Pointwise minabs operator between instance image and a value.
27867     /**
27868        \param val Value used as the reference argument of the minabs operator.
27869        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27870        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
27871      **/
27872     CImg<T>& minabs(const T& value) {
27873       if (is_empty()) return *this;
27874       const T absvalue = cimg::abs(value);
27875       cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536);
27876       return *this;
27877     }
27878 
27879     //! Pointwise minabs operator between instance image and a value \newinstance.
27880     CImg<T> get_minabs(const T& value) const {
27881       return (+*this).minabs(value);
27882     }
27883 
27884     //! Pointwise minabs operator between two images.
27885     /**
27886        \param img Image used as the reference argument of the minabs operator.
27887        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27888        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27889      **/
27890     template<typename t>
27891     CImg<T>& minabs(const CImg<t>& img) {
27892       const ulongT siz = size(), isiz = img.size();
27893       if (siz && isiz) {
27894         if (is_overlapped(img)) return minabs(+img);
27895         T *ptrd = _data, *const ptre = _data + siz;
27896         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27897           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27898             *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
27899         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
27900       }
27901       return *this;
27902     }
27903 
27904     //! Pointwise minabs operator between two images \newinstance.
27905     template<typename t>
27906     CImg<_cimg_Tt> get_minabs(const CImg<t>& img) const {
27907       return CImg<_cimg_Tt>(*this,false).minabs(img);
27908     }
27909 
27910     //! Pointwise minabs operator between an image and an expression.
27911     /**
27912        \param expression Math formula as a C-string.
27913        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27914        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27915     **/
27916     CImg<T>& minabs(const char *const expression) {
27917       return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this));
27918     }
27919 
27920     //! Pointwise minabs operator between an image and an expression \newinstance.
27921     CImg<Tfloat> get_minabs(const char *const expression) const {
27922       return CImg<Tfloat>(*this,false).minabs(expression);
27923     }
27924 
27925     //! Pointwise maxabs operator between instance image and a value.
27926     /**
27927        \param val Value used as the reference argument of the maxabs operator.
27928        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27929        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
27930      **/
27931     CImg<T>& maxabs(const T& value) {
27932       if (is_empty()) return *this;
27933       const T absvalue = cimg::abs(value);
27934       cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536);
27935       return *this;
27936     }
27937 
27938     //! Pointwise maxabs operator between instance image and a value \newinstance.
27939     CImg<T> get_maxabs(const T& value) const {
27940       return (+*this).maxabs(value);
27941     }
27942 
27943     //! Pointwise maxabs operator between two images.
27944     /**
27945        \param img Image used as the reference argument of the maxabs operator.
27946        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27947        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27948      **/
27949     template<typename t>
27950     CImg<T>& maxabs(const CImg<t>& img) {
27951       const ulongT siz = size(), isiz = img.size();
27952       if (siz && isiz) {
27953         if (is_overlapped(img)) return maxabs(+img);
27954         T *ptrd = _data, *const ptre = _data + siz;
27955         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27956           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27957             *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
27958         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
27959       }
27960       return *this;
27961     }
27962 
27963     //! Pointwise maxabs operator between two images \newinstance.
27964     template<typename t>
27965     CImg<_cimg_Tt> get_maxabs(const CImg<t>& img) const {
27966       return CImg<_cimg_Tt>(*this,false).maxabs(img);
27967     }
27968 
27969     //! Pointwise maxabs operator between an image and an expression.
27970     /**
27971        \param expression Math formula as a C-string.
27972        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27973        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27974     **/
27975     CImg<T>& maxabs(const char *const expression) {
27976       return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this));
27977     }
27978 
27979     //! Pointwise maxabs operator between an image and an expression \newinstance.
27980     CImg<Tfloat> get_maxabs(const char *const expression) const {
27981       return CImg<Tfloat>(*this,false).maxabs(expression);
27982     }
27983 
27984     //! Return a reference to the minimum pixel value.
27985     /**
27986      **/
27987     T& min() {
27988       if (is_empty())
27989         throw CImgInstanceException(_cimg_instance
27990                                     "min(): Empty instance.",
27991                                     cimg_instance);
27992       T *ptr_min = _data;
27993       T min_value = *ptr_min;
27994       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
27995       return *ptr_min;
27996     }
27997 
27998     //! Return a reference to the minimum pixel value \const.
27999     const T& min() const {
28000       if (is_empty())
28001         throw CImgInstanceException(_cimg_instance
28002                                     "min(): Empty instance.",
28003                                     cimg_instance);
28004       const T *ptr_min = _data;
28005       T min_value = *ptr_min;
28006       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
28007       return *ptr_min;
28008     }
28009 
28010     //! Return a reference to the minimum pixel value in absolute value.
28011     /**
28012      **/
28013     T& minabs() {
28014       if (is_empty())
28015         throw CImgInstanceException(_cimg_instance
28016                                     "minabs(): Empty instance.",
28017                                     cimg_instance);
28018       T *ptr_minabs = _data;
28019       T minabs_value = *ptr_minabs;
28020       cimg_for(*this,ptrs,T) {
28021         const T ma = cimg::abs(*ptrs);
28022         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
28023       }
28024       return *ptr_minabs;
28025     }
28026 
28027     //! Return a reference to the minimum pixel value in absolute value \const.
28028     const T& minabs() const {
28029       if (is_empty())
28030         throw CImgInstanceException(_cimg_instance
28031                                     "minabs(): Empty instance.",
28032                                     cimg_instance);
28033       const T *ptr_minabs = _data;
28034       T minabs_value = *ptr_minabs;
28035       cimg_for(*this,ptrs,T) {
28036         const T ma = cimg::abs(*ptrs);
28037         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
28038       }
28039       return *ptr_minabs;
28040     }
28041 
28042     //! Return a reference to the maximum pixel value.
28043     /**
28044      **/
28045     T& max() {
28046       if (is_empty())
28047         throw CImgInstanceException(_cimg_instance
28048                                     "max(): Empty instance.",
28049                                     cimg_instance);
28050       T *ptr_max = _data;
28051       T max_value = *ptr_max;
28052       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
28053       return *ptr_max;
28054     }
28055 
28056     //! Return a reference to the maximum pixel value \const.
28057     const T& max() const {
28058       if (is_empty())
28059         throw CImgInstanceException(_cimg_instance
28060                                     "max(): Empty instance.",
28061                                     cimg_instance);
28062       const T *ptr_max = _data;
28063       T max_value = *ptr_max;
28064       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
28065       return *ptr_max;
28066     }
28067 
28068     //! Return a reference to the maximum pixel value in absolute value.
28069     /**
28070      **/
28071     T& maxabs() {
28072       if (is_empty())
28073         throw CImgInstanceException(_cimg_instance
28074                                     "maxabs(): Empty instance.",
28075                                     cimg_instance);
28076       T *ptr_maxabs = _data;
28077       T maxabs_value = *ptr_maxabs;
28078       cimg_for(*this,ptrs,T) {
28079         const T ma = cimg::abs(*ptrs);
28080         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
28081       }
28082       return *ptr_maxabs;
28083     }
28084 
28085     //! Return a reference to the maximum pixel value in absolute value \const.
28086     const T& maxabs() const {
28087       if (is_empty())
28088         throw CImgInstanceException(_cimg_instance
28089                                     "maxabs(): Empty instance.",
28090                                     cimg_instance);
28091       const T *ptr_maxabs = _data;
28092       T maxabs_value = *ptr_maxabs;
28093       cimg_for(*this,ptrs,T) {
28094         const T ma = cimg::abs(*ptrs);
28095         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
28096       }
28097       return *ptr_maxabs;
28098     }
28099 
28100     //! Return a reference to the minimum pixel value as well as the maximum pixel value.
28101     /**
28102        \param[out] max_val Maximum pixel value.
28103     **/
28104     template<typename t>
28105     T& min_max(t& max_val) {
28106       if (is_empty())
28107         throw CImgInstanceException(_cimg_instance
28108                                     "min_max(): Empty instance.",
28109                                     cimg_instance);
28110       T *ptr_min = _data;
28111       T min_value = *ptr_min, max_value = min_value;
28112       cimg_for(*this,ptrs,T) {
28113         const T val = *ptrs;
28114         if (val<min_value) { min_value = val; ptr_min = ptrs; }
28115         if (val>max_value) max_value = val;
28116       }
28117       max_val = (t)max_value;
28118       return *ptr_min;
28119     }
28120 
28121     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
28122     template<typename t>
28123     const T& min_max(t& max_val) const {
28124       if (is_empty())
28125         throw CImgInstanceException(_cimg_instance
28126                                     "min_max(): Empty instance.",
28127                                     cimg_instance);
28128       const T *ptr_min = _data;
28129       T min_value = *ptr_min, max_value = min_value;
28130       cimg_for(*this,ptrs,T) {
28131         const T val = *ptrs;
28132         if (val<min_value) { min_value = val; ptr_min = ptrs; }
28133         if (val>max_value) max_value = val;
28134       }
28135       max_val = (t)max_value;
28136       return *ptr_min;
28137     }
28138 
28139     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
28140     /**
28141        \param[out] min_val Minimum pixel value.
28142     **/
28143     template<typename t>
28144     T& max_min(t& min_val) {
28145       if (is_empty())
28146         throw CImgInstanceException(_cimg_instance
28147                                     "max_min(): Empty instance.",
28148                                     cimg_instance);
28149       T *ptr_max = _data;
28150       T max_value = *ptr_max, min_value = max_value;
28151       cimg_for(*this,ptrs,T) {
28152         const T val = *ptrs;
28153         if (val>max_value) { max_value = val; ptr_max = ptrs; }
28154         if (val<min_value) min_value = val;
28155       }
28156       min_val = (t)min_value;
28157       return *ptr_max;
28158     }
28159 
28160     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
28161     template<typename t>
28162     const T& max_min(t& min_val) const {
28163       if (is_empty())
28164         throw CImgInstanceException(_cimg_instance
28165                                     "max_min(): Empty instance.",
28166                                     cimg_instance);
28167       const T *ptr_max = _data;
28168       T max_value = *ptr_max, min_value = max_value;
28169       cimg_for(*this,ptrs,T) {
28170         const T val = *ptrs;
28171         if (val>max_value) { max_value = val; ptr_max = ptrs; }
28172         if (val<min_value) min_value = val;
28173       }
28174       min_val = (t)min_value;
28175       return *ptr_max;
28176     }
28177 
28178     //! Return the kth smallest pixel value.
28179     /**
28180        \param k Rank of the smallest element searched.
28181     **/
28182     T kth_smallest(const ulongT k) const {
28183       if (is_empty())
28184         throw CImgInstanceException(_cimg_instance
28185                                     "kth_smallest(): Empty instance.",
28186                                     cimg_instance);
28187       if (k>=size()) return max();
28188       CImg<T> arr(*this,false);
28189       ulongT l = 0, ir = size() - 1;
28190       for ( ; ; ) {
28191         if (ir<=l + 1) {
28192           if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
28193           return arr[k];
28194         } else {
28195           const ulongT mid = (l + ir)>>1;
28196           cimg::swap(arr[mid],arr[l + 1]);
28197           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
28198           if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
28199           if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
28200           ulongT i = l + 1, j = ir;
28201           const T pivot = arr[l + 1];
28202           for ( ; ; ) {
28203             do ++i; while (arr[i]<pivot);
28204             do --j; while (arr[j]>pivot);
28205             if (j<i) break;
28206             cimg::swap(arr[i],arr[j]);
28207           }
28208           arr[l + 1] = arr[j];
28209           arr[j] = pivot;
28210           if (j>=k) ir = j - 1;
28211           if (j<=k) l = i;
28212         }
28213       }
28214     }
28215 
28216     //! Return the median pixel value.
28217     /**
28218      **/
28219     T median() const {
28220       if (is_empty())
28221         throw CImgInstanceException(_cimg_instance
28222                                     "median(): Empty instance.",
28223                                     cimg_instance);
28224       const ulongT s = size();
28225       switch (s) {
28226       case 1 : return _data[0];
28227       case 2 : return cimg::median(_data[0],_data[1]);
28228       case 3 : return cimg::median(_data[0],_data[1],_data[2]);
28229       case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
28230       case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
28231       case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
28232       case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
28233                                     _data[9],_data[10],_data[11],_data[12]);
28234       }
28235       const T res = kth_smallest(s>>1);
28236       return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
28237     }
28238 
28239     //! Return the product of all the pixel values.
28240     /**
28241      **/
28242     double product() const {
28243       if (is_empty()) return 0;
28244       double res = 1;
28245       cimg_for(*this,ptrs,T) res*=(double)*ptrs;
28246       return res;
28247     }
28248 
28249     //! Return the sum of all the pixel values.
28250     /**
28251      **/
28252     double sum() const {
28253       double res = 0;
28254       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
28255       return res;
28256     }
28257 
28258     //! Return the average pixel value.
28259     /**
28260      **/
28261     double mean() const {
28262       double res = 0;
28263       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
28264       return res/size();
28265     }
28266 
28267     //! Return the variance of the pixel values.
28268     /**
28269        \param variance_method Method used to estimate the variance. Can be:
28270        - \c 0: Second moment, computed as
28271        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
28272        1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
28273        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
28274        - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
28275        - \c 2: Least median of squares.
28276        - \c 3: Least trimmed of squares.
28277     **/
28278     double variance(const unsigned int variance_method=1) const {
28279       double foo;
28280       return variance_mean(variance_method,foo);
28281     }
28282 
28283     //! Return the variance as well as the average of the pixel values.
28284     /**
28285        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
28286        \param[out] mean Average pixel value.
28287     **/
28288     template<typename t>
28289     double variance_mean(const unsigned int variance_method, t& mean) const {
28290       if (is_empty())
28291         throw CImgInstanceException(_cimg_instance
28292                                     "variance_mean(): Empty instance.",
28293                                     cimg_instance);
28294 
28295       double variance = 0, average = 0;
28296       const ulongT siz = size();
28297       switch (variance_method) {
28298       case 0 : { // Least mean square (standard definition)
28299         double S = 0, S2 = 0;
28300         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
28301         variance = (S2 - S*S/siz)/siz;
28302         average = S;
28303       } break;
28304       case 1 : { // Least mean square (robust definition)
28305         double S = 0, S2 = 0;
28306         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
28307         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
28308         average = S;
28309       } break;
28310       case 2 : { // Least Median of Squares (MAD)
28311         CImg<Tfloat> buf(*this,false);
28312         buf.sort();
28313         const ulongT siz2 = siz>>1;
28314         const double med_i = (double)buf[siz2];
28315         cimg_for(buf,ptrs,Tfloat) {
28316           const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
28317         }
28318         buf.sort();
28319         const double sig = (double)(1.4828*buf[siz2]);
28320         variance = sig*sig;
28321       } break;
28322       default : { // Least trimmed of Squares
28323         CImg<Tfloat> buf(*this,false);
28324         const ulongT siz2 = siz>>1;
28325         cimg_for(buf,ptrs,Tfloat) {
28326           const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
28327         }
28328         buf.sort();
28329         double a = 0;
28330         const Tfloat *ptrs = buf._data;
28331         for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
28332         const double sig = (double)(2.6477*std::sqrt(a/siz2));
28333         variance = sig*sig;
28334       }
28335       }
28336       mean = (t)(average/siz);
28337       return variance>0?variance:0;
28338     }
28339 
28340     //! Return estimated variance of the noise.
28341     /**
28342        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
28343        \note Because of structures such as edges in images it is
28344        recommended to use a robust variance estimation. The variance of the
28345        noise is estimated by computing the variance of the Laplacian \f$(\Delta
28346        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
28347        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
28348     **/
28349     double variance_noise(const unsigned int variance_method=2) const {
28350       if (is_empty())
28351         throw CImgInstanceException(_cimg_instance
28352                                     "variance_noise(): Empty instance.",
28353                                     cimg_instance);
28354 
28355       const ulongT siz = size();
28356       if (!siz || !_data) return 0;
28357       if (variance_method>1) { // Compute a scaled version of the Laplacian
28358         CImg<Tdouble> tmp(*this,false);
28359         if (_depth==1) {
28360           const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed
28361           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 &&
28362                                                          _spectrum>=2))
28363           cimg_forC(*this,c) {
28364             CImg_3x3(I,T);
28365             cimg_for3x3(*this,x,y,0,c,I,T) {
28366               tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
28367                                  (double)Icp - 4*(double)Icc);
28368             }
28369           }
28370         } else {
28371           const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed
28372           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 &&
28373                                                          _spectrum>=2))
28374           cimg_forC(*this,c) {
28375             CImg_3x3x3(I,T);
28376             cimg_for3x3x3(*this,x,y,z,c,I,T) {
28377               tmp(x,y,z,c) = cste*(
28378                                    (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
28379                                    (double)Iccn + (double)Iccp - 6*(double)Iccc);
28380             }
28381           }
28382         }
28383         return tmp.variance(variance_method);
28384       }
28385 
28386       // Version that doesn't need intermediate images.
28387       double variance = 0, S = 0, S2 = 0;
28388       if (_depth==1) {
28389         const double cste = 1./std::sqrt(20.);
28390         CImg_3x3(I,T);
28391         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
28392           const double val = cste*((double)Inc + (double)Ipc +
28393                                    (double)Icn + (double)Icp - 4*(double)Icc);
28394           S+=val; S2+=val*val;
28395         }
28396       } else {
28397         const double cste = 1./std::sqrt(42.);
28398         CImg_3x3x3(I,T);
28399         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
28400           const double val = cste *
28401             ((double)Incc + (double)Ipcc + (double)Icnc +
28402              (double)Icpc +
28403              (double)Iccn + (double)Iccp - 6*(double)Iccc);
28404           S+=val; S2+=val*val;
28405         }
28406       }
28407       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
28408       else variance = (S2 - S*S/siz)/siz;
28409       return variance>0?variance:0;
28410     }
28411 
28412     //! Compute the MSE (Mean-Squared Error) between two images.
28413     /**
28414        \param img Image used as the second argument of the MSE operator.
28415     **/
28416     template<typename t>
28417     double MSE(const CImg<t>& img) const {
28418       if (img.size()!=size())
28419         throw CImgArgumentException(_cimg_instance
28420                                     "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
28421                                     cimg_instance,
28422                                     img._width,img._height,img._depth,img._spectrum,img._data);
28423       double vMSE = 0;
28424       const t* ptr2 = img._data;
28425       cimg_for(*this,ptr1,T) {
28426         const double diff = (double)*ptr1 - (double)*(ptr2++);
28427         vMSE+=diff*diff;
28428       }
28429       const ulongT siz = img.size();
28430       if (siz) vMSE/=siz;
28431       return vMSE;
28432     }
28433 
28434     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
28435     /**
28436        \param img Image used as the second argument of the PSNR operator.
28437        \param max_value Maximum theoretical value of the signal.
28438      **/
28439     template<typename t>
28440     double PSNR(const CImg<t>& img, const double max_value=255) const {
28441       const double vMSE = (double)std::sqrt(MSE(img));
28442       return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
28443     }
28444 
28445     //! Evaluate math formula.
28446     /**
28447        \param expression Math formula, as a C-string.
28448        \param x Value of the pre-defined variable \c x.
28449        \param y Value of the pre-defined variable \c y.
28450        \param z Value of the pre-defined variable \c z.
28451        \param c Value of the pre-defined variable \c c.
28452        \param list_inputs A list of input images attached to the specified math formula.
28453        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28454     **/
28455     double eval(const char *const expression,
28456                 const double x=0, const double y=0, const double z=0, const double c=0,
28457                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28458       return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
28459     }
28460 
28461     //! Evaluate math formula \const.
28462     double eval(const char *const expression,
28463                 const double x=0, const double y=0, const double z=0, const double c=0,
28464                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28465       return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
28466     }
28467 
28468     // Fast function to pre-evaluate common expressions.
28469     // (return 'true' in case of success, and set value of 'res').
28470     template<typename t>
28471     bool __eval(const char *const expression, t &res) const {
28472       if (!expression || !*expression) { res = (t)0; return true; }
28473       const char c = *expression;
28474       bool is_success = false;
28475       char c1, end;
28476       double val;
28477       if (c>='0' && c<='9') { // Possible value
28478         if (!expression[1]) { // Single digit
28479           res = (t)(c - '0');
28480           is_success = true;
28481         } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value
28482           res = (t)val;
28483           is_success = true;
28484         }
28485       } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value
28486                  (c1=expression[1])>='0' && c1<='0') {
28487         if (!expression[2]) { // [+-!] + Single digit
28488           const int ival = c1 - '0';
28489           res = (t)(c=='+'?ival:c=='-'?-ival:!ival);
28490           is_success = true;
28491         } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value
28492           res = (t)(c=='+'?val:c=='-'?-val:(double)!val);
28493           is_success = true;
28494         }
28495       } else if (!expression[1]) switch (*expression) { // Other common single-char expressions
28496         case 'w' : res = (t)_width; is_success = true; break;
28497         case 'h' : res = (t)_height; is_success = true; break;
28498         case 'd' : res = (t)_depth; is_success = true; break;
28499         case 's' : res = (t)_spectrum; is_success = true; break;
28500         case 'r' : res = (t)_is_shared; is_success = true; break;
28501         }
28502       return is_success;
28503     }
28504 
28505     double _eval(CImg<T> *const img_output, const char *const expression,
28506                  const double x, const double y, const double z, const double c,
28507                  const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28508       if (!expression || !*expression) return 0;
28509       double _val = 0;
28510       if (__eval(expression,_val)) return _val;
28511       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28512                                          *expression=='*' || *expression==':'),"eval",
28513                            *this,img_output,list_inputs,list_outputs,false);
28514       mp.begin_t();
28515       const double val = mp(x,y,z,c);
28516       mp.end_t();
28517       mp.end();
28518       return val;
28519     }
28520 
28521     //! Evaluate math formula.
28522     /**
28523        \param[out] output Contains values of output vector returned by the evaluated expression
28524          (or is empty if the returned type is scalar).
28525        \param expression Math formula, as a C-string.
28526        \param x Value of the pre-defined variable \c x.
28527        \param y Value of the pre-defined variable \c y.
28528        \param z Value of the pre-defined variable \c z.
28529        \param c Value of the pre-defined variable \c c.
28530        \param list_inputs A list of input images attached to the specified math formula.
28531        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28532     **/
28533     template<typename t>
28534     void eval(CImg<t> &output, const char *const expression,
28535               const double x=0, const double y=0, const double z=0, const double c=0,
28536               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28537       _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
28538     }
28539 
28540     //! Evaluate math formula \const.
28541     template<typename t>
28542     void eval(CImg<t>& output, const char *const expression,
28543               const double x=0, const double y=0, const double z=0, const double c=0,
28544               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28545       _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
28546     }
28547 
28548     template<typename t>
28549     void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
28550                const double x, const double y, const double z, const double c,
28551                const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28552       if (!expression || !*expression) { output.assign(1); *output = 0; return; }
28553       double _val = 0;
28554       if (__eval(expression,_val)) { output.assign(1); *output = _val; return; }
28555       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28556                                          *expression=='*' || *expression==':'),"eval",
28557                            *this,img_output,list_inputs,list_outputs,false);
28558       output.assign(1,std::max(1U,mp.result_dim));
28559       mp.begin_t();
28560       mp(x,y,z,c,output._data);
28561       mp.end_t();
28562       mp.end();
28563     }
28564 
28565     //! Evaluate math formula on a set of variables.
28566     /**
28567        \param expression Math formula, as a C-string.
28568        \param xyzc Set of values (x,y,z,c) used for the evaluation.
28569        \param list_inputs A list of input images attached to the specified math formula.
28570        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28571     **/
28572     template<typename t>
28573     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28574                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28575       return _eval(this,expression,xyzc,list_inputs,list_outputs);
28576     }
28577 
28578     //! Evaluate math formula on a set of variables \const.
28579     template<typename t>
28580     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28581                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28582       return _eval(0,expression,xyzc,list_inputs,list_outputs);
28583     }
28584 
28585     template<typename t>
28586     CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
28587                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28588       CImg<doubleT> res(1,xyzc.size()/4);
28589       if (!expression || !*expression) return res.fill(0);
28590       _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
28591 
28592 #if cimg_use_openmp!=0
28593       cimg_pragma_openmp(parallel if (res._height>=512))
28594       {
28595         _cimg_math_parser
28596           *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
28597           &lmp = *_mp;
28598         cimg_pragma_openmp(barrier)
28599         lmp.begin_t();
28600         cimg_pragma_openmp(for)
28601           for (int i = 0; i<res.height(); ++i) {
28602             const unsigned int i4 = 4*i;
28603             const double
28604               x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
28605               z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
28606             res[i] = lmp(x,y,z,c);
28607           }
28608         lmp.end_t();
28609         cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
28610         if (&lmp!=&mp) delete &lmp;
28611       }
28612 #else
28613       mp.begin_t();
28614       const t *ps = xyzc._data;
28615       cimg_for(res,pd,double) {
28616         const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
28617         *pd = mp(x,y,z,c);
28618       }
28619       mp.end_t();
28620 #endif
28621       mp.end();
28622       return res;
28623     }
28624 
28625     //! Compute statistics vector from the pixel values.
28626     /**
28627        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
28628        \return Statistics vector as
28629          <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
28630     **/
28631     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
28632       if (is_empty()) return CImg<doubleT>();
28633       const ulongT siz = size();
28634       const longT off_end = (longT)siz;
28635       double S = 0, S2 = 0, P = 1;
28636       longT offm = 0, offM = 0;
28637       T m = *_data, M = m;
28638 
28639       cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) {
28640         longT loffm = 0, loffM = 0;
28641         T lm = *_data, lM = lm;
28642         cimg_pragma_openmp(for)
28643         for (longT off = 0; off<off_end; ++off) {
28644           const T val = _data[off];
28645           const double _val = (double)val;
28646           if (val<lm) { lm = val; loffm = off; }
28647           if (val>lM) { lM = val; loffM = off; }
28648           S+=_val;
28649           S2+=_val*_val;
28650           P*=_val;
28651         }
28652         cimg_pragma_openmp(critical(get_stats)) {
28653           if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
28654           if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
28655         }
28656       }
28657 
28658       const double
28659         mean_value = S/siz,
28660         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
28661         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
28662          variance(variance_method)),
28663         variance_value = _variance_value>0?_variance_value:0;
28664       int
28665         xm = 0, ym = 0, zm = 0, cm = 0,
28666         xM = 0, yM = 0, zM = 0, cM = 0;
28667       contains(_data[offm],xm,ym,zm,cm);
28668       contains(_data[offM],xM,yM,zM,cM);
28669       return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
28670                                       (double)xm,(double)ym,(double)zm,(double)cm,
28671                                       (double)xM,(double)yM,(double)zM,(double)cM,
28672                                       S,P);
28673     }
28674 
28675     //! Compute statistics vector from the pixel values \inplace.
28676     CImg<T>& stats(const unsigned int variance_method=1) {
28677       return get_stats(variance_method).move_to(*this);
28678     }
28679 
28680     //@}
28681     //-------------------------------------
28682     //
28683     //! \name Vector / Matrix Operations
28684     //@{
28685     //-------------------------------------
28686 
28687     //! Compute norm of the image, viewed as a matrix.
28688     /**
28689        \param magnitude_type Norm type. Can be:
28690        - \c -1: Linf-norm
28691        - \c 0: L0-norm
28692        - \c 1: L1-norm
28693        - \c 2: L2-norm
28694     **/
28695     double magnitude(const int magnitude_type=2) const {
28696       if (is_empty())
28697         throw CImgInstanceException(_cimg_instance
28698                                     "magnitude(): Empty instance.",
28699                                     cimg_instance);
28700       const ulongT siz = size();
28701       double res = 0;
28702       switch (magnitude_type) {
28703       case -1 : {
28704         cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
28705       } break;
28706       case 1 : {
28707         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
28708         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
28709       } break;
28710       default : {
28711         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
28712         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
28713         res = (double)std::sqrt(res);
28714       }
28715       }
28716       return res;
28717     }
28718 
28719     //! Compute the trace of the image, viewed as a matrix.
28720     /**
28721      **/
28722     double trace() const {
28723       if (is_empty())
28724         throw CImgInstanceException(_cimg_instance
28725                                     "trace(): Empty instance.",
28726                                     cimg_instance);
28727       double res = 0;
28728       cimg_forX(*this,k) res+=(double)(*this)(k,k);
28729       return res;
28730     }
28731 
28732     //! Compute the determinant of the image, viewed as a matrix.
28733     /**
28734      **/
28735     double det() const {
28736       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
28737         throw CImgInstanceException(_cimg_instance
28738                                     "det(): Instance is not a square matrix.",
28739                                     cimg_instance);
28740 
28741       switch (_width) {
28742       case 1 : return (double)((*this)(0,0));
28743       case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
28744       case 3 : {
28745         const double
28746           a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
28747           b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
28748           c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
28749         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
28750       }
28751       default : {
28752         CImg<Tfloat> lu(*this,false);
28753         CImg<uintT> indx;
28754         bool d;
28755         lu._LU(indx,d);
28756         double res = d?(double)1:(double)-1;
28757         cimg_forX(lu,i) res*=lu(i,i);
28758         return res;
28759       }
28760       }
28761     }
28762 
28763     //! Compute the dot product between instance and argument, viewed as matrices.
28764     /**
28765        \param img Image used as a second argument of the dot product.
28766     **/
28767     template<typename t>
28768     double dot(const CImg<t>& img) const {
28769       const ulongT nb = std::min(size(),img.size());
28770       double res = 0;
28771       cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192))
28772       for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off];
28773       return res;
28774     }
28775 
28776     //! Get vector-valued pixel located at specified position.
28777     /**
28778        \param x X-coordinate of the pixel value.
28779        \param y Y-coordinate of the pixel value.
28780        \param z Z-coordinate of the pixel value.
28781     **/
28782     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
28783       CImg<T> res;
28784       if (res._height!=_spectrum) res.assign(1,_spectrum);
28785       const ulongT whd = (ulongT)_width*_height*_depth;
28786       const T *ptrs = data(x,y,z);
28787       T *ptrd = res._data;
28788       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
28789       return res;
28790     }
28791 
28792     //! Get (square) matrix-valued pixel located at specified position.
28793     /**
28794        \param x X-coordinate of the pixel value.
28795        \param y Y-coordinate of the pixel value.
28796        \param z Z-coordinate of the pixel value.
28797        \note - The spectrum() of the image must be a square.
28798      **/
28799     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
28800       const int n = (int)cimg::round(std::sqrt((double)_spectrum));
28801       const T *ptrs = data(x,y,z,0);
28802       const ulongT whd = (ulongT)_width*_height*_depth;
28803       CImg<T> res(n,n);
28804       T *ptrd = res._data;
28805       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
28806       return res;
28807     }
28808 
28809     //! Get tensor-valued pixel located at specified position.
28810     /**
28811        \param x X-coordinate of the pixel value.
28812        \param y Y-coordinate of the pixel value.
28813        \param z Z-coordinate of the pixel value.
28814     **/
28815     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
28816       const T *ptrs = data(x,y,z,0);
28817       const ulongT whd = (ulongT)_width*_height*_depth;
28818       if (_spectrum==6)
28819         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
28820       if (_spectrum==3)
28821         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
28822       return tensor(*ptrs);
28823     }
28824 
28825     //! Set vector-valued pixel at specified position.
28826     /**
28827        \param vec Vector to put on the instance image.
28828        \param x X-coordinate of the pixel value.
28829        \param y Y-coordinate of the pixel value.
28830        \param z Z-coordinate of the pixel value.
28831     **/
28832     template<typename t>
28833     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
28834       if (x<_width && y<_height && z<_depth) {
28835         const t *ptrs = vec._data;
28836         const ulongT whd = (ulongT)_width*_height*_depth;
28837         T *ptrd = data(x,y,z);
28838         for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
28839           *ptrd = (T)*(ptrs++); ptrd+=whd;
28840         }
28841       }
28842       return *this;
28843     }
28844 
28845     //! Set (square) matrix-valued pixel at specified position.
28846     /**
28847        \param mat Matrix to put on the instance image.
28848        \param x X-coordinate of the pixel value.
28849        \param y Y-coordinate of the pixel value.
28850        \param z Z-coordinate of the pixel value.
28851     **/
28852     template<typename t>
28853     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
28854       return set_vector_at(mat,x,y,z);
28855     }
28856 
28857     //! Set tensor-valued pixel at specified position.
28858     /**
28859        \param ten Tensor to put on the instance image.
28860        \param x X-coordinate of the pixel value.
28861        \param y Y-coordinate of the pixel value.
28862        \param z Z-coordinate of the pixel value.
28863     **/
28864     template<typename t>
28865     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
28866       T *ptrd = data(x,y,z,0);
28867       const ulongT siz = (ulongT)_width*_height*_depth;
28868       if (ten._height==2) {
28869         *ptrd = (T)ten[0]; ptrd+=siz;
28870         *ptrd = (T)ten[1]; ptrd+=siz;
28871         *ptrd = (T)ten[3];
28872       }
28873       else {
28874         *ptrd = (T)ten[0]; ptrd+=siz;
28875         *ptrd = (T)ten[1]; ptrd+=siz;
28876         *ptrd = (T)ten[2]; ptrd+=siz;
28877         *ptrd = (T)ten[4]; ptrd+=siz;
28878         *ptrd = (T)ten[5]; ptrd+=siz;
28879         *ptrd = (T)ten[8];
28880       }
28881       return *this;
28882     }
28883 
28884     //! Resize image to become a diagonal matrix.
28885     /**
28886        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
28887     **/
28888     CImg<T>& diagonal() {
28889       return get_diagonal().move_to(*this);
28890     }
28891 
28892     //! Resize image to become a diagonal matrix \newinstance.
28893     CImg<T> get_diagonal() const {
28894       if (is_empty()) return *this;
28895       const unsigned int siz = (unsigned int)size();
28896       CImg<T> res(siz,siz,1,1,0);
28897       cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
28898       return res;
28899     }
28900 
28901     //! Replace the image by an identity matrix.
28902     /**
28903        \note If the instance image is not square, it is resized to a square matrix using its maximum
28904        dimension as a reference.
28905     **/
28906     CImg<T>& identity_matrix() {
28907       return identity_matrix(std::max(_width,_height)).move_to(*this);
28908     }
28909 
28910     //! Replace the image by an identity matrix \newinstance.
28911     CImg<T> get_identity_matrix() const {
28912       return identity_matrix(std::max(_width,_height));
28913     }
28914 
28915     //! Fill image with a linear sequence of values.
28916     /**
28917        \param a0 Starting value of the sequence.
28918        \param a1 Ending value of the sequence.
28919     **/
28920     CImg<T>& sequence(const T& a0, const T& a1) {
28921       if (is_empty()) return *this;
28922       const ulongT siz = size() - 1;
28923       T* ptr = _data;
28924       if (siz) {
28925         const double delta = (double)a1 - (double)a0;
28926         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
28927       } else *ptr = a0;
28928       return *this;
28929     }
28930 
28931     //! Fill image with a linear sequence of values \newinstance.
28932     CImg<T> get_sequence(const T& a0, const T& a1) const {
28933       return (+*this).sequence(a0,a1);
28934     }
28935 
28936     //! Transpose the image, viewed as a matrix.
28937     /**
28938        \note Equivalent to \code permute_axes("yxzc"); \endcode.
28939     **/
28940     CImg<T>& transpose() {
28941       if (_width==1) { _width = _height; _height = 1; return *this; }
28942       if (_height==1) { _height = _width; _width = 1; return *this; }
28943       if (_width==_height) {
28944         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));
28945         return *this;
28946       }
28947       return get_transpose().move_to(*this);
28948     }
28949 
28950     //! Transpose the image, viewed as a matrix \newinstance.
28951     CImg<T> get_transpose() const {
28952       return get_permute_axes("yxzc");
28953     }
28954 
28955     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors.
28956     /**
28957        \param img Image used as the second argument of the cross product.
28958        \note The first argument of the cross product is \c *this.
28959      **/
28960     template<typename t>
28961     CImg<T>& cross(const CImg<t>& img) {
28962       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
28963         throw CImgInstanceException(_cimg_instance
28964                                     "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.",
28965                                     cimg_instance,
28966                                     img._width,img._height,img._depth,img._spectrum,img._data);
28967 
28968       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
28969       (*this)[0] = (T)(y*img[2] - z*img[1]);
28970       (*this)[1] = (T)(z*img[0] - x*img[2]);
28971       (*this)[2] = (T)(x*img[1] - y*img[0]);
28972       return *this;
28973     }
28974 
28975     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance.
28976     template<typename t>
28977     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
28978       return CImg<_cimg_Tt>(*this).cross(img);
28979     }
28980 
28981     //! Invert the instance image, viewed as a matrix.
28982     /**
28983        \param use_LU Choose the inverting algorithm. Can be:
28984        - \c true: LU-based matrix inversion.
28985        - \c false: SVD-based matrix inversion.
28986     **/
28987     CImg<T>& invert(const bool use_LU=true) {
28988       if (_width!=_height || _depth!=1 || _spectrum!=1)
28989         throw CImgInstanceException(_cimg_instance
28990                                     "invert(): Instance is not a square matrix.",
28991                                     cimg_instance);
28992       const double dete = _width>3?-1.:det();
28993       if (dete!=0. && _width==2) {
28994         const double
28995           a = _data[0], c = _data[1],
28996           b = _data[2], d = _data[3];
28997         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
28998         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
28999       } else if (dete!=0. && _width==3) {
29000         const double
29001           a = _data[0], d = _data[1], g = _data[2],
29002           b = _data[3], e = _data[4], h = _data[5],
29003           c = _data[6], f = _data[7], i = _data[8];
29004         _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
29005         _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
29006         _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
29007       } else {
29008 
29009 #ifdef cimg_use_lapack
29010         int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
29011         Tfloat
29012           *const lapA = new Tfloat[N*N],
29013           *const WORK = new Tfloat[LWORK];
29014         cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
29015         cimg::getrf(N,lapA,IPIV,INFO);
29016         if (INFO)
29017           cimg::warn(_cimg_instance
29018                      "invert(): LAPACK function dgetrf_() returned error code %d.",
29019                      cimg_instance,
29020                      INFO);
29021         else {
29022           cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
29023           if (INFO)
29024             cimg::warn(_cimg_instance
29025                        "invert(): LAPACK function dgetri_() returned error code %d.",
29026                        cimg_instance,
29027                        INFO);
29028         }
29029         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
29030         delete[] IPIV; delete[] lapA; delete[] WORK;
29031 #else
29032         if (use_LU) { // LU-based
29033           CImg<Tfloat> A(*this,false), indx;
29034           bool d;
29035           A._LU(indx,d);
29036           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16))
29037           cimg_forX(*this,j) {
29038             CImg<Tfloat> col(1,_width,1,1,0);
29039             col(j) = 1;
29040             col._solve(A,indx);
29041             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
29042           }
29043         } else pseudoinvert(false); // SVD-based
29044 #endif
29045       }
29046       return *this;
29047     }
29048 
29049     //! Invert the instance image, viewed as a matrix \newinstance.
29050     CImg<Tfloat> get_invert(const bool use_LU=true) const {
29051       return CImg<Tfloat>(*this,false).invert(use_LU);
29052     }
29053 
29054     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
29055     /**
29056     **/
29057     CImg<T>& pseudoinvert(const bool use_LU=false) {
29058       return get_pseudoinvert(use_LU).move_to(*this);
29059     }
29060 
29061     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
29062     CImg<Tfloat> get_pseudoinvert(const bool use_LU=false) const {
29063 
29064       // LU-based method.
29065       if (use_LU) {
29066         CImg<Tfloat> AtA(width(),width());
29067         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
29068         cimg_forY(AtA,i)
29069           for (int j = 0; j<=i; ++j) {
29070             double res = 0;
29071             cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k);
29072             AtA(j,i) = AtA(i,j) = (Tfloat)res;
29073           }
29074         AtA.invert(true);
29075         return AtA*get_transpose();
29076       }
29077 
29078       // SVD-based method.
29079       CImg<Tfloat> U, S, V;
29080       SVD(U,S,V,false);
29081       const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
29082       cimg_forX(V,x) {
29083         const Tfloat s = S(x), invs = s>epsilon?1/s:0;
29084         cimg_forY(V,y) V(x,y)*=invs;
29085       }
29086       return V*U.transpose();
29087     }
29088 
29089     //! Solve a system of linear equations.
29090     /**
29091        \param A Matrix of the linear system.
29092        \param use_LU In case of non square system (least-square solution),
29093                      choose between SVD-based (\c false) or LU-based (\c true) method.
29094                      LU method is faster for large matrices, but numerically less stable.
29095        \note Solve \c AX = B where \c B=*this.
29096     **/
29097     template<typename t>
29098     CImg<T>& solve(const CImg<t>& A, const bool use_LU=false) {
29099       if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
29100         throw CImgArgumentException(_cimg_instance
29101                                     "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
29102                                     "incompatible dimensions.",
29103                                     cimg_instance,
29104                                     A._width,A._height,A._depth,A._spectrum,A._data);
29105       typedef _cimg_Ttfloat Ttfloat;
29106 
29107       if (A.size()==1) return (*this)/=A[0];
29108       if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system
29109         const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3],
29110           fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d),
29111           det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd);
29112         if (fM==fa)
29113           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29114           cimg_forX(*this,k) {
29115             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
29116             (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y;
29117           } else if (fM==fc)
29118           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29119           cimg_forX(*this,k) {
29120             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
29121             (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y;
29122           } else if (fM==fb)
29123           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29124           cimg_forX(*this,k) {
29125             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
29126             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b);
29127           } else
29128           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29129           cimg_forX(*this,k) {
29130             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
29131             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d);
29132           }
29133         return *this;
29134       }
29135 
29136       if (A._width==A._height) { // Square linear system
29137 #ifdef cimg_use_lapack
29138         char TRANS = 'N';
29139         int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
29140         Ttfloat
29141           *const lapA = new Ttfloat[N*N],
29142           *const lapB = new Ttfloat[N],
29143           *const WORK = new Ttfloat[LWORK];
29144         cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
29145         cimg_forX(*this,i) {
29146           cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j));
29147           cimg::getrf(N,lapA,IPIV,INFO);
29148           if (INFO)
29149             cimg::warn(_cimg_instance
29150                        "solve(): LAPACK library function dgetrf_() returned error code %d.",
29151                        cimg_instance,
29152                        INFO);
29153           else {
29154             cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
29155             if (INFO)
29156               cimg::warn(_cimg_instance
29157                          "solve(): LAPACK library function dgetrs_() returned error code %d.",
29158                          cimg_instance,
29159                          INFO);
29160           }
29161           if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0;
29162         }
29163         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
29164 #else
29165         CImg<Ttfloat> lu(A,false);
29166         CImg<Ttfloat> indx;
29167         bool d;
29168         lu._LU(indx,d);
29169         CImg<T> res(_width,A._width);
29170         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16))
29171           cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx));
29172         res.move_to(*this);
29173 #endif
29174       } else { // Least-square solution for non-square systems
29175 
29176 #ifdef cimg_use_lapack
29177         char TRANS = 'N';
29178         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
29179         Ttfloat WORK_QUERY;
29180         Ttfloat
29181           * const lapA = new Ttfloat[M*N],
29182           * const lapB = new Ttfloat[M*NRHS];
29183         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
29184         LWORK = (int) WORK_QUERY;
29185         Ttfloat *const WORK = new Ttfloat[LWORK];
29186         cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
29187         cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
29188         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
29189         if (INFO != 0)
29190           cimg::warn(_cimg_instance
29191                      "solve(): LAPACK library function sgels() returned error code %d.",
29192                      cimg_instance,
29193                      INFO);
29194         assign(NRHS, N);
29195         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
29196         else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
29197         delete[] lapA; delete[] lapB; delete[] WORK;
29198 #else
29199         (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
29200 #endif
29201       }
29202       return *this;
29203     }
29204 
29205     //! Solve a system of linear equations \newinstance.
29206     template<typename t>
29207     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A, const bool use_LU=false) const {
29208       typedef _cimg_Ttfloat Ttfloat;
29209       return CImg<Ttfloat>(*this,false).solve(A,use_LU);
29210     }
29211 
29212     template<typename t, typename ti>
29213     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
29214       typedef _cimg_Ttfloat Ttfloat;
29215       const int N = height();
29216       int ii = -1;
29217       Ttfloat sum;
29218       for (int i = 0; i<N; ++i) {
29219         const int ip = (int)indx[i];
29220         sum = (*this)(ip);
29221         (*this)(ip) = (*this)(i);
29222         if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
29223         else if (sum!=0) ii = i;
29224         (*this)(i) = (T)sum;
29225       }
29226       for (int i = N - 1; i>=0; --i) {
29227         sum = (*this)(i);
29228         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
29229         (*this)(i) = (T)(sum/A(i,i));
29230       }
29231       return *this;
29232     }
29233 
29234     //! Solve a tridiagonal system of linear equations.
29235     /**
29236        \param A Coefficients of the tridiagonal system.
29237        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
29238        stored as a 3 columns matrix
29239        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
29240     **/
29241     template<typename t>
29242     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
29243       const unsigned int siz = (unsigned int)size();
29244       if (A._width!=3 || A._height!=siz)
29245         throw CImgArgumentException(_cimg_instance
29246                                     "solve_tridiagonal(): Instance and tridiagonal matrix "
29247                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
29248                                     cimg_instance,
29249                                     A._width,A._height,A._depth,A._spectrum,A._data);
29250       typedef _cimg_Ttfloat Ttfloat;
29251       const Ttfloat epsilon = 1e-4f;
29252       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
29253       for (int i = 1; i<(int)siz; ++i) {
29254         const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
29255         B[i] -= m*A(2,i - 1);
29256         V[i] -= m*V[i - 1];
29257       }
29258       (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
29259       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));
29260       return *this;
29261     }
29262 
29263     //! Solve a tridiagonal system of linear equations \newinstance.
29264     template<typename t>
29265     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
29266       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
29267     }
29268 
29269     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
29270     /**
29271        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
29272        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
29273     **/
29274     template<typename t>
29275     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
29276       if (is_empty()) { val.assign(); vec.assign(); }
29277       else {
29278         if (_width!=_height || _depth>1 || _spectrum>1)
29279           throw CImgInstanceException(_cimg_instance
29280                                       "eigen(): Instance is not a square matrix.",
29281                                       cimg_instance);
29282 
29283         if (val.size()<(ulongT)_width) val.assign(1,_width);
29284         if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
29285         switch (_width) {
29286         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
29287         case 2 : {
29288           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
29289           double f = e*e - 4*(a*d - b*c);
29290           if (f<0) cimg::warn(_cimg_instance
29291                               "eigen(): Complex eigenvalues found.",
29292                               cimg_instance);
29293           f = std::sqrt(f);
29294           const double
29295             l1 = 0.5*(e - f),
29296             l2 = 0.5*(e + f),
29297             b2 = b*b,
29298             norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
29299             norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
29300           val[0] = (t)l2;
29301           val[1] = (t)l1;
29302           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; }
29303           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; }
29304         } break;
29305         default :
29306           throw CImgInstanceException(_cimg_instance
29307                                       "eigen(): Eigenvalues computation of general matrices is limited "
29308                                       "to 2x2 matrices.",
29309                                       cimg_instance);
29310         }
29311       }
29312       return *this;
29313     }
29314 
29315     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
29316     /**
29317        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
29318     **/
29319     CImgList<Tfloat> get_eigen() const {
29320       CImgList<Tfloat> res(2);
29321       eigen(res[0],res[1]);
29322       return res;
29323     }
29324 
29325     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
29326     /**
29327        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
29328        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
29329     **/
29330     template<typename t>
29331     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
29332       if (is_empty()) { val.assign(); vec.assign(); return *this; }
29333       if (_width!=_height || _depth>1 || _spectrum>1)
29334         throw CImgInstanceException(_cimg_instance
29335                                     "eigen(): Instance is not a square matrix.",
29336                                     cimg_instance);
29337       val.assign(1,_width);
29338       vec.assign(_width,_width);
29339 
29340       if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; }
29341       if (_width==2) {
29342         const double
29343           a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3],
29344           e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)),
29345           l1 = 0.5*(e - f), l2 = 0.5*(e + f),
29346           n = std::sqrt(cimg::sqr(l2 - a) + b*b);
29347         val[0] = (t)l2;
29348         val[1] = (t)l1;
29349         if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; }
29350         vec[1] = -vec[2];
29351         vec[3] = vec[0];
29352         return *this;
29353       }
29354 
29355 #ifdef cimg_use_lapack
29356       char JOB = 'V', UPLO = 'U';
29357       int N = _width, LWORK = 4*N, INFO;
29358       Tfloat
29359         *const lapA = new Tfloat[N*N],
29360         *const lapW = new Tfloat[N],
29361         *const WORK = new Tfloat[LWORK];
29362       cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
29363       cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
29364       if (INFO)
29365         cimg::warn(_cimg_instance
29366                    "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
29367                    cimg_instance,
29368                    INFO);
29369       if (!INFO) {
29370         cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
29371         cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
29372       } else { val.fill(0); vec.fill(0); }
29373       delete[] lapA; delete[] lapW; delete[] WORK;
29374 
29375 #else
29376       CImg<t> V(_width,_width);
29377       Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
29378       (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
29379       if (maxabs!=1) val*=maxabs;
29380 
29381       bool is_ambiguous = false;
29382       float eig = 0;
29383       cimg_forY(val,p) { // Check for ambiguous cases
29384         if (val[p]>eig) eig = (float)val[p];
29385         t scal = 0;
29386         cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
29387         if (cimg::abs(scal)<0.9f) is_ambiguous = true;
29388         if (scal<0) val[p] = -val[p];
29389       }
29390       if (is_ambiguous) {
29391         ++(eig*=2);
29392         SVD(vec,val,V,false,40,eig);
29393         val-=eig;
29394       }
29395 
29396       CImg<intT> permutations; // Sort eigenvalues in decreasing order
29397       CImg<t> tmp(_width);
29398       val.sort(permutations,false);
29399       cimg_forY(vec,k) {
29400         cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
29401         std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
29402       }
29403 #endif
29404       return *this;
29405     }
29406 
29407     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
29408     /**
29409        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
29410          symmetric_eigen(CImg<t>&,CImg<t>&) const.
29411     **/
29412     CImgList<Tfloat> get_symmetric_eigen() const {
29413       CImgList<Tfloat> res(2);
29414       symmetric_eigen(res[0],res[1]);
29415       return res;
29416     }
29417 
29418     //! Sort pixel values and get sorting permutations.
29419     /**
29420        \param[out] permutations Permutation map used for the sorting.
29421        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29422     **/
29423     template<typename t>
29424     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
29425       permutations.assign(_width,_height,_depth,_spectrum);
29426       if (is_empty()) return *this;
29427       cimg_foroff(permutations,off) permutations[off] = (t)off;
29428       return _quicksort(0,size() - 1,permutations,is_increasing,true);
29429     }
29430 
29431     //! Sort pixel values and get sorting permutations \newinstance.
29432     template<typename t>
29433     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
29434       return (+*this).sort(permutations,is_increasing);
29435     }
29436 
29437     //! Sort pixel values.
29438     /**
29439        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29440        \param axis Tells if the value sorting must be done along a specific axis. Can be:
29441        - \c 0: All pixel values are sorted, independently on their initial position.
29442        - \c 'x': Image columns are sorted, according to the first value in each column.
29443        - \c 'y': Image rows are sorted, according to the first value in each row.
29444        - \c 'z': Image slices are sorted, according to the first value in each slice.
29445        - \c 'c': Image channels are sorted, according to the first value in each channel.
29446     **/
29447     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
29448       if (is_empty()) return *this;
29449       CImg<uintT> perm;
29450       switch (cimg::lowercase(axis)) {
29451       case 0 :
29452         _quicksort(0,size() - 1,perm,is_increasing,false);
29453         break;
29454       case 'x' : {
29455         perm.assign(_width);
29456         get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
29457         CImg<T> img(*this,false);
29458         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
29459       } break;
29460       case 'y' : {
29461         perm.assign(_height);
29462         get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
29463         CImg<T> img(*this,false);
29464         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
29465       } break;
29466       case 'z' : {
29467         perm.assign(_depth);
29468         get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
29469         CImg<T> img(*this,false);
29470         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
29471       } break;
29472       case 'c' : {
29473         perm.assign(_spectrum);
29474         get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
29475         CImg<T> img(*this,false);
29476         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
29477       } break;
29478       default :
29479         throw CImgArgumentException(_cimg_instance
29480                                     "sort(): Invalid specified axis '%c' "
29481                                     "(should be { x | y | z | c }).",
29482                                     cimg_instance,axis);
29483       }
29484       return *this;
29485     }
29486 
29487     //! Sort pixel values \newinstance.
29488     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
29489       return (+*this).sort(is_increasing,axis);
29490     }
29491 
29492     template<typename t>
29493     CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
29494                         const bool is_increasing, const bool is_permutations) {
29495       if (indm<indM) {
29496         const long mid = (indm + indM)/2;
29497         if (is_increasing) {
29498           if ((*this)[indm]>(*this)[mid]) {
29499             cimg::swap((*this)[indm],(*this)[mid]);
29500             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29501           }
29502           if ((*this)[mid]>(*this)[indM]) {
29503             cimg::swap((*this)[indM],(*this)[mid]);
29504             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29505           }
29506           if ((*this)[indm]>(*this)[mid]) {
29507             cimg::swap((*this)[indm],(*this)[mid]);
29508             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29509           }
29510         } else {
29511           if ((*this)[indm]<(*this)[mid]) {
29512             cimg::swap((*this)[indm],(*this)[mid]);
29513             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29514           }
29515           if ((*this)[mid]<(*this)[indM]) {
29516             cimg::swap((*this)[indM],(*this)[mid]);
29517             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29518           }
29519           if ((*this)[indm]<(*this)[mid]) {
29520             cimg::swap((*this)[indm],(*this)[mid]);
29521             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29522           }
29523         }
29524         if (indM - indm>=3) {
29525           const T pivot = (*this)[mid];
29526           long i = indm, j = indM;
29527           if (is_increasing) {
29528             do {
29529               while ((*this)[i]<pivot) ++i;
29530               while ((*this)[j]>pivot) --j;
29531               if (i<=j) {
29532                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29533                 cimg::swap((*this)[i++],(*this)[j--]);
29534               }
29535             } while (i<=j);
29536           } else {
29537             do {
29538               while ((*this)[i]>pivot) ++i;
29539               while ((*this)[j]<pivot) --j;
29540               if (i<=j) {
29541                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29542                 cimg::swap((*this)[i++],(*this)[j--]);
29543               }
29544             } while (i<=j);
29545           }
29546           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
29547           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
29548         }
29549       }
29550       return *this;
29551     }
29552 
29553     //! Compute the SVD of the instance image, viewed as a general matrix.
29554     /**
29555        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
29556        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
29557        \param[out] U First matrix of the SVD product.
29558        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
29559          These coefficients are stored as a vector.
29560        \param[out] V Third matrix of the SVD product.
29561        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
29562        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
29563        \param lambda Epsilon used for the algorithm convergence.
29564        \note The instance matrix can be computed from \c U,\c S and \c V by
29565        \code
29566        const CImg<> A;  // Input matrix (assumed to contain some values)
29567        CImg<> U,S,V;
29568        A.SVD(U,S,V)
29569        \endcode
29570     **/
29571     template<typename t>
29572     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
29573                        const unsigned int max_iteration=40, const float lambda=0) const {
29574       typedef _cimg_Ttfloat Ttfloat;
29575       const Ttfloat epsilon = (Ttfloat)1e-25;
29576 
29577       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
29578       else if (_depth!=1 || _spectrum!=1)
29579         throw CImgInstanceException(_cimg_instance
29580                                     "SVD(): Instance has invalid dimensions (depth or channels different from 1).",
29581                                     cimg_instance);
29582       else {
29583         U = *this;
29584         if (lambda!=0) {
29585           const unsigned int delta = std::min(U._width,U._height);
29586           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
29587         }
29588         if (S.size()<_width) S.assign(1,_width);
29589         if (V._width<_width || V._height<_height) V.assign(_width,_width);
29590         CImg<t> rv1(_width);
29591         Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0;
29592         int l = 0;
29593 
29594         cimg_forX(U,i) {
29595           l = i + 1;
29596           rv1[i] = scale*g;
29597           g = s = scale = 0;
29598           if (i<height()) {
29599             for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
29600             if (scale) {
29601               for (int k = i; k<height(); ++k) {
29602                 U(i,k)/=scale;
29603                 s+=U(i,k)*U(i,k);
29604               }
29605               f = U(i,i);
29606               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29607               h = f*g - s;
29608               U(i,i) = f - g;
29609               for (int j = l; j<width(); ++j) {
29610                 s = 0;
29611                 for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
29612                 f = s/h;
29613                 for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
29614               }
29615               for (int k = i; k<height(); ++k) U(i,k)*=scale;
29616             }
29617           }
29618           S[i] = scale*g;
29619 
29620           g = s = scale = 0;
29621           if (i<height() && i!=width() - 1) {
29622             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
29623             if (scale) {
29624               for (int k = l; k<width(); ++k) {
29625                 U(k,i)/=scale;
29626                 s+=U(k,i)*U(k,i);
29627               }
29628               f = U(l,i);
29629               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29630               h = f*g - s;
29631               U(l,i) = f - g;
29632               for (int k = l; k<width(); ++k) rv1[k] = U(k,i)/h;
29633               for (int j = l; j<height(); ++j) {
29634                 s = 0;
29635                 for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
29636                 for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
29637               }
29638               for (int k = l; k<width(); ++k) U(k,i)*=scale;
29639             }
29640           }
29641           anorm = (Ttfloat)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
29642         }
29643 
29644         for (int i = width() - 1; i>=0; --i) {
29645           if (i<width() - 1) {
29646             if (g) {
29647               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
29648               for (int j = l; j<width(); ++j) {
29649                 s = 0;
29650                 for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
29651                 for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
29652               }
29653             }
29654             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.;
29655           }
29656           V(i,i) = (t)1;
29657           g = rv1[i];
29658           l = i;
29659         }
29660 
29661         for (int i = std::min(width(),height()) - 1; i>=0; --i) {
29662           l = i + 1;
29663           g = S[i];
29664           for (int j = l; j<width(); ++j) U(j,i) = 0;
29665           if (g) {
29666             g = 1/g;
29667             for (int j = l; j<width(); ++j) {
29668               s = 0;
29669               for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
29670               f = (s/U(i,i))*g;
29671               for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
29672             }
29673             for (int j = i; j<height(); ++j) U(i,j)*= g;
29674           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
29675           ++U(i,i);
29676         }
29677 
29678         for (int k = width() - 1; k>=0; --k) {
29679           int nm = 0;
29680           for (unsigned int its = 0; its<max_iteration; ++its) {
29681             bool flag = true;
29682             for (l = k; l>=1; --l) {
29683               nm = l - 1;
29684               if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
29685               if ((cimg::abs(S[nm]) + anorm)==anorm) break;
29686             }
29687             if (flag) {
29688               c = 0;
29689               s = 1;
29690               for (int i = l; i<=k; ++i) {
29691                 f = s*rv1[i];
29692                 rv1[i] = c*rv1[i];
29693                 if ((cimg::abs(f) + anorm)==anorm) break;
29694                 g = S[i];
29695                 h = cimg::_hypot(f,g);
29696                 S[i] = h;
29697                 h = 1/h;
29698                 c = g*h;
29699                 s = -f*h;
29700                 cimg_forY(U,j) {
29701                   const t y = U(nm,j), z = U(i,j);
29702                   U(nm,j) = y*c + z*s;
29703                   U(i,j) = z*c - y*s;
29704                 }
29705               }
29706             }
29707 
29708             const t z = S[k];
29709             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
29710             nm = k - 1;
29711             t x = S[l], y = S[nm];
29712             g = rv1[nm];
29713             h = rv1[k];
29714             f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y);
29715             g = cimg::_hypot(f,(Ttfloat)1);
29716             f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x);
29717             c = s = 1;
29718             for (int j = l; j<=nm; ++j) {
29719               const int i = j + 1;
29720               g = rv1[i];
29721               h = s*g;
29722               g = c*g;
29723               t y1 = S[i], z1 = cimg::_hypot(f,h);
29724               rv1[j] = z1;
29725               c = f/std::max(epsilon,(Ttfloat)z1);
29726               s = h/std::max(epsilon,(Ttfloat)z1);
29727               f = x*c + g*s;
29728               g = g*c - x*s;
29729               h = y1*s;
29730               y1*=c;
29731               cimg_forX(U,jj) {
29732                 const t x2 = V(j,jj), z2 = V(i,jj);
29733                 V(j,jj) = x2*c + z2*s;
29734                 V(i,jj) = z2*c - x2*s;
29735               }
29736               z1 = cimg::_hypot(f,h);
29737               S[j] = z1;
29738               if (z1) {
29739                 z1 = 1/std::max(epsilon,(Ttfloat)z1);
29740                 c = f*z1;
29741                 s = h*z1;
29742               }
29743               f = c*g + s*y1;
29744               x = c*y1 - s*g;
29745               cimg_forY(U,jj) {
29746                 const t y2 = U(j,jj), z2 = U(i,jj);
29747                 U(j,jj) = y2*c + z2*s;
29748                 U(i,jj) = z2*c - y2*s;
29749               }
29750             }
29751             rv1[l] = 0;
29752             rv1[k] = f;
29753             S[k] = x;
29754           }
29755         }
29756 
29757         if (sorting) {
29758           CImg<intT> permutations;
29759           CImg<t> tmp(_width);
29760           S.sort(permutations,false);
29761           cimg_forY(U,k) {
29762             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
29763             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
29764           }
29765           cimg_forY(V,k) {
29766             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
29767             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
29768           }
29769         }
29770       }
29771       return *this;
29772     }
29773 
29774     //! Compute the SVD of the instance image, viewed as a general matrix.
29775     /**
29776        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
29777          SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
29778     **/
29779     CImgList<Tfloat> get_SVD(const bool sorting=true,
29780                              const unsigned int max_iteration=40, const float lambda=0) const {
29781       CImgList<Tfloat> res(3);
29782       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
29783       return res;
29784     }
29785 
29786     // [internal] Compute the LU decomposition of a permuted matrix.
29787     template<typename t>
29788     CImg<T>& _LU(CImg<t>& indx, bool& d) {
29789       const int N = width();
29790       int imax = 0;
29791       CImg<Tfloat> vv(N);
29792       indx.assign(N);
29793       d = true;
29794 
29795       bool return0 = false;
29796       cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512))
29797       cimg_forX(*this,i) {
29798         Tfloat vmax = 0;
29799         cimg_forX(*this,j) {
29800           const Tfloat tmp = cimg::abs((*this)(j,i));
29801           if (tmp>vmax) vmax = tmp;
29802         }
29803         if (vmax==0) return0 = true; else vv[i] = 1/vmax;
29804       }
29805       if (return0) { indx.fill(0); return fill(0); }
29806 
29807       cimg_forX(*this,j) {
29808         for (int i = 0; i<j; ++i) {
29809           Tfloat sum = (*this)(j,i);
29810           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
29811           (*this)(j,i) = (T)sum;
29812         }
29813 
29814         Tfloat vmax = 0;
29815         for (int i = j; i<width(); ++i) {
29816           Tfloat sum = (*this)(j,i);
29817           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
29818           (*this)(j,i) = (T)sum;
29819           const Tfloat tmp = vv[i]*cimg::abs(sum);
29820           if (tmp>=vmax) { vmax = tmp; imax = i; }
29821         }
29822         if (j!=imax) {
29823           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
29824           d = !d;
29825           vv[imax] = vv[j];
29826         }
29827         indx[j] = (t)imax;
29828         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
29829         if (j<N) {
29830           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
29831           for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
29832         }
29833       }
29834 
29835       return *this;
29836     }
29837 
29838     //! Compute the projection of the instance matrix onto the specified dictionary.
29839     /**
29840        Find the best matching projection of selected matrix onto the span of an over-complete dictionary D,
29841        using the orthogonal projection or (opt. Orthogonal) Matching Pursuit algorithm.
29842        Instance image must a 2D-matrix in which each column represent a signal to project.
29843        \param dictionary A matrix in which each column is an element of the dictionary D.
29844        \param method Tell what projection method is applied. It can be:
29845          - 0 = orthogonal projection (default).
29846          - 1 = matching pursuit.
29847          - 2 = matching pursuit, with a single orthogonal projection step at the end.
29848          - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
29849                  every 'method-2' iterations.
29850        \param max_iter Sets the max number of iterations processed for each signal.
29851                        If set to '0' (default), 'max_iter' is set to the number of dictionary columns.
29852                        (only meaningful for matching pursuit and its variants).
29853        \param max_residual Gives a stopping criterion on signal reconstruction accuracy.
29854                            (only meaningful for matching pursuit and its variants).
29855        \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column.
29856                Thus, the matrix product D*W is an approximation of the input matrix.
29857     **/
29858     template<typename t>
29859     CImg<T>& project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
29860                             const unsigned int max_iter=0, const double max_residual=1e-6) {
29861       return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this);
29862     }
29863 
29864     template<typename t>
29865     CImg<Tfloat> get_project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
29866                                     const unsigned int max_iter=0, const double max_residual=1e-6) const {
29867       if (_depth!=1 || _spectrum!=1)
29868         throw CImgInstanceException(_cimg_instance
29869                                     "project_matrix(): Instance image is not a matrix.",
29870                                     cimg_instance);
29871       if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1)
29872         throw CImgArgumentException(_cimg_instance
29873                                     "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.",
29874                                     cimg_instance,
29875                                     dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum);
29876 
29877       if (!method) return get_solve(dictionary,true);
29878       CImg<Tfloat> W(_width,dictionary._width,1,1,0);
29879 
29880       // Compute dictionary norm and normalize it.
29881       CImg<Tfloat> D(dictionary,false), Dnorm(D._width);
29882       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
29883       cimg_forX(Dnorm,d) {
29884         Tfloat norm = 0;
29885         cimg_forY(D,y) norm+=cimg::sqr(D(d,y));
29886         Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm));
29887       }
29888       cimg_forXY(D,d,y) D(d,y)/=Dnorm[d];
29889 
29890       // Matching pursuit.
29891       const unsigned int proj_step = method<3?1:method - 2;
29892       bool is_orthoproj = false;
29893 
29894       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
29895         cimg_forX(*this,x) {
29896         CImg<Tfloat> S = get_column(x);
29897         const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
29898         Tfloat residual = S.magnitude()/S._height;
29899         const unsigned int nmax = max_iter?max_iter:D._width;
29900 
29901         for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
29902 
29903           // Find best matching column in D.
29904           int dmax = 0;
29905           Tfloat absdotmax = 0, dotmax = 0;
29906           cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32))
29907           cimg_forX(D,d) {
29908             Tfloat _dot = 0;
29909             cimg_forY(D,y) _dot+=S[y]*D(d,y);
29910             Tfloat absdot = cimg::abs(_dot);
29911             cimg_pragma_openmp(critical(get_project_matrix)) {
29912               if (absdot>absdotmax) {
29913                 absdotmax = absdot;
29914                 dotmax = _dot;
29915                 dmax = d;
29916               }
29917             }
29918           }
29919 
29920           if (!n || method<3 || n%proj_step) {
29921             // Matching Pursuit: Subtract component to signal.
29922             W(x,dmax)+=dotmax;
29923             residual = 0;
29924             cimg_forY(S,y) {
29925               S[y]-=dotmax*D(dmax,y);
29926               residual+=cimg::sqr(S[y]);
29927             }
29928             residual = std::sqrt(residual)/S._height;
29929             is_orthoproj = false;
29930 
29931           } else {
29932             // Orthogonal Matching Pursuit: Orthogonal projection step.
29933             W(x,dmax) = 1; // Used as a marker only.
29934             unsigned int nbW = 0;
29935             cimg_forY(W,d) if (W(x,d)) ++nbW;
29936             CImg<Tfloat> sD(nbW,D._height);
29937             CImg<uintT> inds(nbW);
29938             int sd = 0;
29939             cimg_forY(W,d) if (W(x,d)) {
29940               cimg_forY(sD,y) sD(sd,y) = D(d,y);
29941               inds[sd++] = d;
29942             }
29943             S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights
29944 
29945             // Recompute residual signal.
29946             S = S0;
29947             cimg_forY(sD,k) {
29948               const Tfloat weight = sD[k];
29949               const unsigned int ind = inds[k];
29950               W(x,ind) = weight;
29951               cimg_forY(S,y) S[y]-=weight*D(ind,y);
29952             }
29953             residual = S.magnitude()/S._height;
29954             is_orthoproj = true;
29955           }
29956         }
29957 
29958         // Perform last orthoprojection step if needed.
29959         if (method>=2 && !is_orthoproj) {
29960           unsigned int nbW = 0;
29961           cimg_forY(W,d) if (W(x,d)) ++nbW;
29962           if (nbW) { // Avoid degenerated case where 0 coefs are used
29963             CImg<Tfloat> sD(nbW,D._height);
29964             CImg<uintT> inds(nbW);
29965             int sd = 0;
29966             cimg_forY(W,d) if (W(x,d)) {
29967               cimg_forY(sD,y) sD(sd,y) = D(d,y);
29968               inds[sd++] = d;
29969             }
29970             S0.get_solve(sD,true).move_to(sD);
29971             cimg_forY(sD,k) W(x,inds[k]) = sD[k];
29972           }
29973         }
29974       }
29975 
29976       // Normalize resulting coefficients according to initial (non-normalized) dictionary.
29977       cimg_forXY(W,x,y) W(x,y)/=Dnorm[y];
29978       return W;
29979     }
29980 
29981     //! Compute minimal path in a graph, using the Dijkstra algorithm.
29982     /**
29983        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
29984          between two nodes (i,j).
29985        \param nb_nodes Number of graph nodes.
29986        \param starting_node Index of the starting node.
29987        \param ending_node Index of the ending node (set to ~0U to ignore ending node).
29988        \param previous_node Array that gives the previous node index in the path to the starting node
29989          (optional parameter).
29990        \return Array of distances of each node to the starting node.
29991     **/
29992     template<typename tf, typename t>
29993     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
29994                             const unsigned int starting_node, const unsigned int ending_node,
29995                             CImg<t>& previous_node) {
29996       if (starting_node>=nb_nodes)
29997         throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher "
29998                                     "than number of nodes %u.",
29999                                     pixel_type(),starting_node,nb_nodes);
30000       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
30001       dist(starting_node) = 0;
30002       previous_node.assign(1,nb_nodes,1,1,(t)-1);
30003       previous_node(starting_node) = (t)starting_node;
30004       CImg<uintT> Q(nb_nodes);
30005       cimg_forX(Q,u) Q(u) = (unsigned int)u;
30006       cimg::swap(Q(starting_node),Q(0));
30007       unsigned int sizeQ = nb_nodes;
30008       while (sizeQ) {
30009         // Update neighbors from minimal vertex
30010         const unsigned int umin = Q(0);
30011         if (umin==ending_node) sizeQ = 0;
30012         else {
30013           const T dmin = dist(umin);
30014           const T infty = cimg::type<T>::max();
30015           for (unsigned int q = 1; q<sizeQ; ++q) {
30016             const unsigned int v = Q(q);
30017             const T d = (T)distance(v,umin);
30018             if (d<infty) {
30019               const T alt = dmin + d;
30020               if (alt<dist(v)) {
30021                 dist(v) = alt;
30022                 previous_node(v) = (t)umin;
30023                 const T distpos = dist(Q(q));
30024                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
30025                   cimg::swap(Q(pos),Q(par));
30026               }
30027             }
30028           }
30029           // Remove minimal vertex from queue
30030           Q(0) = Q(--sizeQ);
30031           const T distpos = dist(Q(0));
30032           for (unsigned int pos = 0, left = 0, right = 0;
30033                ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
30034                  (right<sizeQ && distpos>dist(Q(right)));) {
30035             if (right<sizeQ) {
30036               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
30037               else { cimg::swap(Q(pos),Q(right)); pos = right; }
30038             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
30039           }
30040         }
30041       }
30042       return dist;
30043     }
30044 
30045     //! Return minimal path in a graph, using the Dijkstra algorithm.
30046     template<typename tf, typename t>
30047     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
30048                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
30049       CImg<uintT> foo;
30050       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
30051     }
30052 
30053     //! Return minimal path in a graph, using the Dijkstra algorithm.
30054     /**
30055        \param starting_node Index of the starting node.
30056        \param ending_node Index of the ending node.
30057        \param previous_node Array that gives the previous node index in the path to the starting node
30058          (optional parameter).
30059        \return Array of distances of each node to the starting node.
30060        \note image instance corresponds to the adjacency matrix of the graph.
30061     **/
30062     template<typename t>
30063     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
30064                       CImg<t>& previous_node) {
30065       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
30066     }
30067 
30068     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
30069     template<typename t>
30070     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
30071                          CImg<t>& previous_node) const {
30072       if (_width!=_height || _depth!=1 || _spectrum!=1)
30073         throw CImgInstanceException(_cimg_instance
30074                                     "dijkstra(): Instance is not a graph adjacency matrix.",
30075                                     cimg_instance);
30076 
30077       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
30078     }
30079 
30080     //! Return minimal path in a graph, using the Dijkstra algorithm.
30081     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
30082       return get_dijkstra(starting_node,ending_node).move_to(*this);
30083     }
30084 
30085     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
30086     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
30087       CImg<uintT> foo;
30088       return get_dijkstra(starting_node,ending_node,foo);
30089     }
30090 
30091     //! Return an image containing the character codes of specified string.
30092     /**
30093        \param str input C-string to encode as an image.
30094        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
30095        \param is_shared Return result that shares its buffer with \p str.
30096     **/
30097     static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
30098       if (!str) return CImg<T>();
30099       return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
30100     }
30101 
30102     //! Return a \c 1x1 image containing specified value.
30103     /**
30104        \param a0 First vector value.
30105     **/
30106     static CImg<T> row_vector(const T& a0) {
30107       return vector(a0);
30108     }
30109 
30110     //! Return a \c 2x1 image containing specified values.
30111     /**
30112        \param a0 First vector value.
30113        \param a1 Second vector value.
30114     **/
30115     static CImg<T> row_vector(const T& a0, const T& a1) {
30116       CImg<T> r(2,1);
30117       r[0] = a0; r[1] = a1;
30118       return r;
30119     }
30120 
30121     //! Return a \c 3x1 image containing specified values.
30122     /**
30123        \param a0 First vector value.
30124        \param a1 Second vector value.
30125        \param a2 Third vector value.
30126     **/
30127     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2) {
30128       CImg<T> r(3,1);
30129       r[0] = a0; r[1] = a1; r[2] = a2;
30130       return r;
30131     }
30132 
30133     //! Return a \c 4x1 image containing specified values.
30134     /**
30135        \param a0 First vector value.
30136        \param a1 Second vector value.
30137        \param a2 Third vector value.
30138        \param a3 Fourth vector value.
30139     **/
30140     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3) {
30141       CImg<T> r(4,1);
30142       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
30143       return r;
30144     }
30145 
30146     //! Return a \c 5x1 image containing specified values.
30147     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30148       CImg<T> r(5,1);
30149       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
30150       return r;
30151     }
30152 
30153     //! Return a \c 6x1 image containing specified values.
30154     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30155       CImg<T> r(6,1);
30156       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
30157       return r;
30158     }
30159 
30160     //! Return a \c 7x1 image containing specified values.
30161     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30162                               const T& a4, const T& a5, const T& a6) {
30163       CImg<T> r(7,1);
30164       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
30165       return r;
30166     }
30167 
30168     //! Return a \c 8x1 image containing specified values.
30169     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30170                               const T& a4, const T& a5, const T& a6, const T& a7) {
30171       CImg<T> r(8,1);
30172       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
30173       return r;
30174     }
30175 
30176     //! Return a \c 9x1 image containing specified values.
30177     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30178                               const T& a4, const T& a5, const T& a6, const T& a7,
30179                               const T& a8) {
30180       CImg<T> r(9,1);
30181       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8;
30182       return r;
30183     }
30184 
30185     //! Return a \c 10x1 image containing specified values.
30186     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30187                               const T& a4, const T& a5, const T& a6, const T& a7,
30188                               const T& a8, const T& a9) {
30189       CImg<T> r(10,1);
30190       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30191       return r;
30192     }
30193 
30194     //! Return a \c 11x1 image containing specified values.
30195     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30196                               const T& a4, const T& a5, const T& a6, const T& a7,
30197                               const T& a8, const T& a9, const T& a10) {
30198       CImg<T> r(11,1);
30199       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30200       r[10] = a10;
30201       return r;
30202     }
30203 
30204     //! Return a \c 12x1 image containing specified values.
30205     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30206                               const T& a4, const T& a5, const T& a6, const T& a7,
30207                               const T& a8, const T& a9, const T& a10, const T& a11) {
30208       CImg<T> r(12,1);
30209       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30210       r[10] = a10; r[11] = a11;
30211       return r;
30212     }
30213 
30214     //! Return a \c 13x1 image containing specified values.
30215     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30216                               const T& a4, const T& a5, const T& a6, const T& a7,
30217                               const T& a8, const T& a9, const T& a10, const T& a11,
30218                               const T& a12) {
30219       CImg<T> r(13,1);
30220       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30221       r[10] = a10; r[11] = a11; r[12] = a12;
30222       return r;
30223     }
30224 
30225     //! Return a \c 14x1 image containing specified values.
30226     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30227                               const T& a4, const T& a5, const T& a6, const T& a7,
30228                               const T& a8, const T& a9, const T& a10, const T& a11,
30229                               const T& a12, const T& a13) {
30230       CImg<T> r(14,1);
30231       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30232       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
30233       return r;
30234     }
30235 
30236     //! Return a \c 15x1 image containing specified values.
30237     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30238                               const T& a4, const T& a5, const T& a6, const T& a7,
30239                               const T& a8, const T& a9, const T& a10, const T& a11,
30240                               const T& a12, const T& a13, const T& a14) {
30241       CImg<T> r(15,1);
30242       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30243       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
30244       return r;
30245     }
30246 
30247     //! Return a \c 16x1 image containing specified values.
30248     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30249                               const T& a4, const T& a5, const T& a6, const T& a7,
30250                               const T& a8, const T& a9, const T& a10, const T& a11,
30251                               const T& a12, const T& a13, const T& a14, const T& a15) {
30252       CImg<T> r(16,1);
30253       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30254       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
30255       return r;
30256     }
30257 
30258     //! Return a \c 1x1 image containing specified value.
30259     /**
30260        \param a0 First vector value.
30261     **/
30262     static CImg<T> vector(const T& a0) {
30263       CImg<T> r(1,1);
30264       r[0] = a0;
30265       return r;
30266     }
30267 
30268     //! Return a \c 1x2 image containing specified values.
30269     /**
30270        \param a0 First vector value.
30271        \param a1 Second vector value.
30272     **/
30273     static CImg<T> vector(const T& a0, const T& a1) {
30274       CImg<T> r(1,2);
30275       r[0] = a0; r[1] = a1;
30276       return r;
30277     }
30278 
30279     //! Return a \c 1x3 image containing specified values.
30280     /**
30281        \param a0 First vector value.
30282        \param a1 Second vector value.
30283        \param a2 Third vector value.
30284     **/
30285     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
30286       CImg<T> r(1,3);
30287       r[0] = a0; r[1] = a1; r[2] = a2;
30288       return r;
30289     }
30290 
30291     //! Return a \c 1x4 image containing specified values.
30292     /**
30293        \param a0 First vector value.
30294        \param a1 Second vector value.
30295        \param a2 Third vector value.
30296        \param a3 Fourth vector value.
30297     **/
30298     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
30299       CImg<T> r(1,4);
30300       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
30301       return r;
30302     }
30303 
30304     //! Return a \c 1x5 image containing specified values.
30305     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30306       CImg<T> r(1,5);
30307       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
30308       return r;
30309     }
30310 
30311     //! Return a \c 1x6 image containing specified values.
30312     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30313       CImg<T> r(1,6);
30314       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
30315       return r;
30316     }
30317 
30318     //! Return a \c 1x7 image containing specified values.
30319     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30320                           const T& a4, const T& a5, const T& a6) {
30321       CImg<T> r(1,7);
30322       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
30323       return r;
30324     }
30325 
30326     //! Return a \c 1x8 image containing specified values.
30327     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30328                           const T& a4, const T& a5, const T& a6, const T& a7) {
30329       CImg<T> r(1,8);
30330       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
30331       return r;
30332     }
30333 
30334     //! Return a \c 1x9 image containing specified values.
30335     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30336                           const T& a4, const T& a5, const T& a6, const T& a7,
30337                           const T& a8) {
30338       CImg<T> r(1,9);
30339       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8;
30340       return r;
30341     }
30342 
30343     //! Return a \c 1x10 image containing specified values.
30344     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30345                           const T& a4, const T& a5, const T& a6, const T& a7,
30346                           const T& a8, const T& a9) {
30347       CImg<T> r(1,10);
30348       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30349       return r;
30350     }
30351 
30352     //! Return a \c 1x11 image containing specified values.
30353     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30354                           const T& a4, const T& a5, const T& a6, const T& a7,
30355                           const T& a8, const T& a9, const T& a10) {
30356       CImg<T> r(1,11);
30357       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30358       r[10] = a10;
30359       return r;
30360     }
30361 
30362     //! Return a \c 1x12 image containing specified values.
30363     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30364                           const T& a4, const T& a5, const T& a6, const T& a7,
30365                           const T& a8, const T& a9, const T& a10, const T& a11) {
30366       CImg<T> r(1,12);
30367       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30368       r[10] = a10; r[11] = a11;
30369       return r;
30370     }
30371 
30372     //! Return a \c 1x13 image containing specified values.
30373     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30374                           const T& a4, const T& a5, const T& a6, const T& a7,
30375                           const T& a8, const T& a9, const T& a10, const T& a11,
30376                           const T& a12) {
30377       CImg<T> r(1,13);
30378       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30379       r[10] = a10; r[11] = a11; r[12] = a12;
30380       return r;
30381     }
30382 
30383     //! Return a \c 1x14 image containing specified values.
30384     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30385                           const T& a4, const T& a5, const T& a6, const T& a7,
30386                           const T& a8, const T& a9, const T& a10, const T& a11,
30387                           const T& a12, const T& a13) {
30388       CImg<T> r(1,14);
30389       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30390       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
30391       return r;
30392     }
30393 
30394     //! Return a \c 1x15 image containing specified values.
30395     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30396                           const T& a4, const T& a5, const T& a6, const T& a7,
30397                           const T& a8, const T& a9, const T& a10, const T& a11,
30398                           const T& a12, const T& a13, const T& a14) {
30399       CImg<T> r(1,15);
30400       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30401       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
30402       return r;
30403     }
30404 
30405     //! Return a \c 1x16 image containing specified values.
30406     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30407                           const T& a4, const T& a5, const T& a6, const T& a7,
30408                           const T& a8, const T& a9, const T& a10, const T& a11,
30409                           const T& a12, const T& a13, const T& a14, const T& a15) {
30410       CImg<T> r(1,16);
30411       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
30412       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
30413       return r;
30414     }
30415 
30416     //! Return a 1x1 matrix containing specified coefficients.
30417     /**
30418        \param a0 First matrix value.
30419        \note Equivalent to vector(const T&).
30420     **/
30421     static CImg<T> matrix(const T& a0) {
30422       return vector(a0);
30423     }
30424 
30425     //! Return a 2x2 matrix containing specified coefficients.
30426     /**
30427        \param a0 First matrix value.
30428        \param a1 Second matrix value.
30429        \param a2 Third matrix value.
30430        \param a3 Fourth matrix value.
30431     **/
30432     static CImg<T> matrix(const T& a0, const T& a1,
30433                           const T& a2, const T& a3) {
30434       CImg<T> r(2,2); T *ptr = r._data;
30435       *(ptr++) = a0; *(ptr++) = a1;
30436       *(ptr++) = a2; *(ptr++) = a3;
30437       return r;
30438     }
30439 
30440     //! Return a 3x3 matrix containing specified coefficients.
30441     /**
30442        \param a0 First matrix value.
30443        \param a1 Second matrix value.
30444        \param a2 Third matrix value.
30445        \param a3 Fourth matrix value.
30446        \param a4 Fifth matrix value.
30447        \param a5 Sixth matrix value.
30448        \param a6 Seventh matrix value.
30449        \param a7 Eighth matrix value.
30450        \param a8 Ninth matrix value.
30451     **/
30452     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
30453                           const T& a3, const T& a4, const T& a5,
30454                           const T& a6, const T& a7, const T& a8) {
30455       CImg<T> r(3,3); T *ptr = r._data;
30456       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
30457       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
30458       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
30459       return r;
30460     }
30461 
30462     //! Return a 4x4 matrix containing specified coefficients.
30463     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
30464                           const T& a4, const T& a5, const T& a6, const T& a7,
30465                           const T& a8, const T& a9, const T& a10, const T& a11,
30466                           const T& a12, const T& a13, const T& a14, const T& a15) {
30467       CImg<T> r(4,4); T *ptr = r._data;
30468       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
30469       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
30470       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
30471       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
30472       return r;
30473     }
30474 
30475     //! Return a 5x5 matrix containing specified coefficients.
30476     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
30477                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
30478                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
30479                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
30480                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
30481       CImg<T> r(5,5); T *ptr = r._data;
30482       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
30483       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
30484       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
30485       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
30486       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
30487       return r;
30488     }
30489 
30490     //! Return a 1x1 symmetric matrix containing specified coefficients.
30491     /**
30492        \param a0 First matrix value.
30493        \note Equivalent to vector(const T&).
30494     **/
30495     static CImg<T> tensor(const T& a0) {
30496       return matrix(a0);
30497     }
30498 
30499     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
30500     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
30501       return matrix(a0,a1,a1,a2);
30502     }
30503 
30504     //! Return a 3x3 symmetric matrix containing specified coefficients.
30505     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30506       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
30507     }
30508 
30509     //! Return a 1x1 diagonal matrix containing specified coefficients.
30510     static CImg<T> diagonal(const T& a0) {
30511       return matrix(a0);
30512     }
30513 
30514     //! Return a 2x2 diagonal matrix containing specified coefficients.
30515     static CImg<T> diagonal(const T& a0, const T& a1) {
30516       return matrix(a0,0,0,a1);
30517     }
30518 
30519     //! Return a 3x3 diagonal matrix containing specified coefficients.
30520     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
30521       return matrix(a0,0,0,0,a1,0,0,0,a2);
30522     }
30523 
30524     //! Return a 4x4 diagonal matrix containing specified coefficients.
30525     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
30526       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
30527     }
30528 
30529     //! Return a 5x5 diagonal matrix containing specified coefficients.
30530     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30531       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);
30532     }
30533 
30534     //! Return a NxN identity matrix.
30535     /**
30536        \param N Dimension of the matrix.
30537     **/
30538     static CImg<T> identity_matrix(const unsigned int N) {
30539       CImg<T> res(N,N,1,1,0);
30540       cimg_forX(res,x) res(x,x) = 1;
30541       return res;
30542     }
30543 
30544     //! Return a N-numbered sequence vector from \p a0 to \p a1.
30545     /**
30546        \param N Size of the resulting vector.
30547        \param a0 Starting value of the sequence.
30548        \param a1 Ending value of the sequence.
30549      **/
30550     static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
30551       if (N) return CImg<T>(1,N).sequence(a0,a1);
30552       return CImg<T>();
30553     }
30554 
30555     //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
30556     /**
30557        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
30558        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
30559        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
30560        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
30561        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
30562      **/
30563     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
30564                                    const bool is_quaternion=false) {
30565       double X, Y, Z, W, N;
30566       if (is_quaternion) {
30567         N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
30568         if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
30569         else { X = Y = Z = 0; W = 1; }
30570         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),
30571                                (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
30572                                (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
30573       }
30574       N = cimg::hypot((double)x,(double)y,(double)z);
30575       if (N>0) { X = x/N; Y = y/N; Z = z/N; }
30576       else { X = Y = 0; Z = 1; }
30577       const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
30578       return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
30579                              (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
30580                              (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
30581     }
30582 
30583     //@}
30584     //-----------------------------------
30585     //
30586     //! \name Value Manipulation
30587     //@{
30588     //-----------------------------------
30589 
30590     //! Fill all pixel values with specified value.
30591     /**
30592        \param val Fill value.
30593     **/
30594     CImg<T>& fill(const T& val) {
30595       if (is_empty()) return *this;
30596       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
30597       else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
30598       return *this;
30599     }
30600 
30601     //! Fill all pixel values with specified value \newinstance.
30602     CImg<T> get_fill(const T& val) const {
30603       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
30604     }
30605 
30606     //! Fill sequentially all pixel values with specified values.
30607     /**
30608        \param val0 First fill value.
30609        \param val1 Second fill value.
30610     **/
30611     CImg<T>& fill(const T& val0, const T& val1) {
30612       if (is_empty()) return *this;
30613       T *ptrd, *ptre = end() - 1;
30614       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
30615       if (ptrd!=ptre + 1) *(ptrd++) = val0;
30616       return *this;
30617     }
30618 
30619     //! Fill sequentially all pixel values with specified values \newinstance.
30620     CImg<T> get_fill(const T& val0, const T& val1) const {
30621       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
30622     }
30623 
30624     //! Fill sequentially all pixel values with specified values \overloading.
30625     CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
30626       if (is_empty()) return *this;
30627       T *ptrd, *ptre = end() - 2;
30628       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
30629       ptre+=2;
30630       switch (ptre - ptrd) {
30631       case 2 : *(--ptre) = val1; // fallthrough
30632       case 1 : *(--ptre) = val0; // fallthrough
30633       }
30634       return *this;
30635     }
30636 
30637     //! Fill sequentially all pixel values with specified values \newinstance.
30638     CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
30639       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
30640     }
30641 
30642     //! Fill sequentially all pixel values with specified values \overloading.
30643     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
30644       if (is_empty()) return *this;
30645       T *ptrd, *ptre = end() - 3;
30646       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
30647       ptre+=3;
30648       switch (ptre - ptrd) {
30649       case 3 : *(--ptre) = val2; // fallthrough
30650       case 2 : *(--ptre) = val1; // fallthrough
30651       case 1 : *(--ptre) = val0; // fallthrough
30652       }
30653       return *this;
30654     }
30655 
30656     //! Fill sequentially all pixel values with specified values \newinstance.
30657     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
30658       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
30659     }
30660 
30661     //! Fill sequentially all pixel values with specified values \overloading.
30662     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
30663       if (is_empty()) return *this;
30664       T *ptrd, *ptre = end() - 4;
30665       for (ptrd = _data; ptrd<ptre; ) {
30666         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30667       }
30668       ptre+=4;
30669       switch (ptre - ptrd) {
30670       case 4 : *(--ptre) = val3; // fallthrough
30671       case 3 : *(--ptre) = val2; // fallthrough
30672       case 2 : *(--ptre) = val1; // fallthrough
30673       case 1 : *(--ptre) = val0; // fallthrough
30674       }
30675       return *this;
30676     }
30677 
30678     //! Fill sequentially all pixel values with specified values \newinstance.
30679     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
30680       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
30681     }
30682 
30683     //! Fill sequentially all pixel values with specified values \overloading.
30684     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
30685       if (is_empty()) return *this;
30686       T *ptrd, *ptre = end() - 5;
30687       for (ptrd = _data; ptrd<ptre; ) {
30688         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30689       }
30690       ptre+=5;
30691       switch (ptre - ptrd) {
30692       case 5 : *(--ptre) = val4; // fallthrough
30693       case 4 : *(--ptre) = val3; // fallthrough
30694       case 3 : *(--ptre) = val2; // fallthrough
30695       case 2 : *(--ptre) = val1; // fallthrough
30696       case 1 : *(--ptre) = val0; // fallthrough
30697       }
30698       return *this;
30699     }
30700 
30701     //! Fill sequentially all pixel values with specified values \newinstance.
30702     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
30703       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
30704     }
30705 
30706     //! Fill sequentially all pixel values with specified values \overloading.
30707     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30708                   const T& val6) {
30709       if (is_empty()) return *this;
30710       T *ptrd, *ptre = end() - 6;
30711       for (ptrd = _data; ptrd<ptre; ) {
30712         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30713         *(ptrd++) = val6;
30714       }
30715       ptre+=6;
30716       switch (ptre - ptrd) {
30717       case 6 : *(--ptre) = val5; // fallthrough
30718       case 5 : *(--ptre) = val4; // fallthrough
30719       case 4 : *(--ptre) = val3; // fallthrough
30720       case 3 : *(--ptre) = val2; // fallthrough
30721       case 2 : *(--ptre) = val1; // fallthrough
30722       case 1 : *(--ptre) = val0; // fallthrough
30723       }
30724       return *this;
30725     }
30726 
30727     //! Fill sequentially all pixel values with specified values \newinstance.
30728     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30729                      const T& val6) const {
30730       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
30731     }
30732 
30733     //! Fill sequentially all pixel values with specified values \overloading.
30734     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30735                   const T& val6, const T& val7) {
30736       if (is_empty()) return *this;
30737       T *ptrd, *ptre = end() - 7;
30738       for (ptrd = _data; ptrd<ptre; ) {
30739         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
30740         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
30741       }
30742       ptre+=7;
30743       switch (ptre - ptrd) {
30744       case 7 : *(--ptre) = val6; // fallthrough
30745       case 6 : *(--ptre) = val5; // fallthrough
30746       case 5 : *(--ptre) = val4; // fallthrough
30747       case 4 : *(--ptre) = val3; // fallthrough
30748       case 3 : *(--ptre) = val2; // fallthrough
30749       case 2 : *(--ptre) = val1; // fallthrough
30750       case 1 : *(--ptre) = val0; // fallthrough
30751       }
30752       return *this;
30753     }
30754 
30755     //! Fill sequentially all pixel values with specified values \newinstance.
30756     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30757                      const T& val6, const T& val7) const {
30758       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
30759     }
30760 
30761     //! Fill sequentially all pixel values with specified values \overloading.
30762     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30763                   const T& val6, const T& val7, const T& val8) {
30764       if (is_empty()) return *this;
30765       T *ptrd, *ptre = end() - 8;
30766       for (ptrd = _data; ptrd<ptre; ) {
30767         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
30768         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30769         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
30770       }
30771       ptre+=8;
30772       switch (ptre - ptrd) {
30773       case 8 : *(--ptre) = val7; // fallthrough
30774       case 7 : *(--ptre) = val6; // fallthrough
30775       case 6 : *(--ptre) = val5; // fallthrough
30776       case 5 : *(--ptre) = val4; // fallthrough
30777       case 4 : *(--ptre) = val3; // fallthrough
30778       case 3 : *(--ptre) = val2; // fallthrough
30779       case 2 : *(--ptre) = val1; // fallthrough
30780       case 1 : *(--ptre) = val0; // fallthrough
30781       }
30782       return *this;
30783     }
30784 
30785     //! Fill sequentially all pixel values with specified values \newinstance.
30786     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30787                      const T& val6, const T& val7, const T& val8) const {
30788       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
30789     }
30790 
30791     //! Fill sequentially all pixel values with specified values \overloading.
30792     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30793                   const T& val6, const T& val7, const T& val8, const T& val9) {
30794       if (is_empty()) return *this;
30795       T *ptrd, *ptre = end() - 9;
30796       for (ptrd = _data; ptrd<ptre; ) {
30797         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30798         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
30799       }
30800       ptre+=9;
30801       switch (ptre - ptrd) {
30802       case 9 : *(--ptre) = val8; // fallthrough
30803       case 8 : *(--ptre) = val7; // fallthrough
30804       case 7 : *(--ptre) = val6; // fallthrough
30805       case 6 : *(--ptre) = val5; // fallthrough
30806       case 5 : *(--ptre) = val4; // fallthrough
30807       case 4 : *(--ptre) = val3; // fallthrough
30808       case 3 : *(--ptre) = val2; // fallthrough
30809       case 2 : *(--ptre) = val1; // fallthrough
30810       case 1 : *(--ptre) = val0; // fallthrough
30811       }
30812       return *this;
30813     }
30814 
30815     //! Fill sequentially all pixel values with specified values \newinstance.
30816     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30817                      const T& val6, const T& val7, const T& val8, const T& val9) const {
30818       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
30819     }
30820 
30821     //! Fill sequentially all pixel values with specified values \overloading.
30822     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30823                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
30824       if (is_empty()) return *this;
30825       T *ptrd, *ptre = end() - 10;
30826       for (ptrd = _data; ptrd<ptre; ) {
30827         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30828         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
30829         *(ptrd++) = val10;
30830       }
30831       ptre+=10;
30832       switch (ptre - ptrd) {
30833       case 10 : *(--ptre) = val9; // fallthrough
30834       case 9 : *(--ptre) = val8; // fallthrough
30835       case 8 : *(--ptre) = val7; // fallthrough
30836       case 7 : *(--ptre) = val6; // fallthrough
30837       case 6 : *(--ptre) = val5; // fallthrough
30838       case 5 : *(--ptre) = val4; // fallthrough
30839       case 4 : *(--ptre) = val3; // fallthrough
30840       case 3 : *(--ptre) = val2; // fallthrough
30841       case 2 : *(--ptre) = val1; // fallthrough
30842       case 1 : *(--ptre) = val0; // fallthrough
30843       }
30844       return *this;
30845     }
30846 
30847     //! Fill sequentially all pixel values with specified values \newinstance.
30848     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30849                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
30850       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
30851     }
30852 
30853     //! Fill sequentially all pixel values with specified values \overloading.
30854     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30855                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
30856       if (is_empty()) return *this;
30857       T *ptrd, *ptre = end() - 11;
30858       for (ptrd = _data; ptrd<ptre; ) {
30859         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30860         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30861       }
30862       ptre+=11;
30863       switch (ptre - ptrd) {
30864       case 11 : *(--ptre) = val10; // fallthrough
30865       case 10 : *(--ptre) = val9; // fallthrough
30866       case 9 : *(--ptre) = val8; // fallthrough
30867       case 8 : *(--ptre) = val7; // fallthrough
30868       case 7 : *(--ptre) = val6; // fallthrough
30869       case 6 : *(--ptre) = val5; // fallthrough
30870       case 5 : *(--ptre) = val4; // fallthrough
30871       case 4 : *(--ptre) = val3; // fallthrough
30872       case 3 : *(--ptre) = val2; // fallthrough
30873       case 2 : *(--ptre) = val1; // fallthrough
30874       case 1 : *(--ptre) = val0; // fallthrough
30875       }
30876       return *this;
30877     }
30878 
30879     //! Fill sequentially all pixel values with specified values \newinstance.
30880     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30881                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
30882       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30883                                                            val11);
30884     }
30885 
30886     //! Fill sequentially all pixel values with specified values \overloading.
30887     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30888                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30889                   const T& val12) {
30890       if (is_empty()) return *this;
30891       T *ptrd, *ptre = end() - 12;
30892       for (ptrd = _data; ptrd<ptre; ) {
30893         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30894         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30895         *(ptrd++) = val12;
30896       }
30897       ptre+=12;
30898       switch (ptre - ptrd) {
30899       case 12 : *(--ptre) = val11; // fallthrough
30900       case 11 : *(--ptre) = val10; // fallthrough
30901       case 10 : *(--ptre) = val9; // fallthrough
30902       case 9 : *(--ptre) = val8; // fallthrough
30903       case 8 : *(--ptre) = val7; // fallthrough
30904       case 7 : *(--ptre) = val6; // fallthrough
30905       case 6 : *(--ptre) = val5; // fallthrough
30906       case 5 : *(--ptre) = val4; // fallthrough
30907       case 4 : *(--ptre) = val3; // fallthrough
30908       case 3 : *(--ptre) = val2; // fallthrough
30909       case 2 : *(--ptre) = val1; // fallthrough
30910       case 1 : *(--ptre) = val0; // fallthrough
30911       }
30912       return *this;
30913     }
30914 
30915     //! Fill sequentially all pixel values with specified values \newinstance.
30916     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30917                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30918                      const T& val12) const {
30919       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30920                                                            val11,val12);
30921     }
30922 
30923     //! Fill sequentially all pixel values with specified values \overloading.
30924     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30925                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30926                   const T& val12, const T& val13) {
30927       if (is_empty()) return *this;
30928       T *ptrd, *ptre = end() - 13;
30929       for (ptrd = _data; ptrd<ptre; ) {
30930         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30931         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30932         *(ptrd++) = val12; *(ptrd++) = val13;
30933       }
30934       ptre+=13;
30935       switch (ptre - ptrd) {
30936       case 13 : *(--ptre) = val12; // fallthrough
30937       case 12 : *(--ptre) = val11; // fallthrough
30938       case 11 : *(--ptre) = val10; // fallthrough
30939       case 10 : *(--ptre) = val9; // fallthrough
30940       case 9 : *(--ptre) = val8; // fallthrough
30941       case 8 : *(--ptre) = val7; // fallthrough
30942       case 7 : *(--ptre) = val6; // fallthrough
30943       case 6 : *(--ptre) = val5; // fallthrough
30944       case 5 : *(--ptre) = val4; // fallthrough
30945       case 4 : *(--ptre) = val3; // fallthrough
30946       case 3 : *(--ptre) = val2; // fallthrough
30947       case 2 : *(--ptre) = val1; // fallthrough
30948       case 1 : *(--ptre) = val0; // fallthrough
30949       }
30950       return *this;
30951     }
30952 
30953     //! Fill sequentially all pixel values with specified values \newinstance.
30954     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30955                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30956                      const T& val12, const T& val13) const {
30957       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30958                                                            val11,val12,val13);
30959     }
30960 
30961     //! Fill sequentially all pixel values with specified values \overloading.
30962     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30963                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30964                   const T& val12, const T& val13, const T& val14) {
30965       if (is_empty()) return *this;
30966       T *ptrd, *ptre = end() - 14;
30967       for (ptrd = _data; ptrd<ptre; ) {
30968         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30969         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30970         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
30971       }
30972       ptre+=14;
30973       switch (ptre - ptrd) {
30974       case 14 : *(--ptre) = val13; // fallthrough
30975       case 13 : *(--ptre) = val12; // fallthrough
30976       case 12 : *(--ptre) = val11; // fallthrough
30977       case 11 : *(--ptre) = val10; // fallthrough
30978       case 10 : *(--ptre) = val9; // fallthrough
30979       case 9 : *(--ptre) = val8; // fallthrough
30980       case 8 : *(--ptre) = val7; // fallthrough
30981       case 7 : *(--ptre) = val6; // fallthrough
30982       case 6 : *(--ptre) = val5; // fallthrough
30983       case 5 : *(--ptre) = val4; // fallthrough
30984       case 4 : *(--ptre) = val3; // fallthrough
30985       case 3 : *(--ptre) = val2; // fallthrough
30986       case 2 : *(--ptre) = val1; // fallthrough
30987       case 1 : *(--ptre) = val0; // fallthrough
30988       }
30989       return *this;
30990     }
30991 
30992     //! Fill sequentially all pixel values with specified values \newinstance.
30993     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30994                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30995                      const T& val12, const T& val13, const T& val14) const {
30996       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30997                                                            val11,val12,val13,val14);
30998     }
30999 
31000     //! Fill sequentially all pixel values with specified values \overloading.
31001     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31002                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31003                   const T& val12, const T& val13, const T& val14, const T& val15) {
31004       if (is_empty()) return *this;
31005       T *ptrd, *ptre = end() - 15;
31006       for (ptrd = _data; ptrd<ptre; ) {
31007         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31008         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31009         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
31010       }
31011       ptre+=15;
31012       switch (ptre - ptrd) {
31013       case 15 : *(--ptre) = val14; // fallthrough
31014       case 14 : *(--ptre) = val13; // fallthrough
31015       case 13 : *(--ptre) = val12; // fallthrough
31016       case 12 : *(--ptre) = val11; // fallthrough
31017       case 11 : *(--ptre) = val10; // fallthrough
31018       case 10 : *(--ptre) = val9; // fallthrough
31019       case 9 : *(--ptre) = val8; // fallthrough
31020       case 8 : *(--ptre) = val7; // fallthrough
31021       case 7 : *(--ptre) = val6; // fallthrough
31022       case 6 : *(--ptre) = val5; // fallthrough
31023       case 5 : *(--ptre) = val4; // fallthrough
31024       case 4 : *(--ptre) = val3; // fallthrough
31025       case 3 : *(--ptre) = val2; // fallthrough
31026       case 2 : *(--ptre) = val1; // fallthrough
31027       case 1 : *(--ptre) = val0; // fallthrough
31028       }
31029       return *this;
31030     }
31031 
31032     //! Fill sequentially all pixel values with specified values \newinstance.
31033     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31034                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31035                      const T& val12, const T& val13, const T& val14, const T& val15) const {
31036       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31037                                                            val11,val12,val13,val14,val15);
31038     }
31039 
31040     //! Fill sequentially pixel values according to a given expression.
31041     /**
31042        \param expression C-string describing a math formula, or a sequence of values.
31043        \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
31044        \param allow_formula Tells that mathematical formulas are authorized for the filling.
31045        \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
31046        \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
31047     **/
31048     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
31049                   const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
31050       return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0);
31051     }
31052 
31053     // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula |
31054     //                    2 = allow formula and do not fill image values }.
31055     CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode,
31056                    const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
31057                    const char *const calling_function, const CImg<T> *provides_copy) {
31058       if (is_empty() || !expression || !*expression) return *this;
31059       const unsigned int omode = cimg::exception_mode();
31060       cimg::exception_mode(0);
31061       CImg<charT> is_error;
31062       bool is_value_sequence = false;
31063       cimg_abort_init;
31064 
31065       if (formula_mode) {
31066 
31067         // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
31068         double value;
31069         char sep;
31070         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
31071         if (err==1 || (err==2 && sep==',')) {
31072           if (err==1) { if (formula_mode==2) return *this; return fill((T)value); }
31073           else is_value_sequence = true;
31074         }
31075 
31076         // Try to fill values according to a formula.
31077         _cimg_abort_init_openmp;
31078         if (!is_value_sequence) try {
31079             CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
31080             _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
31081                                                *expression=='*' || *expression==':'),
31082                                  calling_function,base,this,list_inputs,list_outputs,true);
31083             if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
31084                 mp.need_input_copy)
31085               base.assign().assign(*this,false); // Needs input copy
31086 
31087             // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation).
31088             unsigned int M;
31089             if (mp.result_dim) {
31090               M = cimg::max(_width,_height,_depth);
31091               M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height);
31092             } else {
31093               M = cimg::max(_width,_height,_depth,_spectrum);
31094               M = M==_width?cimg::max(_height,_depth,_spectrum):
31095                 M==_height?cimg::max(_width,_depth,_spectrum):
31096                 M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth);
31097             }
31098 
31099             bool do_in_parallel = false;
31100 #if cimg_use_openmp!=0
31101             cimg_openmp_if(*expression=='*' || *expression==':' ||
31102                            (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))
31103               do_in_parallel = true;
31104 #endif
31105             if (mp.result_dim) { // Vector-valued expression
31106               const unsigned int N = std::min(mp.result_dim,_spectrum);
31107               const ulongT whd = (ulongT)_width*_height*_depth;
31108               T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
31109               if (*expression=='<') {
31110                 CImg<doubleT> res(1,mp.result_dim);
31111                 mp.begin_t();
31112                 cimg_rofYZ(*this,y,z) {
31113                   cimg_abort_test;
31114                   if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0);
31115                   else cimg_rofX(*this,x) {
31116                       mp(x,y,z,0,res._data);
31117                       const double *ptrs = res._data;
31118                       T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
31119                     }
31120                 }
31121                 mp.end_t();
31122 
31123               } else if (*expression=='>' || !do_in_parallel) {
31124                 CImg<doubleT> res(1,mp.result_dim);
31125                 mp.begin_t();
31126                 cimg_forYZ(*this,y,z) {
31127                   cimg_abort_test;
31128                   if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0);
31129                   else cimg_forX(*this,x) {
31130                       mp(x,y,z,0,res._data);
31131                       const double *ptrs = res._data;
31132                       T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
31133                     }
31134                 }
31135                 mp.end_t();
31136 
31137               } else {
31138 
31139 #if cimg_use_openmp!=0
31140                 cimg_pragma_openmp(parallel)
31141                 {
31142                   _cimg_math_parser
31143                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
31144                     &lmp = *_mp;
31145                   lmp.is_fill = true;
31146                   cimg_pragma_openmp(barrier)
31147                   lmp.begin_t();
31148 
31149 #define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \
31150   cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
31151   cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
31152     cimg_abort_test; \
31153     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
31154     else { \
31155       CImg<doubleT> res(1,lmp.result_dim); \
31156       T *__ptrd = data(_sx,_sy,_sz,0); \
31157       const ulongT off = (ulongT)_off; \
31158       cimg_for##_X(*this,_x) { \
31159         lmp(x,y,z,0,res._data); \
31160         const double *ptrs = res._data; \
31161         T *_ptrd = __ptrd; \
31162         for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \
31163         __ptrd+=off; \
31164       } \
31165     } \
31166   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
31167 
31168                   if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
31169                   else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
31170                   else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
31171 
31172                   lmp.end_t();
31173                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
31174                   if (&lmp!=&mp) delete &lmp;
31175                 }
31176 #endif
31177               }
31178 
31179             } else { // Scalar-valued expression
31180               T *ptrd = *expression=='<'?end() - 1:_data;
31181               if (*expression=='<') {
31182                 mp.begin_t();
31183                 if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
31184                 else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
31185                 mp.end_t();
31186 
31187               } else if (*expression=='>' || !do_in_parallel) {
31188                 mp.begin_t();
31189                 if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
31190                 else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
31191                 mp.end_t();
31192 
31193               } else {
31194 
31195 #if cimg_use_openmp!=0
31196                 cimg_pragma_openmp(parallel)
31197                 {
31198                   _cimg_math_parser
31199                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
31200                     &lmp = *_mp;
31201                   lmp.is_fill = true;
31202                   cimg_pragma_openmp(barrier)
31203                   lmp.begin_t();
31204 
31205 #define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \
31206   cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
31207   cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
31208     cimg_abort_test; \
31209     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
31210     else { \
31211       T *_ptrd = data(_sx,_sy,_sz,_sc); \
31212       const ulongT off = (ulongT)_off; \
31213       cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \
31214     } \
31215   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
31216 
31217                   if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
31218                   else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
31219                   else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
31220                   else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
31221 
31222                   lmp.end_t();
31223                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
31224                   if (&lmp!=&mp) delete &lmp;
31225                 }
31226 #endif
31227               }
31228             }
31229             mp.end();
31230           } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
31231       }
31232 
31233       // Try to fill values according to a value sequence.
31234       if (!formula_mode || is_value_sequence || is_error) {
31235         CImg<charT> item(256);
31236         char sep = 0;
31237         const char *nexpression = expression;
31238         ulongT nb = 0;
31239         const ulongT siz = size();
31240         T *ptrd = _data;
31241         for (double val = 0; *nexpression && nb<siz; ++nb) {
31242           sep = 0;
31243           const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
31244           if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
31245             nexpression+=std::strlen(item) + (err>1);
31246             *(ptrd++) = (T)val;
31247           } else break;
31248         }
31249         cimg::exception_mode(omode);
31250         if (nb<siz && (sep || *nexpression)) {
31251           if (is_error) throw CImgArgumentException("%s",is_error._data);
31252           else throw CImgArgumentException(_cimg_instance
31253                                            "%s(): Invalid sequence of filling values '%s'.",
31254                                            cimg_instance,calling_function,expression);
31255         }
31256         if (repeat_values && nb && nb<siz)
31257           for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
31258       }
31259 
31260       cimg::exception_mode(omode);
31261       cimg_abort_test;
31262       return *this;
31263     }
31264 
31265     //! Fill sequentially pixel values according to a given expression \newinstance.
31266     CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
31267                      const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
31268       return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs);
31269     }
31270 
31271     //! Fill sequentially pixel values according to the values found in another image.
31272     /**
31273        \param values Image containing the values used for the filling.
31274        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
31275          repeated for the filling.
31276     **/
31277     template<typename t>
31278     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
31279       if (is_empty() || !values) return *this;
31280       T *ptrd = _data, *ptre = ptrd + size();
31281       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
31282         *(ptrd++) = (T)*ptrs;
31283       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
31284       return *this;
31285     }
31286 
31287     //! Fill sequentially pixel values according to the values found in another image \newinstance.
31288     template<typename t>
31289     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
31290       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
31291         (+*this).fill(values,repeat_values);
31292     }
31293 
31294     //! Fill pixel values along the X-axis at a specified pixel position.
31295     /**
31296        \param y Y-coordinate of the filled column.
31297        \param z Z-coordinate of the filled column.
31298        \param c C-coordinate of the filled column.
31299        \param a0 First fill value.
31300     **/
31301     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
31302 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
31303     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
31304     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
31305     va_end(ap); }
31306       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
31307       return *this;
31308     }
31309 
31310     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
31311     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
31312       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
31313       return *this;
31314     }
31315 
31316     //! Fill pixel values along the Y-axis at a specified pixel position.
31317     /**
31318        \param x X-coordinate of the filled row.
31319        \param z Z-coordinate of the filled row.
31320        \param c C-coordinate of the filled row.
31321        \param a0 First fill value.
31322     **/
31323     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
31324       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
31325       return *this;
31326     }
31327 
31328     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
31329     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
31330       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
31331       return *this;
31332     }
31333 
31334     //! Fill pixel values along the Z-axis at a specified pixel position.
31335     /**
31336        \param x X-coordinate of the filled slice.
31337        \param y Y-coordinate of the filled slice.
31338        \param c C-coordinate of the filled slice.
31339        \param a0 First fill value.
31340     **/
31341     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
31342       const ulongT wh = (ulongT)_width*_height;
31343       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
31344       return *this;
31345     }
31346 
31347     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
31348     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
31349       const ulongT wh = (ulongT)_width*_height;
31350       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
31351       return *this;
31352     }
31353 
31354     //! Fill pixel values along the C-axis at a specified pixel position.
31355     /**
31356        \param x X-coordinate of the filled channel.
31357        \param y Y-coordinate of the filled channel.
31358        \param z Z-coordinate of the filled channel.
31359        \param a0 First filling value.
31360     **/
31361     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
31362       const ulongT whd = (ulongT)_width*_height*_depth;
31363       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
31364       return *this;
31365     }
31366 
31367     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
31368     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
31369       const ulongT whd = (ulongT)_width*_height*_depth;
31370       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
31371       return *this;
31372     }
31373 
31374     //! Discard specified sequence of values in the image buffer, along a specific axis.
31375     /**
31376        \param values Sequence of values to discard.
31377        \param axis Axis along which the values are discarded. If set to \c 0 (default value)
31378          the method does it for all the buffer values and returns a one-column vector.
31379        \note Discarded values will change the image geometry, so the resulting image
31380          is returned as a one-column vector.
31381     **/
31382     template<typename t>
31383     CImg<T>& discard(const CImg<t>& values, const char axis=0) {
31384       if (is_empty() || !values) return *this;
31385       return get_discard(values,axis).move_to(*this);
31386     }
31387 
31388     template<typename t>
31389     CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
31390       if (!values) return +*this;
31391       CImg<T> res;
31392       if (is_empty()) return res;
31393       const ulongT vsiz = values.size();
31394       const char _axis = cimg::lowercase(axis);
31395       ulongT j = 0;
31396       unsigned int k = 0;
31397       int i0 = 0;
31398       res.assign(width(),height(),depth(),spectrum());
31399       switch (_axis) {
31400       case 'x' : {
31401         cimg_forX(*this,i) {
31402           if ((*this)(i)!=(T)values[j]) {
31403             if (j) --i;
31404             res.draw_image(k,get_columns(i0,i));
31405             k+=i - i0 + 1; i0 = i + 1; j = 0;
31406           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31407         }
31408         if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
31409         res.resize(k,-100,-100,-100,0);
31410       } break;
31411       case 'y' : {
31412         cimg_forY(*this,i) {
31413           if ((*this)(0,i)!=(T)values[j]) {
31414             if (j) --i;
31415             res.draw_image(0,k,get_rows(i0,i));
31416             k+=i - i0 + 1; i0 = i + 1; j = 0;
31417           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31418         }
31419         if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
31420         res.resize(-100,k,-100,-100,0);
31421       } break;
31422       case 'z' : {
31423         cimg_forZ(*this,i) {
31424           if ((*this)(0,0,i)!=(T)values[j]) {
31425             if (j) --i;
31426             res.draw_image(0,0,k,get_slices(i0,i));
31427             k+=i - i0 + 1; i0 = i + 1; j = 0;
31428           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31429         }
31430         if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
31431         res.resize(-100,-100,k,-100,0);
31432       } break;
31433       case 'c' : {
31434         cimg_forC(*this,i) {
31435           if ((*this)(0,0,0,i)!=(T)values[j]) {
31436             if (j) --i;
31437             res.draw_image(0,0,0,k,get_channels(i0,i));
31438             k+=i - i0 + 1; i0 = i + 1; j = 0;
31439           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31440         }
31441         if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
31442         res.resize(-100,-100,-100,k,0);
31443       } break;
31444       default : {
31445         const ulongT siz = size();
31446         res.unroll('y');
31447         if (vsiz==1) { // Optimized version for a single discard value
31448           const T val = (T)values[0];
31449           cimg_foroff(*this,i) {
31450             const T _val = (T)_data[i];
31451             if (_val!=val) res[k++] = _val;
31452           }
31453         } else { // Generic version
31454           cimg_foroff(*this,i) {
31455             if ((*this)[i]!=(T)values[j]) {
31456               if (j) --i;
31457               std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
31458               k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
31459             } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
31460           }
31461           if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
31462         }
31463         res.resize(1,k,1,1,0);
31464       }
31465       }
31466       return res;
31467     }
31468 
31469     //! Discard neighboring duplicates in the image buffer, along the specified axis.
31470     CImg<T>& discard(const char axis=0) {
31471       return get_discard(axis).move_to(*this);
31472     }
31473 
31474     //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
31475     CImg<T> get_discard(const char axis=0) const {
31476       CImg<T> res;
31477       if (is_empty()) return res;
31478       const char _axis = cimg::lowercase(axis);
31479       T current = *_data?(T)0:(T)1;
31480       int j = 0;
31481       res.assign(width(),height(),depth(),spectrum());
31482       switch (_axis) {
31483       case 'x' : {
31484         cimg_forX(*this,i)
31485           if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
31486         res.resize(j,-100,-100,-100,0);
31487       } break;
31488       case 'y' : {
31489         cimg_forY(*this,i)
31490           if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
31491         res.resize(-100,j,-100,-100,0);
31492       } break;
31493       case 'z' : {
31494         cimg_forZ(*this,i)
31495           if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
31496         res.resize(-100,-100,j,-100,0);
31497       } break;
31498       case 'c' : {
31499         cimg_forC(*this,i)
31500           if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
31501         res.resize(-100,-100,-100,j,0);
31502       } break;
31503       default : {
31504         res.unroll('y');
31505         cimg_foroff(*this,i) {
31506           const T val = (*this)[i];
31507           if (val!=current) res[j++] = current = val;
31508         }
31509         res.resize(-100,j,-100,-100,0);
31510       }
31511       }
31512       return res;
31513     }
31514 
31515     //! Invert endianness of all pixel values.
31516     /**
31517      **/
31518     CImg<T>& invert_endianness() {
31519       cimg::invert_endianness(_data,size());
31520       return *this;
31521     }
31522 
31523     //! Invert endianness of all pixel values \newinstance.
31524     CImg<T> get_invert_endianness() const {
31525       return (+*this).invert_endianness();
31526     }
31527 
31528     //! Fill image with random values in specified range.
31529     /**
31530        \param val_min Minimal authorized random value.
31531        \param val_max Maximal authorized random value.
31532        \note Random variables are uniformly distributed in [val_min,val_max].
31533      **/
31534     CImg<T>& rand(const T& val_min, const T& val_max) {
31535       const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
31536       if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31537           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31538 
31539 #if cimg_use_openmp!=0
31540           rng+=omp_get_thread_num();
31541 #endif
31542           cimg_pragma_openmp(for)
31543             cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng));
31544           cimg::srand(rng);
31545         } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31546           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31547 
31548 #if cimg_use_openmp!=0
31549           rng+=omp_get_thread_num();
31550 #endif
31551           cimg_pragma_openmp(for)
31552             cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng)));
31553           cimg::srand(rng);
31554         }
31555       return *this;
31556     }
31557 
31558     //! Fill image with random values in specified range \newinstance.
31559     CImg<T> get_rand(const T& val_min, const T& val_max) const {
31560       return (+*this).rand(val_min,val_max);
31561     }
31562 
31563     //! Round pixel values.
31564     /**
31565        \param y Rounding precision.
31566        \param rounding_type Rounding type. Can be:
31567        - \c -1: Backward.
31568        - \c 0: Nearest.
31569        - \c 1: Forward.
31570     **/
31571     CImg<T>& round(const double y=1, const int rounding_type=0) {
31572       if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192);
31573       return *this;
31574     }
31575 
31576     //! Round pixel values \newinstance.
31577     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
31578       return (+*this).round(y,rounding_type);
31579     }
31580 
31581     //! Add random noise to pixel values.
31582     /**
31583        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
31584          global value range.
31585        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
31586          \p 3=Poisson or \p 4=Rician).
31587        \return A reference to the modified image instance.
31588        \note
31589        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
31590          the image value itself.
31591        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
31592        \par Example
31593        \code
31594        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
31595        (img,res.normalize(0,255)).display();
31596        \endcode
31597        \image html ref_noise.jpg
31598     **/
31599     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
31600       if (is_empty()) return *this;
31601       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
31602       Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
31603       if (nsigma==0 && noise_type!=3) return *this;
31604       if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
31605       if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.);
31606       switch (noise_type) {
31607       case 0 : { // Gaussian noise
31608         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31609           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31610 
31611 #if cimg_use_openmp!=0
31612           rng+=omp_get_thread_num();
31613 #endif
31614           cimg_pragma_openmp(for)
31615             cimg_rofoff(*this,off) {
31616             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng));
31617             if (val>vmax) val = vmax;
31618             if (val<vmin) val = vmin;
31619             _data[off] = (T)val;
31620           }
31621           cimg::srand(rng);
31622         }
31623       } break;
31624       case 1 : { // Uniform noise
31625         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31626           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31627 
31628 #if cimg_use_openmp!=0
31629           rng+=omp_get_thread_num();
31630 #endif
31631           cimg_pragma_openmp(for)
31632             cimg_rofoff(*this,off) {
31633             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng));
31634             if (val>vmax) val = vmax;
31635             if (val<vmin) val = vmin;
31636             _data[off] = (T)val;
31637           }
31638           cimg::srand(rng);
31639         }
31640       } break;
31641       case 2 : { // Salt & Pepper noise
31642         if (nsigma<0) nsigma = -nsigma;
31643         if (M==m) {
31644           if (cimg::type<T>::is_float()) { --m; ++M; }
31645           else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
31646         }
31647         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31648           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31649 
31650 #if cimg_use_openmp!=0
31651           rng+=omp_get_thread_num();
31652 #endif
31653           cimg_pragma_openmp(for)
31654             cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m);
31655           cimg::srand(rng);
31656           }
31657       } break;
31658       case 3 : { // Poisson Noise
31659         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31660           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31661 
31662 #if cimg_use_openmp!=0
31663           rng+=omp_get_thread_num();
31664 #endif
31665           cimg_pragma_openmp(for)
31666             cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng);
31667           cimg::srand(rng);
31668         }
31669       } break;
31670       case 4 : { // Rice noise
31671         const Tfloat sqrt2 = (Tfloat)std::sqrt(2.);
31672         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31673           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31674 
31675 #if cimg_use_openmp!=0
31676           rng+=omp_get_thread_num();
31677 #endif
31678           cimg_pragma_openmp(for)
31679             cimg_rofoff(*this,off) {
31680             const Tfloat
31681               val0 = (Tfloat)_data[off]/sqrt2,
31682               re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)),
31683               im = (Tfloat)(val0 + nsigma*cimg::grand(&rng));
31684             Tfloat val = cimg::hypot(re,im);
31685             if (val>vmax) val = vmax;
31686             if (val<vmin) val = vmin;
31687             _data[off] = (T)val;
31688           }
31689           cimg::srand(rng);
31690         }
31691       } break;
31692       default :
31693         throw CImgArgumentException(_cimg_instance
31694                                     "noise(): Invalid specified noise type %d "
31695                                     "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
31696                                     cimg_instance,
31697                                     noise_type);
31698       }
31699       return *this;
31700     }
31701 
31702     //! Add random noise to pixel values \newinstance.
31703     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
31704       return (+*this).noise(sigma,noise_type);
31705     }
31706 
31707     //! Linearly normalize pixel values.
31708     /**
31709        \param min_value Minimum desired value of the resulting image.
31710        \param max_value Maximum desired value of the resulting image.
31711        \param constant_case_ratio In case of instance image having a constant value, tell what ratio
31712               of [min_value,max_value] is used to fill the normalized image
31713               (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2).
31714        \par Example
31715        \code
31716        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
31717        (img,res).display();
31718        \endcode
31719        \image html ref_normalize2.jpg
31720     **/
31721     CImg<T>& normalize(const T& min_value, const T& max_value,
31722                        const float constant_case_ratio=0) {
31723       if (is_empty()) return *this;
31724       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
31725       T m, M = max_min(m);
31726       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
31727       if (m==M)
31728         return fill(constant_case_ratio==0?a:
31729                     constant_case_ratio==1?b:
31730                     (T)((1 - constant_case_ratio)*a + constant_case_ratio*b));
31731       if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
31732       return *this;
31733     }
31734 
31735     //! Linearly normalize pixel values \newinstance.
31736     CImg<Tfloat> get_normalize(const T& min_value, const T& max_value,
31737                                const float ratio_if_constant_image=0) const {
31738       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image);
31739     }
31740 
31741     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
31742     /**
31743        \par Example
31744        \code
31745        const CImg<float> img("reference.jpg"), res = img.get_normalize();
31746        (img,res.normalize(0,255)).display();
31747        \endcode
31748        \image html ref_normalize.jpg
31749     **/
31750     CImg<T>& normalize() {
31751       const ulongT whd = (ulongT)_width*_height*_depth;
31752       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31753                                                                  _height*_depth>=16))
31754       cimg_forYZ(*this,y,z) {
31755         T *ptrd = data(0,y,z,0);
31756         cimg_forX(*this,x) {
31757           const T *ptrs = ptrd;
31758           float n = 0;
31759           cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
31760           n = (float)std::sqrt(n);
31761           T *_ptrd = ptrd++;
31762           if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
31763           else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
31764         }
31765       }
31766       return *this;
31767     }
31768 
31769     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
31770     CImg<Tfloat> get_normalize() const {
31771       return CImg<Tfloat>(*this,false).normalize();
31772     }
31773 
31774     //! Compute Lp-norm of each multi-valued pixel of the image instance.
31775     /**
31776        \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
31777        \par Example
31778        \code
31779        const CImg<float> img("reference.jpg"), res = img.get_norm();
31780        (img,res.normalize(0,255)).display();
31781        \endcode
31782        \image html ref_norm.jpg
31783     **/
31784     CImg<T>& norm(const int norm_type=2) {
31785       if (_spectrum==1 && norm_type) return abs();
31786       return get_norm(norm_type).move_to(*this);
31787     }
31788 
31789     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
31790     CImg<Tfloat> get_norm(const int norm_type=2) const {
31791       if (is_empty()) return *this;
31792       if (_spectrum==1 && norm_type) return get_abs();
31793       const ulongT whd = (ulongT)_width*_height*_depth;
31794       CImg<Tfloat> res(_width,_height,_depth);
31795       switch (norm_type) {
31796       case -1 : { // Linf-norm
31797         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31798                                                                    _height*_depth>=16))
31799         cimg_forYZ(*this,y,z) {
31800           const ulongT off = (ulongT)offset(0,y,z);
31801           const T *ptrs = _data + off;
31802           Tfloat *ptrd = res._data + off;
31803           cimg_forX(*this,x) {
31804             Tfloat n = 0;
31805             const T *_ptrs = ptrs++;
31806             cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
31807             *(ptrd++) = n;
31808           }
31809         }
31810       } break;
31811       case 0 : { // L0-norm
31812         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31813                                                                    _height*_depth>=16))
31814         cimg_forYZ(*this,y,z) {
31815           const ulongT off = (ulongT)offset(0,y,z);
31816           const T *ptrs = _data + off;
31817           Tfloat *ptrd = res._data + off;
31818           cimg_forX(*this,x) {
31819             unsigned int n = 0;
31820             const T *_ptrs = ptrs++;
31821             cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
31822             *(ptrd++) = (Tfloat)n;
31823           }
31824         }
31825       } break;
31826       case 1 : { // L1-norm
31827         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31828                                                                    _height*_depth>=16))
31829         cimg_forYZ(*this,y,z) {
31830           const ulongT off = (ulongT)offset(0,y,z);
31831           const T *ptrs = _data + off;
31832           Tfloat *ptrd = res._data + off;
31833           cimg_forX(*this,x) {
31834             Tfloat n = 0;
31835             const T *_ptrs = ptrs++;
31836             cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
31837             *(ptrd++) = n;
31838           }
31839         }
31840       } break;
31841       case 2 : { // L2-norm
31842         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31843                                                                    _height*_depth>=16))
31844         cimg_forYZ(*this,y,z) {
31845           const ulongT off = (ulongT)offset(0,y,z);
31846           const T *ptrs = _data + off;
31847           Tfloat *ptrd = res._data + off;
31848           cimg_forX(*this,x) {
31849             Tfloat n = 0;
31850             const T *_ptrs = ptrs++;
31851             cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
31852             *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
31853           }
31854         }
31855       } break;
31856       default : { // Linf-norm
31857         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31858                                                                    _height*_depth>=16))
31859         cimg_forYZ(*this,y,z) {
31860           const ulongT off = (ulongT)offset(0,y,z);
31861           const T *ptrs = _data + off;
31862           Tfloat *ptrd = res._data + off;
31863           cimg_forX(*this,x) {
31864             Tfloat n = 0;
31865             const T *_ptrs = ptrs++;
31866             cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
31867             *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
31868           }
31869         }
31870       }
31871       }
31872       return res;
31873     }
31874 
31875     //! Cut pixel values in specified range.
31876     /**
31877        \param min_value Minimum desired value of the resulting image.
31878        \param max_value Maximum desired value of the resulting image.
31879        \par Example
31880        \code
31881        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
31882        (img,res).display();
31883        \endcode
31884        \image html ref_cut.jpg
31885     **/
31886     CImg<T>& cut(const T& min_value, const T& max_value) {
31887       if (is_empty()) return *this;
31888       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
31889       cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768);
31890       return *this;
31891     }
31892 
31893     //! Cut pixel values in specified range \newinstance.
31894     CImg<T> get_cut(const T& min_value, const T& max_value) const {
31895       return (+*this).cut(min_value,max_value);
31896     }
31897 
31898     //! Uniformly quantize pixel values.
31899     /**
31900        \param nb_levels Number of quantization levels.
31901        \param keep_range Tells if resulting values keep the same range as the original ones.
31902        \par Example
31903        \code
31904        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
31905        (img,res).display();
31906        \endcode
31907        \image html ref_quantize.jpg
31908     **/
31909     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
31910       if (!nb_levels)
31911         throw CImgArgumentException(_cimg_instance
31912                                     "quantize(): Invalid quantization request with 0 values.",
31913                                     cimg_instance);
31914 
31915       if (is_empty()) return *this;
31916       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
31917       if (range>0) {
31918         if (keep_range)
31919           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31920           cimg_rofoff(*this,off) {
31921             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
31922             _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
31923           } else
31924           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31925           cimg_rofoff(*this,off) {
31926             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
31927             _data[off] = (T)std::min(val,nb_levels - 1);
31928           }
31929       }
31930       return *this;
31931     }
31932 
31933     //! Uniformly quantize pixel values \newinstance.
31934     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
31935       return (+*this).quantize(n,keep_range);
31936     }
31937 
31938     //! Threshold pixel values.
31939     /**
31940        \param value Threshold value
31941        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
31942        \param strict_threshold Tells if threshold value is strict.
31943        \par Example
31944        \code
31945        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
31946        (img,res.normalize(0,255)).display();
31947        \endcode
31948        \image html ref_threshold.jpg
31949     **/
31950     CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
31951       if (is_empty()) return *this;
31952       if (strict_threshold) {
31953         if (soft_threshold)
31954           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31955           cimg_rofoff(*this,off) {
31956             const T v = _data[off];
31957             _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
31958           }
31959         else
31960           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
31961           cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
31962       } else {
31963         if (soft_threshold)
31964           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31965           cimg_rofoff(*this,off) {
31966             const T v = _data[off];
31967             _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
31968           }
31969         else
31970           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
31971           cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
31972       }
31973       return *this;
31974     }
31975 
31976     //! Threshold pixel values \newinstance.
31977     CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
31978       return (+*this).threshold(value,soft_threshold,strict_threshold);
31979     }
31980 
31981     //! Compute the histogram of pixel values.
31982     /**
31983        \param nb_levels Number of desired histogram levels.
31984        \param min_value Minimum pixel value considered for the histogram computation.
31985          All pixel values lower than \p min_value will not be counted.
31986        \param max_value Maximum pixel value considered for the histogram computation.
31987          All pixel values higher than \p max_value will not be counted.
31988        \note
31989        - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x
31990          in the image I.
31991        - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional.
31992        \par Example
31993        \code
31994        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
31995        img.display_graph(0,3);
31996        \endcode
31997        \image html ref_histogram.jpg
31998     **/
31999     CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
32000       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
32001     }
32002 
32003     //! Compute the histogram of pixel values \overloading.
32004     CImg<T>& histogram(const unsigned int nb_levels) {
32005       return get_histogram(nb_levels).move_to(*this);
32006     }
32007 
32008     //! Compute the histogram of pixel values \newinstance.
32009     CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
32010       if (!nb_levels || is_empty()) return CImg<ulongT>();
32011       const double
32012         vmin = (double)(min_value<max_value?min_value:max_value),
32013         vmax = (double)(min_value<max_value?max_value:min_value);
32014       CImg<ulongT> res(nb_levels,1,1,1,0);
32015       cimg_rof(*this,ptrs,T) {
32016         const T val = *ptrs;
32017         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
32018       }
32019       return res;
32020     }
32021 
32022     //! Compute the histogram of pixel values \newinstance.
32023     CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
32024       if (!nb_levels || is_empty()) return CImg<ulongT>();
32025       T vmax = 0, vmin = min_max(vmax);
32026       return get_histogram(nb_levels,vmin,vmax);
32027     }
32028 
32029     //! Equalize histogram of pixel values.
32030     /**
32031        \param nb_levels Number of histogram levels used for the equalization.
32032        \param min_value Minimum pixel value considered for the histogram computation.
32033          All pixel values lower than \p min_value will not be counted.
32034        \param max_value Maximum pixel value considered for the histogram computation.
32035          All pixel values higher than \p max_value will not be counted.
32036        \par Example
32037        \code
32038        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
32039        (img,res).display();
32040        \endcode
32041        \image html ref_equalize.jpg
32042     **/
32043     CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
32044       if (!nb_levels || is_empty()) return *this;
32045       const T
32046         vmin = min_value<max_value?min_value:max_value,
32047         vmax = min_value<max_value?max_value:min_value;
32048       CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
32049       ulongT cumul = 0;
32050       cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
32051       if (!cumul) cumul = 1;
32052       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576))
32053       cimg_rofoff(*this,off) {
32054         const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin));
32055         if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul);
32056       }
32057       return *this;
32058     }
32059 
32060     //! Equalize histogram of pixel values \overloading.
32061     CImg<T>& equalize(const unsigned int nb_levels) {
32062       if (!nb_levels || is_empty()) return *this;
32063       T vmax = 0, vmin = min_max(vmax);
32064       return equalize(nb_levels,vmin,vmax);
32065     }
32066 
32067     //! Equalize histogram of pixel values \newinstance.
32068     CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
32069       return (+*this).equalize(nblevels,val_min,val_max);
32070     }
32071 
32072     //! Equalize histogram of pixel values \newinstance.
32073     CImg<T> get_equalize(const unsigned int nblevels) const {
32074       return (+*this).equalize(nblevels);
32075     }
32076 
32077     //! Index multi-valued pixels regarding to a specified colormap.
32078     /**
32079        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
32080        \param dithering Level of dithering (0=disable, 1=standard level).
32081        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
32082        \note
32083        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
32084        \par Example
32085        \code
32086        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
32087        const CImg<float> res = img.get_index(colormap,1,true);
32088        (img,res).display();
32089        \endcode
32090        \image html ref_index.jpg
32091     **/
32092     template<typename t>
32093     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
32094       return get_index(colormap,dithering,map_indexes).move_to(*this);
32095     }
32096 
32097     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
32098     template<typename t>
32099     CImg<typename CImg<t>::Tuint>
32100     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
32101       if (colormap._spectrum!=_spectrum)
32102         throw CImgArgumentException(_cimg_instance
32103                                     "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
32104                                     "have incompatible dimensions.",
32105                                     cimg_instance,
32106                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
32107 
32108       typedef typename CImg<t>::Tuint tuint;
32109       if (is_empty()) return CImg<tuint>();
32110       const ulongT
32111         whd = (ulongT)_width*_height*_depth,
32112         pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
32113       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
32114       if (dithering>0) { // Dithered versions
32115         tuint *ptrd = res._data;
32116         const float ndithering = cimg::cut(dithering,0,1)/16;
32117         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
32118         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
32119         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
32120         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
32121         const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
32122         switch (_spectrum) {
32123         case 1 : { // Optimized for scalars
32124           cimg_forYZ(*this,y,z) {
32125             if (y<height() - 2) {
32126               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
32127               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
32128             }
32129             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
32130             cimg_forX(*this,x) {
32131               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
32132               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32133               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
32134                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
32135                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32136               }
32137               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
32138               *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
32139               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32140             }
32141             cimg::swap(cache_current,cache_next);
32142           }
32143         } break;
32144         case 2 : { // Optimized for 2D vectors
32145           tuint *ptrd1 = ptrd + whd;
32146           cimg_forYZ(*this,y,z) {
32147             if (y<height() - 2) {
32148               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
32149               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
32150               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
32151             }
32152             Tfloat
32153               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
32154               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
32155             cimg_forX(*this,x) {
32156               const Tfloat
32157                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
32158                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
32159               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32160               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32161                 const Tfloat
32162                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
32163                   dist = pval0*pval0 + pval1*pval1;
32164                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32165               }
32166               const t *const ptrmin1 = ptrmin0 + pwhd;
32167               const Tfloat
32168                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
32169                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
32170               *ptrs0+=7*err0; *ptrs1+=7*err1;
32171               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
32172               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
32173               *ptrsn0+=err0; *ptrsn1+=err1;
32174               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
32175               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32176             }
32177             cimg::swap(cache_current,cache_next);
32178           }
32179         } break;
32180         case 3 : { // Optimized for 3D vectors (colors)
32181           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
32182           cimg_forYZ(*this,y,z) {
32183             if (y<height() - 2) {
32184               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
32185               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
32186               cimg_forX(*this,x) {
32187                 *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
32188               }
32189             }
32190             Tfloat
32191               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
32192               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
32193             cimg_forX(*this,x) {
32194               const Tfloat
32195                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
32196                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
32197                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
32198               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32199               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
32200                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32201                 const Tfloat
32202                   pval0 = (Tfloat)*(ptrp0++) - val0,
32203                   pval1 = (Tfloat)*(ptrp1++) - val1,
32204                   pval2 = (Tfloat)*(ptrp2++) - val2,
32205                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
32206                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32207               }
32208               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
32209               const Tfloat
32210                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
32211                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
32212                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
32213 
32214               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
32215               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
32216               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
32217               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
32218 
32219               if (map_indexes) {
32220                 *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
32221               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32222             }
32223             cimg::swap(cache_current,cache_next);
32224           }
32225         } break;
32226         default : // Generic version
32227           cimg_forYZ(*this,y,z) {
32228             if (y<height() - 2) {
32229               Tfloat *ptrc = cache_next;
32230               cimg_forC(*this,c) {
32231                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
32232                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
32233                 ptrc+=cwhd;
32234               }
32235             }
32236             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
32237             cimg_forX(*this,x) {
32238               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
32239               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
32240                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
32241                 cimg_forC(*this,c) {
32242                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
32243                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
32244                 }
32245                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
32246               }
32247               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
32248               cimg_forC(*this,c) {
32249                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
32250                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
32251                 _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
32252               }
32253               if (map_indexes) {
32254                 tuint *_ptrd = ptrd++;
32255                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
32256               }
32257               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
32258             }
32259             cimg::swap(cache_current,cache_next);
32260           }
32261         }
32262       } else { // Non-dithered versions
32263         switch (_spectrum) {
32264         case 1 : { // Optimized for scalars
32265           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32266                                                                      _height*_depth>=16 && pwhd>=16))
32267           cimg_forYZ(*this,y,z) {
32268             tuint *ptrd = res.data(0,y,z);
32269             for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32270               const Tfloat val0 = (Tfloat)*(ptrs0++);
32271               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32272               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
32273                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
32274                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32275               }
32276               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32277             }
32278           }
32279         } break;
32280         case 2 : { // Optimized for 2D vectors
32281           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32282                                                                      _height*_depth>=16 && pwhd>=16))
32283           cimg_forYZ(*this,y,z) {
32284             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
32285             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32286               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
32287               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32288               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32289                 const Tfloat
32290                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
32291                   dist = pval0*pval0 + pval1*pval1;
32292                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32293               }
32294               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
32295               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32296             }
32297           }
32298         } break;
32299         case 3 : { // Optimized for 3D vectors (colors)
32300           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32301                                                                      _height*_depth>=16 && pwhd>=16))
32302           cimg_forYZ(*this,y,z) {
32303             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
32304             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
32305                    *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32306               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
32307               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32308               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
32309                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32310                 const Tfloat
32311                   pval0 = (Tfloat)*(ptrp0++) - val0,
32312                   pval1 = (Tfloat)*(ptrp1++) - val1,
32313                   pval2 = (Tfloat)*(ptrp2++) - val2,
32314                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
32315                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32316               }
32317               if (map_indexes) {
32318                 *(ptrd++) = (tuint)*ptrmin0;
32319                 *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
32320                 *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
32321               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32322             }
32323           }
32324         } break;
32325         default : // Generic version
32326           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32327                                                                      _height*_depth>=16 && pwhd>=16))
32328           cimg_forYZ(*this,y,z) {
32329             tuint *ptrd = res.data(0,y,z);
32330             for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
32331               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
32332               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
32333                 Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
32334                 cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
32335                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
32336               }
32337               if (map_indexes) {
32338                 tuint *_ptrd = ptrd++;
32339                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
32340               }
32341               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
32342             }
32343           }
32344         }
32345       }
32346       return res;
32347     }
32348 
32349     //! Map predefined colormap on the scalar (indexed) image instance.
32350     /**
32351        \param colormap Multi-valued colormap used for mapping the indexes.
32352        \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
32353        \par Example
32354        \code
32355        const CImg<float> img("reference.jpg"),
32356                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
32357                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
32358                          res = img.get_index(colormap1,0).map(colormap2);
32359        (img,res).display();
32360        \endcode
32361        \image html ref_map.jpg
32362     **/
32363     template<typename t>
32364     CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
32365       return get_map(colormap,boundary_conditions).move_to(*this);
32366     }
32367 
32368     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
32369     template<typename t>
32370     CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
32371       const ulongT
32372         whd = (ulongT)_width*_height*_depth, siz = size(),
32373         cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
32374         cwhd2 = 2*cwhd;
32375       CImg<t> res(_width,_height,_depth,_spectrum*colormap._spectrum);
32376       switch (colormap._spectrum) {
32377 
32378       case 1 : { // Optimized for scalars
32379         switch (boundary_conditions) {
32380         case 3 : // Mirror
32381           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32382           for (longT off = 0; off<(longT)siz; ++off) {
32383             const ulongT ind = ((ulongT)_data[off])%cwhd2;
32384             res[off] = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
32385           }
32386           break;
32387         case 2 : // Periodic
32388           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32389           for (longT off = 0; off<(longT)siz; ++off) {
32390             const ulongT ind = (ulongT)_data[off];
32391             res[off] = colormap[ind%cwhd];
32392           }
32393           break;
32394         case 1 : // Neumann
32395           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32396           for (longT off = 0; off<(longT)siz; ++off) {
32397             const longT ind = (longT)_data[off];
32398             res[off] = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
32399           } break;
32400         default : // Dirichlet
32401           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32402           for (longT off = 0; off<(longT)siz; ++off) {
32403             const ulongT ind = (ulongT)_data[off];
32404             res[off] = ind<cwhd?colormap[ind]:(t)0;
32405           }
32406         }
32407       } break;
32408 
32409       case 2 : { // Optimized for 2D vectors
32410         const t *const ptrp0 = colormap._data, *const ptrp1 = ptrp0 + cwhd;
32411         switch (boundary_conditions) {
32412         case 3 : // Mirror
32413           cimg_forC(*this,c) {
32414             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32415             const T *const ptrs = data(0,0,0,c);
32416             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32417             for (longT off = 0; off<(longT)whd; ++off) {
32418               const ulongT
32419                 _ind = ((ulongT)ptrs[off])%cwhd2,
32420                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32421               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32422             }
32423           } break;
32424         case 2 : // Periodic
32425           cimg_forC(*this,c) {
32426             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32427             const T *const ptrs = data(0,0,0,c);
32428             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32429             for (longT off = 0; off<(longT)whd; ++off) {
32430               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32431               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32432             }
32433           } break;
32434         case 1 : // Neumann
32435           cimg_forC(*this,c) {
32436             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32437             const T *const ptrs = data(0,0,0,c);
32438             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32439             for (longT off = 0; off<(longT)whd; ++off) {
32440               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32441               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32442             }
32443           } break;
32444         default : // Dirichlet
32445           cimg_forC(*this,c) {
32446             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32447             const T *const ptrs = data(0,0,0,c);
32448             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32449             for (longT off = 0; off<(longT)whd; ++off) {
32450               const ulongT ind = (ulongT)ptrs[off];
32451               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; }
32452               else ptrd0[off] = ptrd1[off] = (t)0;
32453             }
32454           }
32455         }
32456       } break;
32457 
32458       case 3 : { // Optimized for 3D vectors (colors)
32459         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp0 + 2*cwhd;
32460         switch (boundary_conditions) {
32461         case 3 : // Mirror
32462           cimg_forC(*this,c) {
32463             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32464             const T *const ptrs = data(0,0,0,c);
32465             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32466             for (longT off = 0; off<(longT)whd; ++off) {
32467               const ulongT
32468                 _ind = ((ulongT)ptrs[off])%cwhd2,
32469                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32470               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32471             }
32472           } break;
32473         case 2 : // Periodic
32474           cimg_forC(*this,c) {
32475             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32476             const T *const ptrs = data(0,0,0,c);
32477             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32478             for (longT off = 0; off<(longT)whd; ++off) {
32479               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32480               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32481             }
32482           } break;
32483         case 1 : // Neumann
32484           cimg_forC(*this,c) {
32485             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32486             const T *const ptrs = data(0,0,0,c);
32487             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32488             for (longT off = 0; off<(longT)whd; ++off) {
32489               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32490               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32491             }
32492           } break;
32493         default : // Dirichlet
32494           cimg_forC(*this,c) {
32495             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32496             const T *const ptrs = data(0,0,0,c);
32497             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32498             for (longT off = 0; off<(longT)whd; ++off) {
32499               const ulongT ind = (ulongT)ptrs[off];
32500               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind]; }
32501               else ptrd0[off] = ptrd1[off] = ptrd2[off] = (t)0;
32502             }
32503           }
32504         }
32505       } break;
32506 
32507       default : { // Generic version
32508         switch (boundary_conditions) {
32509         case 3 : // Mirror
32510           cimg_forC(*this,c) {
32511             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32512             const T *const ptrs = data(0,0,0,c);
32513             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32514             for (longT off = 0; off<(longT)whd; ++off) {
32515               const ulongT
32516                 _ind = ((ulongT)ptrs[off])%cwhd,
32517                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32518               t *const _ptrd = ptrd + off;
32519               const t *const ptrp = &colormap[ind];
32520               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32521             }
32522           } break;
32523         case 2 : // Periodic
32524           cimg_forC(*this,c) {
32525             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32526             const T *const ptrs = data(0,0,0,c);
32527             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32528             for (longT off = 0; off<(longT)whd; ++off) {
32529               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32530               t *const _ptrd = ptrd + off;
32531               const t *const ptrp = &colormap[ind];
32532               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32533             }
32534           } break;
32535         case 1 : // Neumann
32536           cimg_forC(*this,c) {
32537             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32538             const T *const ptrs = data(0,0,0,c);
32539             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32540             for (longT off = 0; off<(longT)whd; ++off) {
32541               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32542               t *const _ptrd = ptrd + off;
32543               const t *const ptrp = &colormap[ind];
32544               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32545             }
32546           } break;
32547         default : // Dirichlet
32548           cimg_forC(*this,c) {
32549             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32550             const T *const ptrs = data(0,0,0,c);
32551             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32552             for (longT off = 0; off<(longT)whd; ++off) {
32553               const ulongT ind = (ulongT)ptrs[off];
32554               t *const _ptrd = ptrd + off;
32555               if (ind<cwhd) {
32556                 const t *const ptrp = &colormap[ind];
32557                 cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32558               } else cimg_forC(colormap,k) _ptrd[k*whd] = (t)0;
32559             }
32560           }
32561         }
32562       }
32563       }
32564       return res;
32565     }
32566 
32567     //! Label connected components.
32568     /**
32569        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
32570        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
32571        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32572        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32573        \note The algorithm of connected components computation has been primarily done
32574        by A. Meijster, according to the publication:
32575        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
32576        In: Science of Computer Programming 41 (2001), pp. 173--194'.
32577        The submitted code has then been modified to fit CImg coding style and constraints.
32578     **/
32579     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32580                    const bool is_L2_norm=true) {
32581       if (is_empty()) return *this;
32582       return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this);
32583     }
32584 
32585     //! Label connected components \newinstance.
32586     CImg<ulongT> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32587                            const bool is_L2_norm=true) const {
32588       if (is_empty()) return CImg<ulongT>();
32589 
32590       // Create neighborhood tables.
32591       int dx[13], dy[13], dz[13], nb = 0;
32592       dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
32593       dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
32594       if (is_high_connectivity) {
32595         dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
32596         dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
32597       }
32598       if (_depth>1) { // 3D version
32599         dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
32600         if (is_high_connectivity) {
32601           dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
32602           dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
32603           dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
32604           dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
32605 
32606           dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
32607           dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
32608           dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
32609           dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
32610         }
32611       }
32612       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
32613     }
32614 
32615     //! Label connected components \overloading.
32616     /**
32617        \param connectivity_mask Mask of the neighboring pixels.
32618        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32619        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32620     **/
32621     template<typename t>
32622     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32623                    const bool is_L2_norm=true) {
32624       if (is_empty()) return *this;
32625       return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this);
32626     }
32627 
32628     //! Label connected components \newinstance.
32629     template<typename t>
32630     CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32631                            const bool is_L2_norm=true) const {
32632       if (is_empty()) return CImg<ulongT>();
32633       int nb = 0;
32634       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
32635       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
32636       nb = 0;
32637       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
32638                                                connectivity_mask(x,y,z)) {
32639         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
32640       }
32641       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
32642     }
32643 
32644     CImg<ulongT> _label(const unsigned int nb, const int *const dx,
32645                         const int *const dy, const int *const dz,
32646                         const Tfloat tolerance, const bool is_L2_norm) const {
32647       CImg<ulongT> res(_width,_height,_depth);
32648       const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance;
32649 
32650       // Init label numbers.
32651       ulongT *ptr = res.data();
32652       cimg_foroff(res,p) *(ptr++) = p;
32653 
32654       // For each neighbour-direction, label.
32655       for (unsigned int n = 0; n<nb; ++n) {
32656         const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
32657         if (_dx || _dy || _dz) {
32658           const int
32659             x0 = _dx<0?-_dx:0,
32660             x1 = _dx<0?width():width() - _dx,
32661             y0 = _dy<0?-_dy:0,
32662             y1 = _dy<0?height():height() - _dy,
32663             z0 = _dz<0?-_dz:0,
32664             z1 = _dz<0?depth():depth() - _dz;
32665           const longT
32666             wh = (longT)width()*height(),
32667             whd = (longT)width()*height()*depth(),
32668             offset = _dz*wh + _dy*width() + _dx;
32669           for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
32670             for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
32671               for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
32672                 Tfloat diff;
32673                 switch (_spectrum) {
32674                 case 1 :
32675                   diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd));
32676                   break;
32677                 case 2 :
32678                   if (is_L2_norm)
32679                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32680                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
32681                   else
32682                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32683                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
32684                   break;
32685                 case 3 :
32686                   if (is_L2_norm)
32687                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32688                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32689                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
32690                   else
32691                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32692                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32693                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
32694                   break;
32695                 case 4 :
32696                   if (is_L2_norm)
32697                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32698                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32699                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
32700                       cimg::sqr((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
32701                   else
32702                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32703                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32704                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
32705                       cimg::abs((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
32706                   break;
32707                 default :
32708                   diff = 0;
32709                   if (is_L2_norm)
32710                     cimg_forC(*this,c)
32711                       diff+=cimg::sqr((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
32712                   else
32713                     cimg_forC(*this,c)
32714                       diff+=cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
32715                 }
32716 
32717                 if (diff<=_tolerance) {
32718                   const longT q = p + offset;
32719                   ulongT xk, yk;
32720                   for (xk = (ulongT)(p<q?q:p), yk = (ulongT)(p<q?p:q); xk!=yk && res[xk]!=xk; ) {
32721                     xk = res[xk]; if (xk<yk) cimg::swap(xk,yk);
32722                   }
32723                   if (xk!=yk) res[xk] = (ulongT)yk;
32724                   for (ulongT _p = (ulongT)p; _p!=yk; ) {
32725                     const ulongT h = res[_p];
32726                     res[_p] = (ulongT)yk;
32727                     _p = h;
32728                   }
32729                   for (ulongT _q = (ulongT)q; _q!=yk; ) {
32730                     const ulongT h = res[_q];
32731                     res[_q] = (ulongT)yk;
32732                     _q = h;
32733                   }
32734                 }
32735               }
32736             }
32737           }
32738         }
32739       }
32740 
32741       // Resolve equivalences.
32742       ulongT counter = 0;
32743       ptr = res.data();
32744       cimg_foroff(res,p) { *ptr = *ptr==p?counter++:res[*ptr]; ++ptr; }
32745       return res;
32746     }
32747 
32748     // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
32749     CImg<T>& _system_strescape() {
32750 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
32751       move_to(list); \
32752       CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
32753       CImgList<T> list;
32754       const T *ptrs = _data;
32755       cimg_for(*this,p,T) switch ((int)*p) {
32756         cimg_system_strescape('\\',"\\\\");
32757         cimg_system_strescape('\"',"\\\"");
32758         cimg_system_strescape('!',"\"\\!\"");
32759         cimg_system_strescape('`',"\\`");
32760         cimg_system_strescape('$',"\\$");
32761       }
32762       if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
32763       return (list>'x').move_to(*this);
32764     }
32765 
32766     //@}
32767     //---------------------------------
32768     //
32769     //! \name Color Base Management
32770     //@{
32771     //---------------------------------
32772 
32773     //! Return colormap \e "default", containing 256 colors entries in RGB.
32774     /**
32775        \return The following \c 256x1x1x3 colormap is returned:
32776        \image html ref_colormap_default.jpg
32777     **/
32778     static const CImg<Tuchar>& default_LUT256() {
32779       static CImg<Tuchar> colormap;
32780       cimg::mutex(8);
32781       if (!colormap) {
32782         colormap.assign(1,256,1,3);
32783         for (unsigned int index = 0, r = 16; r<256; r+=32)
32784           for (unsigned int g = 16; g<256; g+=32)
32785             for (unsigned int b = 32; b<256; b+=64) {
32786               colormap(0,index,0) = (Tuchar)r;
32787               colormap(0,index,1) = (Tuchar)g;
32788               colormap(0,index++,2) = (Tuchar)b;
32789             }
32790       }
32791       cimg::mutex(8,0);
32792       return colormap;
32793     }
32794 
32795     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
32796     /**
32797        \return The following \c 256x1x1x3 colormap is returned:
32798        \image html ref_colormap_hsv.jpg
32799     **/
32800     static const CImg<Tuchar>& HSV_LUT256() {
32801       static CImg<Tuchar> colormap;
32802       cimg::mutex(8);
32803       if (!colormap) {
32804         CImg<Tint> tmp(1,256,1,3,1);
32805         tmp.get_shared_channel(0).sequence(0,359);
32806         colormap = tmp.HSVtoRGB();
32807       }
32808       cimg::mutex(8,0);
32809       return colormap;
32810     }
32811 
32812     //! Return colormap \e "lines", containing 256 colors entries in RGB.
32813     /**
32814        \return The following \c 256x1x1x3 colormap is returned:
32815        \image html ref_colormap_lines.jpg
32816     **/
32817     static const CImg<Tuchar>& lines_LUT256() {
32818       static const unsigned char pal[] = {
32819         0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255,
32820         53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125,
32821         206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138,
32822         101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125,
32823         16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0,
32824         97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121,
32825         227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85,
32826         210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255,
32827         20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49,
32828         210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121,
32829         166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190,
32830         85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81,
32831         247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0,
32832         170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81,
32833         215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121,
32834         0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125,
32835         0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219,
32836         251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239,
32837         89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57,
32838         202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125,
32839         32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142,
32840         8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65,
32841         194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210,
32842         154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85,
32843         146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125,
32844         0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49,
32845         89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125,
32846         69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69,
32847         40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194,
32848         125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0,
32849         125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93,
32850         16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85,
32851         0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97,
32852         255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49,
32853         162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130,
32854         36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125,
32855         219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125,
32856         202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210,
32857         85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255,
32858         97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255,
32859         85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239,
32860         89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166,
32861         0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0,
32862         174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69,
32863         162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81,
32864         158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97,
32865         57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4,
32866         73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 };
32867       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
32868       return colormap;
32869     }
32870 
32871     //! Return colormap \e "hot", containing 256 colors entries in RGB.
32872     /**
32873        \return The following \c 256x1x1x3 colormap is returned:
32874        \image html ref_colormap_hot.jpg
32875     **/
32876     static const CImg<Tuchar>& hot_LUT256() {
32877       static CImg<Tuchar> colormap;
32878       cimg::mutex(8);
32879       if (!colormap) {
32880         colormap.assign(1,4,1,3,(T)0);
32881         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
32882         colormap.resize(1,256,1,3,3);
32883       }
32884       cimg::mutex(8,0);
32885       return colormap;
32886     }
32887 
32888     //! Return colormap \e "cool", containing 256 colors entries in RGB.
32889     /**
32890        \return The following \c 256x1x1x3 colormap is returned:
32891        \image html ref_colormap_cool.jpg
32892     **/
32893     static const CImg<Tuchar>& cool_LUT256() {
32894       static CImg<Tuchar> colormap;
32895       cimg::mutex(8);
32896       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);
32897       cimg::mutex(8,0);
32898       return colormap;
32899     }
32900 
32901     //! Return colormap \e "jet", containing 256 colors entries in RGB.
32902     /**
32903        \return The following \c 256x1x1x3 colormap is returned:
32904        \image html ref_colormap_jet.jpg
32905     **/
32906     static const CImg<Tuchar>& jet_LUT256() {
32907       static CImg<Tuchar> colormap;
32908       cimg::mutex(8);
32909       if (!colormap) {
32910         colormap.assign(1,4,1,3,(T)0);
32911         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
32912         colormap.resize(1,256,1,3,3);
32913       }
32914       cimg::mutex(8,0);
32915       return colormap;
32916     }
32917 
32918     //! Return colormap \e "flag", containing 256 colors entries in RGB.
32919     /**
32920        \return The following \c 256x1x1x3 colormap is returned:
32921        \image html ref_colormap_flag.jpg
32922     **/
32923     static const CImg<Tuchar>& flag_LUT256() {
32924       static CImg<Tuchar> colormap;
32925       cimg::mutex(8);
32926       if (!colormap) {
32927         colormap.assign(1,4,1,3,(T)0);
32928         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
32929         colormap.resize(1,256,1,3,0,2);
32930       }
32931       cimg::mutex(8,0);
32932       return colormap;
32933     }
32934 
32935     //! Return colormap \e "cube", containing 256 colors entries in RGB.
32936     /**
32937        \return The following \c 256x1x1x3 colormap is returned:
32938        \image html ref_colormap_cube.jpg
32939     **/
32940     static const CImg<Tuchar>& cube_LUT256() {
32941       static CImg<Tuchar> colormap;
32942       cimg::mutex(8);
32943       if (!colormap) {
32944         colormap.assign(1,8,1,3,(T)0);
32945         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
32946           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
32947           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
32948         colormap.resize(1,256,1,3,3);
32949       }
32950       cimg::mutex(8,0);
32951       return colormap;
32952     }
32953 
32954     //! Convert pixel values from sRGB to RGB color spaces.
32955     CImg<T>& sRGBtoRGB() {
32956       if (is_empty()) return *this;
32957       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
32958       cimg_rofoff(*this,off) {
32959         const Tfloat
32960           sval = (Tfloat)_data[off]/255,
32961           val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
32962         _data[off] = (T)cimg::cut(val*255,0,255);
32963       }
32964       return *this;
32965     }
32966 
32967     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
32968     CImg<Tfloat> get_sRGBtoRGB() const {
32969       return CImg<Tfloat>(*this,false).sRGBtoRGB();
32970     }
32971 
32972     //! Convert pixel values from RGB to sRGB color spaces.
32973     CImg<T>& RGBtosRGB() {
32974       if (is_empty()) return *this;
32975       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
32976       cimg_rofoff(*this,off) {
32977         const Tfloat
32978           val = (Tfloat)_data[off]/255,
32979           sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
32980         _data[off] = (T)cimg::cut(sval*255,0,255);
32981       }
32982       return *this;
32983     }
32984 
32985     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
32986     CImg<Tfloat> get_RGBtosRGB() const {
32987       return CImg<Tfloat>(*this,false).RGBtosRGB();
32988     }
32989 
32990     //! Convert pixel values from RGB to HSI color spaces.
32991     CImg<T>& RGBtoHSI() {
32992       if (_spectrum!=3)
32993         throw CImgInstanceException(_cimg_instance
32994                                     "RGBtoHSI(): Instance is not a RGB image.",
32995                                     cimg_instance);
32996 
32997       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32998       const longT whd = (longT)width()*height()*depth();
32999       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33000       for (longT N = 0; N<whd; ++N) {
33001         const Tfloat
33002           R = (Tfloat)p1[N],
33003           G = (Tfloat)p2[N],
33004           B = (Tfloat)p3[N],
33005           m = cimg::min(R,G,B),
33006           M = cimg::max(R,G,B),
33007           C = M - m,
33008           sum = R + G + B,
33009           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33010           S = sum<=0?0:1 - 3*m/sum,
33011           I = sum/(3*255);
33012         p1[N] = (T)H;
33013         p2[N] = (T)S;
33014         p3[N] = (T)I;
33015       }
33016       return *this;
33017     }
33018 
33019     //! Convert pixel values from RGB to HSI color spaces \newinstance.
33020     CImg<Tfloat> get_RGBtoHSI() const {
33021       return CImg<Tfloat>(*this,false).RGBtoHSI();
33022     }
33023 
33024     //! Convert pixel values from HSI to RGB color spaces.
33025     CImg<T>& HSItoRGB() {
33026       if (_spectrum!=3)
33027         throw CImgInstanceException(_cimg_instance
33028                                     "HSItoRGB(): Instance is not a HSI image.",
33029                                     cimg_instance);
33030 
33031       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33032       const longT whd = (longT)width()*height()*depth();
33033       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33034       for (longT N = 0; N<whd; ++N) {
33035         const Tfloat
33036           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33037           S = (Tfloat)p2[N],
33038           I = (Tfloat)p3[N],
33039           Z = 1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1),
33040           C = I*S/(1 + Z),
33041           X = C*Z,
33042           m = I*(1 - S)/3;
33043         Tfloat R, G, B;
33044         switch ((int)H) {
33045         case 0 : R = C; G = X; B = 0; break;
33046         case 1 : R = X; G = C; B = 0; break;
33047         case 2 : R = 0; G = C; B = X; break;
33048         case 3 : R = 0; G = X; B = C; break;
33049         case 4 : R = X; G = 0; B = C; break;
33050         default : R = C; G = 0; B = X;
33051         }
33052         p1[N] = (T)((R + m)*3*255);
33053         p2[N] = (T)((G + m)*3*255);
33054         p3[N] = (T)((B + m)*3*255);
33055       }
33056       return *this;
33057     }
33058 
33059     //! Convert pixel values from HSI to RGB color spaces \newinstance.
33060     CImg<Tfloat> get_HSItoRGB() const {
33061       return CImg< Tuchar>(*this,false).HSItoRGB();
33062     }
33063 
33064     //! Convert pixel values from RGB to HSL color spaces.
33065     CImg<T>& RGBtoHSL() {
33066       if (_spectrum!=3)
33067         throw CImgInstanceException(_cimg_instance
33068                                     "RGBtoHSL(): Instance is not a RGB image.",
33069                                     cimg_instance);
33070 
33071       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33072       const longT whd = (longT)width()*height()*depth();
33073       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33074       for (longT N = 0; N<whd; ++N) {
33075         const Tfloat
33076           R = (Tfloat)p1[N],
33077           G = (Tfloat)p2[N],
33078           B = (Tfloat)p3[N],
33079           m = cimg::min(R,G,B),
33080           M = cimg::max(R,G,B),
33081           C = M - m,
33082           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33083           L = 0.5f*(m + M)/255,
33084           S = L==1 || L==0?0:C/(1 - cimg::abs(2*L - 1))/255;
33085         p1[N] = (T)H;
33086         p2[N] = (T)S;
33087         p3[N] = (T)L;
33088       }
33089       return *this;
33090     }
33091 
33092     //! Convert pixel values from RGB to HSL color spaces \newinstance.
33093     CImg<Tfloat> get_RGBtoHSL() const {
33094       return CImg<Tfloat>(*this,false).RGBtoHSL();
33095     }
33096 
33097     //! Convert pixel values from HSL to RGB color spaces.
33098     CImg<T>& HSLtoRGB() {
33099       if (_spectrum!=3)
33100         throw CImgInstanceException(_cimg_instance
33101                                     "HSLtoRGB(): Instance is not a HSL image.",
33102                                     cimg_instance);
33103 
33104       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33105       const longT whd = (longT)width()*height()*depth();
33106       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33107       for (longT N = 0; N<whd; ++N) {
33108         const Tfloat
33109           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33110           S = (Tfloat)p2[N],
33111           L = (Tfloat)p3[N],
33112           C = (1 - cimg::abs(2*L - 1))*S,
33113           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
33114           m = L - C/2;
33115         Tfloat R, G, B;
33116         switch ((int)H) {
33117         case 0 : R = C; G = X; B = 0; break;
33118         case 1 : R = X; G = C; B = 0; break;
33119         case 2 : R = 0; G = C; B = X; break;
33120         case 3 : R = 0; G = X; B = C; break;
33121         case 4 : R = X; G = 0; B = C; break;
33122         default : R = C; G = 0; B = X;
33123         }
33124         p1[N] = (T)((R + m)*255);
33125         p2[N] = (T)((G + m)*255);
33126         p3[N] = (T)((B + m)*255);
33127       }
33128       return *this;
33129     }
33130 
33131     //! Convert pixel values from HSL to RGB color spaces \newinstance.
33132     CImg<Tuchar> get_HSLtoRGB() const {
33133       return CImg<Tuchar>(*this,false).HSLtoRGB();
33134     }
33135 
33136     //! Convert pixel values from RGB to HSV color spaces.
33137     CImg<T>& RGBtoHSV() {
33138       if (_spectrum!=3)
33139         throw CImgInstanceException(_cimg_instance
33140                                     "RGBtoHSV(): Instance is not a RGB image.",
33141                                     cimg_instance);
33142 
33143       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33144       const longT whd = (longT)width()*height()*depth();
33145       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33146       for (longT N = 0; N<whd; ++N) {
33147         const Tfloat
33148           R = (Tfloat)p1[N],
33149           G = (Tfloat)p2[N],
33150           B = (Tfloat)p3[N],
33151           M = cimg::max(R,G,B),
33152           C = M - cimg::min(R,G,B),
33153           H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33154           S = M<=0?0:C/M;
33155         p1[N] = (T)H;
33156         p2[N] = (T)S;
33157         p3[N] = (T)(M/255);
33158       }
33159       return *this;
33160     }
33161 
33162     //! Convert pixel values from RGB to HSV color spaces \newinstance.
33163     CImg<Tfloat> get_RGBtoHSV() const {
33164       return CImg<Tfloat>(*this,false).RGBtoHSV();
33165     }
33166 
33167     //! Convert pixel values from HSV to RGB color spaces.
33168     CImg<T>& HSVtoRGB() {
33169       if (_spectrum!=3)
33170         throw CImgInstanceException(_cimg_instance
33171                                     "HSVtoRGB(): Instance is not a HSV image.",
33172                                     cimg_instance);
33173 
33174       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33175       const longT whd = (longT)width()*height()*depth();
33176       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33177       for (longT N = 0; N<whd; ++N) {
33178         Tfloat
33179           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33180           S = (Tfloat)p2[N],
33181           V = (Tfloat)p3[N],
33182           C = V*S,
33183           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
33184           m = V - C;
33185         Tfloat R, G, B;
33186         switch ((int)H) {
33187         case 0 : R = C; G = X; B = 0; break;
33188         case 1 : R = X; G = C; B = 0; break;
33189         case 2 : R = 0; G = C; B = X; break;
33190         case 3 : R = 0; G = X; B = C; break;
33191         case 4 : R = X; G = 0; B = C; break;
33192         default : R = C; G = 0; B = X;
33193         }
33194         p1[N] = (T)((R + m)*255);
33195         p2[N] = (T)((G + m)*255);
33196         p3[N] = (T)((B + m)*255);
33197       }
33198       return *this;
33199     }
33200 
33201     //! Convert pixel values from HSV to RGB color spaces \newinstance.
33202     CImg<Tuchar> get_HSVtoRGB() const {
33203       return CImg<Tuchar>(*this,false).HSVtoRGB();
33204     }
33205 
33206     //! Convert pixel values from RGB to YCbCr color spaces.
33207     CImg<T>& RGBtoYCbCr() {
33208       if (_spectrum!=3)
33209         throw CImgInstanceException(_cimg_instance
33210                                     "RGBtoYCbCr(): Instance is not a RGB image.",
33211                                     cimg_instance);
33212 
33213       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33214       const longT whd = (longT)width()*height()*depth();
33215       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
33216       for (longT N = 0; N<whd; ++N) {
33217         const Tfloat
33218           R = (Tfloat)p1[N],
33219           G = (Tfloat)p2[N],
33220           B = (Tfloat)p3[N],
33221           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
33222           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
33223           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
33224         p1[N] = (T)cimg::cut(Y,0,255),
33225         p2[N] = (T)cimg::cut(Cb,0,255),
33226         p3[N] = (T)cimg::cut(Cr,0,255);
33227       }
33228       return *this;
33229     }
33230 
33231     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
33232     CImg<Tuchar> get_RGBtoYCbCr() const {
33233       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
33234     }
33235 
33236     //! Convert pixel values from RGB to YCbCr color spaces.
33237     CImg<T>& YCbCrtoRGB() {
33238       if (_spectrum!=3)
33239         throw CImgInstanceException(_cimg_instance
33240                                     "YCbCrtoRGB(): Instance is not a YCbCr image.",
33241                                     cimg_instance);
33242 
33243       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33244       const longT whd = (longT)width()*height()*depth();
33245       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
33246       for (longT N = 0; N<whd; ++N) {
33247         const Tfloat
33248           Y = (Tfloat)p1[N] - 16,
33249           Cb = (Tfloat)p2[N] - 128,
33250           Cr = (Tfloat)p3[N] - 128,
33251           R = (298*Y + 409*Cr + 128)/256,
33252           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
33253           B = (298*Y + 516*Cb + 128)/256;
33254         p1[N] = (T)cimg::cut(R,0,255),
33255         p2[N] = (T)cimg::cut(G,0,255),
33256         p3[N] = (T)cimg::cut(B,0,255);
33257       }
33258       return *this;
33259     }
33260 
33261     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
33262     CImg<Tuchar> get_YCbCrtoRGB() const {
33263       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
33264     }
33265 
33266     //! Convert pixel values from RGB to YUV color spaces.
33267     CImg<T>& RGBtoYUV() {
33268       if (_spectrum!=3)
33269         throw CImgInstanceException(_cimg_instance
33270                                     "RGBtoYUV(): Instance is not a RGB image.",
33271                                     cimg_instance);
33272 
33273       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33274       const longT whd = (longT)width()*height()*depth();
33275       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
33276       for (longT N = 0; N<whd; ++N) {
33277         const Tfloat
33278           R = (Tfloat)p1[N]/255,
33279           G = (Tfloat)p2[N]/255,
33280           B = (Tfloat)p3[N]/255,
33281           Y = 0.299f*R + 0.587f*G + 0.114f*B;
33282         p1[N] = (T)Y;
33283         p2[N] = (T)(0.492f*(B - Y));
33284         p3[N] = (T)(0.877*(R - Y));
33285       }
33286       return *this;
33287     }
33288 
33289     //! Convert pixel values from RGB to YUV color spaces \newinstance.
33290     CImg<Tfloat> get_RGBtoYUV() const {
33291       return CImg<Tfloat>(*this,false).RGBtoYUV();
33292     }
33293 
33294     //! Convert pixel values from YUV to RGB color spaces.
33295     CImg<T>& YUVtoRGB() {
33296       if (_spectrum!=3)
33297         throw CImgInstanceException(_cimg_instance
33298                                     "YUVtoRGB(): Instance is not a YUV image.",
33299                                     cimg_instance);
33300 
33301       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33302       const longT whd = (longT)width()*height()*depth();
33303       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
33304       for (longT N = 0; N<whd; ++N) {
33305         const Tfloat
33306           Y = (Tfloat)p1[N],
33307           U = (Tfloat)p2[N],
33308           V = (Tfloat)p3[N],
33309           R = (Y + 1.140f*V)*255,
33310           G = (Y - 0.395f*U - 0.581f*V)*255,
33311           B = (Y + 2.032f*U)*255;
33312         p1[N] = (T)cimg::cut(R,0,255),
33313         p2[N] = (T)cimg::cut(G,0,255),
33314         p3[N] = (T)cimg::cut(B,0,255);
33315       }
33316       return *this;
33317     }
33318 
33319     //! Convert pixel values from YUV to RGB color spaces \newinstance.
33320     CImg<Tuchar> get_YUVtoRGB() const {
33321       return CImg< Tuchar>(*this,false).YUVtoRGB();
33322     }
33323 
33324     //! Convert pixel values from RGB to CMY color spaces.
33325     CImg<T>& RGBtoCMY() {
33326       if (_spectrum!=3)
33327         throw CImgInstanceException(_cimg_instance
33328                                     "RGBtoCMY(): Instance is not a RGB image.",
33329                                     cimg_instance);
33330 
33331       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33332       const longT whd = (longT)width()*height()*depth();
33333       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33334       for (longT N = 0; N<whd; ++N) {
33335         const Tfloat
33336           R = (Tfloat)p1[N],
33337           G = (Tfloat)p2[N],
33338           B = (Tfloat)p3[N],
33339           C = 255 - R,
33340           M = 255 - G,
33341           Y = 255 - B;
33342         p1[N] = (T)cimg::cut(C,0,255),
33343         p2[N] = (T)cimg::cut(M,0,255),
33344         p3[N] = (T)cimg::cut(Y,0,255);
33345       }
33346       return *this;
33347     }
33348 
33349     //! Convert pixel values from RGB to CMY color spaces \newinstance.
33350     CImg<Tuchar> get_RGBtoCMY() const {
33351       return CImg<Tfloat>(*this,false).RGBtoCMY();
33352     }
33353 
33354     //! Convert pixel values from CMY to RGB color spaces.
33355     CImg<T>& CMYtoRGB() {
33356       if (_spectrum!=3)
33357         throw CImgInstanceException(_cimg_instance
33358                                     "CMYtoRGB(): Instance is not a CMY image.",
33359                                     cimg_instance);
33360 
33361       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33362       const longT whd = (longT)width()*height()*depth();
33363       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33364       for (longT N = 0; N<whd; ++N) {
33365         const Tfloat
33366           C = (Tfloat)p1[N],
33367           M = (Tfloat)p2[N],
33368           Y = (Tfloat)p3[N],
33369           R = 255 - C,
33370           G = 255 - M,
33371           B = 255 - Y;
33372         p1[N] = (T)cimg::cut(R,0,255),
33373         p2[N] = (T)cimg::cut(G,0,255),
33374         p3[N] = (T)cimg::cut(B,0,255);
33375       }
33376       return *this;
33377     }
33378 
33379     //! Convert pixel values from CMY to RGB color spaces \newinstance.
33380     CImg<Tuchar> get_CMYtoRGB() const {
33381       return CImg<Tuchar>(*this,false).CMYtoRGB();
33382     }
33383 
33384     //! Convert pixel values from CMY to CMYK color spaces.
33385     CImg<T>& CMYtoCMYK() {
33386       return get_CMYtoCMYK().move_to(*this);
33387     }
33388 
33389     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
33390     CImg<Tuchar> get_CMYtoCMYK() const {
33391       if (_spectrum!=3)
33392         throw CImgInstanceException(_cimg_instance
33393                                     "CMYtoCMYK(): Instance is not a CMY image.",
33394                                     cimg_instance);
33395 
33396       CImg<Tfloat> res(_width,_height,_depth,4);
33397       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
33398       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);
33399       const longT whd = (longT)width()*height()*depth();
33400       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33401       for (longT N = 0; N<whd; ++N) {
33402         Tfloat
33403           C = (Tfloat)ps1[N],
33404           M = (Tfloat)ps2[N],
33405           Y = (Tfloat)ps3[N],
33406           K = cimg::min(C,M,Y);
33407         if (K>=255) C = M = Y = 0;
33408         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
33409         pd1[N] = (Tfloat)cimg::cut(C,0,255),
33410         pd2[N] = (Tfloat)cimg::cut(M,0,255),
33411         pd3[N] = (Tfloat)cimg::cut(Y,0,255),
33412         pd4[N] = (Tfloat)cimg::cut(K,0,255);
33413       }
33414       return res;
33415     }
33416 
33417     //! Convert pixel values from CMYK to CMY color spaces.
33418     CImg<T>& CMYKtoCMY() {
33419       return get_CMYKtoCMY().move_to(*this);
33420     }
33421 
33422     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
33423     CImg<Tfloat> get_CMYKtoCMY() const {
33424       if (_spectrum!=4)
33425         throw CImgInstanceException(_cimg_instance
33426                                     "CMYKtoCMY(): Instance is not a CMYK image.",
33427                                     cimg_instance);
33428 
33429       CImg<Tfloat> res(_width,_height,_depth,3);
33430       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);
33431       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
33432       const longT whd = (longT)width()*height()*depth();
33433       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33434       for (longT N = 0; N<whd; ++N) {
33435         const Tfloat
33436           C = (Tfloat)ps1[N],
33437           M = (Tfloat)ps2[N],
33438           Y = (Tfloat)ps3[N],
33439           K = (Tfloat)ps4[N],
33440           K1 = 1 - K/255,
33441           nC = C*K1 + K,
33442           nM = M*K1 + K,
33443           nY = Y*K1 + K;
33444         pd1[N] = (Tfloat)cimg::cut(nC,0,255),
33445         pd2[N] = (Tfloat)cimg::cut(nM,0,255),
33446         pd3[N] = (Tfloat)cimg::cut(nY,0,255);
33447       }
33448       return res;
33449     }
33450 
33451     //! Convert pixel values from RGB to XYZ color spaces.
33452     /**
33453        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33454     **/
33455     CImg<T>& RGBtoXYZ(const bool use_D65=true) {
33456       if (_spectrum!=3)
33457         throw CImgInstanceException(_cimg_instance
33458                                     "RGBtoXYZ(): Instance is not a RGB image.",
33459                                     cimg_instance);
33460 
33461       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33462       const longT whd = (longT)width()*height()*depth();
33463       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33464       for (longT N = 0; N<whd; ++N) {
33465         const Tfloat
33466           R = (Tfloat)p1[N]/255,
33467           G = (Tfloat)p2[N]/255,
33468           B = (Tfloat)p3[N]/255;
33469         if (use_D65) { // D65
33470           p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
33471           p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
33472           p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
33473         } else { // D50
33474           p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
33475           p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
33476           p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
33477         }
33478       }
33479       return *this;
33480     }
33481 
33482     //! Convert pixel values from RGB to XYZ color spaces \newinstance.
33483     CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
33484       return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
33485     }
33486 
33487     //! Convert pixel values from XYZ to RGB color spaces.
33488     /**
33489        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33490     **/
33491     CImg<T>& XYZtoRGB(const bool use_D65=true) {
33492       if (_spectrum!=3)
33493         throw CImgInstanceException(_cimg_instance
33494                                     "XYZtoRGB(): Instance is not a XYZ image.",
33495                                     cimg_instance);
33496 
33497       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33498       const longT whd = (longT)width()*height()*depth();
33499       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33500       for (longT N = 0; N<whd; ++N) {
33501         const Tfloat
33502           X = (Tfloat)p1[N]*255,
33503           Y = (Tfloat)p2[N]*255,
33504           Z = (Tfloat)p3[N]*255;
33505         if (use_D65) {
33506           p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
33507           p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
33508           p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
33509         } else {
33510           p1[N] = (T)cimg::cut(3.134274799724*X  - 1.617275708956*Y - 0.490724283042*Z,0,255);
33511           p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
33512           p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
33513         }
33514       }
33515       return *this;
33516     }
33517 
33518     //! Convert pixel values from XYZ to RGB color spaces \newinstance.
33519     CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
33520       return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
33521     }
33522 
33523     //! Convert pixel values from XYZ to Lab color spaces.
33524     CImg<T>& XYZtoLab(const bool use_D65=true) {
33525 #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
33526 
33527       if (_spectrum!=3)
33528         throw CImgInstanceException(_cimg_instance
33529                                     "XYZtoLab(): Instance is not a XYZ image.",
33530                                     cimg_instance);
33531       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33532       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33533       const longT whd = (longT)width()*height()*depth();
33534       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33535       for (longT N = 0; N<whd; ++N) {
33536         const Tfloat
33537           X = (Tfloat)(p1[N]/white[0]),
33538           Y = (Tfloat)(p2[N]/white[1]),
33539           Z = (Tfloat)(p3[N]/white[2]),
33540           fX = (Tfloat)_cimg_Labf(X),
33541           fY = (Tfloat)_cimg_Labf(Y),
33542           fZ = (Tfloat)_cimg_Labf(Z);
33543         p1[N] = (T)cimg::cut(116*fY - 16,0,100);
33544         p2[N] = (T)(500*(fX - fY));
33545         p3[N] = (T)(200*(fY - fZ));
33546       }
33547       return *this;
33548     }
33549 
33550     //! Convert pixel values from XYZ to Lab color spaces \newinstance.
33551     CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
33552       return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
33553     }
33554 
33555     //! Convert pixel values from Lab to XYZ color spaces.
33556     CImg<T>& LabtoXYZ(const bool use_D65=true) {
33557       if (_spectrum!=3)
33558         throw CImgInstanceException(_cimg_instance
33559                                     "LabtoXYZ(): Instance is not a Lab image.",
33560                                     cimg_instance);
33561       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33562       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33563       const longT whd = (longT)width()*height()*depth();
33564       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33565       for (longT N = 0; N<whd; ++N) {
33566         const Tfloat
33567           L = (Tfloat)p1[N],
33568           a = (Tfloat)p2[N],
33569           b = (Tfloat)p3[N],
33570           cY = (L + 16)/116,
33571           cZ = cY - b/200,
33572           cX = a/500 + cY,
33573           X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
33574           Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
33575           Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
33576         p1[N] = (T)(X*white[0]);
33577         p2[N] = (T)(Y*white[1]);
33578         p3[N] = (T)(Z*white[2]);
33579       }
33580       return *this;
33581     }
33582 
33583     //! Convert pixel values from Lab to XYZ color spaces \newinstance.
33584     CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
33585       return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
33586     }
33587 
33588     //! Convert pixel values from XYZ to xyY color spaces.
33589     CImg<T>& XYZtoxyY() {
33590       if (_spectrum!=3)
33591         throw CImgInstanceException(_cimg_instance
33592                                     "XYZtoxyY(): Instance is not a XYZ image.",
33593                                     cimg_instance);
33594 
33595       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33596       const longT whd = (longT)width()*height()*depth();
33597       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33598       for (longT N = 0; N<whd; ++N) {
33599         const Tfloat
33600           X = (Tfloat)p1[N],
33601           Y = (Tfloat)p2[N],
33602           Z = (Tfloat)p3[N],
33603           sum = X + Y + Z,
33604           nsum = sum>0?sum:1;
33605         p1[N] = (T)(X/nsum);
33606         p2[N] = (T)(Y/nsum);
33607         p3[N] = (T)Y;
33608       }
33609       return *this;
33610     }
33611 
33612     //! Convert pixel values from XYZ to xyY color spaces \newinstance.
33613     CImg<Tfloat> get_XYZtoxyY() const {
33614       return CImg<Tfloat>(*this,false).XYZtoxyY();
33615     }
33616 
33617     //! Convert pixel values from xyY pixels to XYZ color spaces.
33618     CImg<T>& xyYtoXYZ() {
33619       if (_spectrum!=3)
33620         throw CImgInstanceException(_cimg_instance
33621                                     "xyYtoXYZ(): Instance is not a xyY image.",
33622                                     cimg_instance);
33623 
33624       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33625       const longT whd = (longT)width()*height()*depth();
33626       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33627       for (longT N = 0; N<whd; ++N) {
33628         const Tfloat
33629          px = (Tfloat)p1[N],
33630          py = (Tfloat)p2[N],
33631          Y = (Tfloat)p3[N],
33632          ny = py>0?py:1;
33633         p1[N] = (T)(px*Y/ny);
33634         p2[N] = (T)Y;
33635         p3[N] = (T)((1 - px - py)*Y/ny);
33636       }
33637       return *this;
33638     }
33639 
33640     //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
33641     CImg<Tfloat> get_xyYtoXYZ() const {
33642       return CImg<Tfloat>(*this,false).xyYtoXYZ();
33643     }
33644 
33645     //! Convert pixel values from RGB to Lab color spaces.
33646     CImg<T>& RGBtoLab(const bool use_D65=true) {
33647       return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
33648     }
33649 
33650     //! Convert pixel values from RGB to Lab color spaces \newinstance.
33651     CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
33652       return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
33653     }
33654 
33655     //! Convert pixel values from Lab to RGB color spaces.
33656     CImg<T>& LabtoRGB(const bool use_D65=true) {
33657       return LabtoXYZ().XYZtoRGB(use_D65);
33658     }
33659 
33660     //! Convert pixel values from Lab to RGB color spaces \newinstance.
33661     CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
33662       return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
33663     }
33664 
33665     //! Convert pixel values from RGB to xyY color spaces.
33666     CImg<T>& RGBtoxyY(const bool use_D65=true) {
33667       return RGBtoXYZ(use_D65).XYZtoxyY();
33668     }
33669 
33670     //! Convert pixel values from RGB to xyY color spaces \newinstance.
33671     CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
33672       return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
33673     }
33674 
33675     //! Convert pixel values from xyY to RGB color spaces.
33676     CImg<T>& xyYtoRGB(const bool use_D65=true) {
33677       return xyYtoXYZ().XYZtoRGB(use_D65);
33678     }
33679 
33680     //! Convert pixel values from xyY to RGB color spaces \newinstance.
33681     CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
33682       return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
33683     }
33684 
33685     //! Convert pixel values from RGB to CMYK color spaces.
33686     CImg<T>& RGBtoCMYK() {
33687       return RGBtoCMY().CMYtoCMYK();
33688     }
33689 
33690     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
33691     CImg<Tfloat> get_RGBtoCMYK() const {
33692       return CImg<Tfloat>(*this,false).RGBtoCMYK();
33693     }
33694 
33695     //! Convert pixel values from CMYK to RGB color spaces.
33696     CImg<T>& CMYKtoRGB() {
33697       return CMYKtoCMY().CMYtoRGB();
33698     }
33699 
33700     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
33701     CImg<Tuchar> get_CMYKtoRGB() const {
33702       return CImg<Tuchar>(*this,false).CMYKtoRGB();
33703     }
33704 
33705     //@}
33706     //------------------------------------------
33707     //
33708     //! \name Geometric / Spatial Manipulation
33709     //@{
33710     //------------------------------------------
33711 
33712     static float _cimg_lanczos(const float x) {
33713       if (x<=-2 || x>=2) return 0;
33714       const float a = (float)cimg::PI*x, b = 0.5f*a;
33715       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
33716     }
33717 
33718     //! Resize image to new dimensions.
33719     /**
33720        \param size_x Number of columns (new size along the X-axis).
33721        \param size_y Number of rows (new size along the Y-axis).
33722        \param size_z Number of slices (new size along the Z-axis).
33723        \param size_c Number of vector-channels (new size along the C-axis).
33724        \param interpolation_type Method of interpolation:
33725        - -1 = no interpolation: raw memory resizing.
33726        - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
33727        - 1 = nearest-neighbor interpolation.
33728        - 2 = moving average interpolation.
33729        - 3 = linear interpolation.
33730        - 4 = grid interpolation.
33731        - 5 = cubic interpolation.
33732        - 6 = lanczos interpolation.
33733        \param boundary_conditions Type of boundary conditions used if necessary.
33734        \param centering_x Set centering type (only if \p interpolation_type=0).
33735        \param centering_y Set centering type (only if \p interpolation_type=0).
33736        \param centering_z Set centering type (only if \p interpolation_type=0).
33737        \param centering_c Set centering type (only if \p interpolation_type=0).
33738        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
33739     **/
33740     CImg<T>& resize(const int size_x, const int size_y=-100,
33741                     const int size_z=-100, const int size_c=-100,
33742                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
33743                     const float centering_x = 0, const float centering_y = 0,
33744                     const float centering_z = 0, const float centering_c = 0) {
33745       if (!size_x || !size_y || !size_z || !size_c) return assign();
33746       const unsigned int
33747         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
33748         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
33749         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
33750         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
33751         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
33752       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
33753       if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
33754       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
33755         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
33756         return *this;
33757       }
33758       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
33759                         centering_x,centering_y,centering_z,centering_c).move_to(*this);
33760     }
33761 
33762     //! Resize image to new dimensions \newinstance.
33763     CImg<T> get_resize(const int size_x, const int size_y = -100,
33764                        const int size_z = -100, const int size_c = -100,
33765                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
33766                        const float centering_x = 0, const float centering_y = 0,
33767                        const float centering_z = 0, const float centering_c = 0) const {
33768       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
33769           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
33770         throw CImgArgumentException(_cimg_instance
33771                                     "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
33772                                     cimg_instance,
33773                                     centering_x,centering_y,centering_z,centering_c);
33774 
33775       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
33776       const unsigned int
33777         sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
33778         sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
33779         sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
33780         sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
33781       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
33782       if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
33783       CImg<T> res;
33784       switch (interpolation_type) {
33785 
33786         // Raw resizing.
33787         //
33788       case -1 :
33789         std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
33790         break;
33791 
33792         // No interpolation.
33793         //
33794       case 0 : {
33795         const int
33796           xc = (int)(centering_x*((int)sx - width())),
33797           yc = (int)(centering_y*((int)sy - height())),
33798           zc = (int)(centering_z*((int)sz - depth())),
33799           cc = (int)(centering_c*((int)sc - spectrum()));
33800 
33801         switch (boundary_conditions) {
33802         case 3 : { // Mirror
33803           res.assign(sx,sy,sz,sc);
33804           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
33805           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
33806           cimg_forXYZC(res,x,y,z,c) {
33807             const int
33808               mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
33809               mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
33810             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
33811                                    my<height()?my:h2 - my - 1,
33812                                    mz<depth()?mz:d2 - mz - 1,
33813                                    mc<spectrum()?mc:s2 - mc - 1);
33814           }
33815         } break;
33816         case 2 : { // Periodic
33817           res.assign(sx,sy,sz,sc);
33818           const int
33819             x0 = ((int)xc%width()) - width(),
33820             y0 = ((int)yc%height()) - height(),
33821             z0 = ((int)zc%depth()) - depth(),
33822             c0 = ((int)cc%spectrum()) - spectrum(),
33823             dx = width(), dy = height(), dz = depth(), dc = spectrum();
33824           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
33825           for (int c = c0; c<(int)sc; c+=dc)
33826             for (int z = z0; z<(int)sz; z+=dz)
33827               for (int y = y0; y<(int)sy; y+=dy)
33828                 for (int x = x0; x<(int)sx; x+=dx)
33829                   res.draw_image(x,y,z,c,*this);
33830         } break;
33831         case 1 : { // Neumann
33832           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
33833           CImg<T> sprite;
33834           if (xc>0) {  // X-backward
33835             res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33836             for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
33837           }
33838           if (xc + width()<(int)sx) { // X-forward
33839             res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
33840                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33841             for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
33842           }
33843           if (yc>0) {  // Y-backward
33844             res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33845             for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
33846           }
33847           if (yc + height()<(int)sy) { // Y-forward
33848             res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
33849                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33850             for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
33851           }
33852           if (zc>0) {  // Z-backward
33853             res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
33854             for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
33855           }
33856           if (zc + depth()<(int)sz) { // Z-forward
33857             res.get_crop(0,0,zc  +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33858             for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
33859           }
33860           if (cc>0) {  // C-backward
33861             res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
33862             for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
33863           }
33864           if (cc + spectrum()<(int)sc) { // C-forward
33865             res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
33866             for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
33867           }
33868         } break;
33869         default : // Dirichlet
33870           res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
33871         }
33872         break;
33873       } break;
33874 
33875         // Nearest neighbor interpolation.
33876         //
33877       case 1 : {
33878         res.assign(sx,sy,sz,sc);
33879         CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
33880         const ulongT
33881           wh = (ulongT)_width*_height,
33882           whd = (ulongT)_width*_height*_depth,
33883           sxy = (ulongT)sx*sy,
33884           sxyz = (ulongT)sx*sy*sz,
33885           one = (ulongT)1;
33886         if (sx==_width) off_x.fill(1);
33887         else {
33888           ulongT *poff_x = off_x._data, curr = 0;
33889           cimg_forX(res,x) {
33890             const ulongT old = curr;
33891             curr = (x + one)*_width/sx;
33892             *(poff_x++) = curr - old;
33893           }
33894         }
33895         if (sy==_height) off_y.fill(_width);
33896         else {
33897           ulongT *poff_y = off_y._data, curr = 0;
33898           cimg_forY(res,y) {
33899             const ulongT old = curr;
33900             curr = (y + one)*_height/sy;
33901             *(poff_y++) = _width*(curr - old);
33902           }
33903           *poff_y = 0;
33904         }
33905         if (sz==_depth) off_z.fill(wh);
33906         else {
33907           ulongT *poff_z = off_z._data, curr = 0;
33908           cimg_forZ(res,z) {
33909             const ulongT old = curr;
33910             curr = (z + one)*_depth/sz;
33911             *(poff_z++) = wh*(curr - old);
33912           }
33913           *poff_z = 0;
33914         }
33915         if (sc==_spectrum) off_c.fill(whd);
33916         else {
33917           ulongT *poff_c = off_c._data, curr = 0;
33918           cimg_forC(res,c) {
33919             const ulongT old = curr;
33920             curr = (c + one)*_spectrum/sc;
33921             *(poff_c++) = whd*(curr - old);
33922           }
33923           *poff_c = 0;
33924         }
33925 
33926         T *ptrd = res._data;
33927         const T* ptrc = _data;
33928         const ulongT *poff_c = off_c._data;
33929         for (unsigned int c = 0; c<sc; ) {
33930           const T *ptrz = ptrc;
33931           const ulongT *poff_z = off_z._data;
33932           for (unsigned int z = 0; z<sz; ) {
33933             const T *ptry = ptrz;
33934             const ulongT *poff_y = off_y._data;
33935             for (unsigned int y = 0; y<sy; ) {
33936               const T *ptrx = ptry;
33937               const ulongT *poff_x = off_x._data;
33938               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
33939               ++y;
33940               ulongT dy = *(poff_y++);
33941               for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
33942               ptry+=dy;
33943             }
33944             ++z;
33945             ulongT dz = *(poff_z++);
33946             for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
33947             ptrz+=dz;
33948           }
33949           ++c;
33950           ulongT dc = *(poff_c++);
33951           for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
33952           ptrc+=dc;
33953         }
33954       } break;
33955 
33956         // Moving average.
33957         //
33958       case 2 : {
33959         bool instance_first = true;
33960         if (sx!=_width) {
33961           if (sx>_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res);
33962           else {
33963             CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
33964             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
33965                                cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256))
33966             cimg_forYZC(tmp,y,z,v) {
33967               for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
33968                 const unsigned int d = std::min(b,c);
33969                 a-=d; b-=d; c-=d;
33970                 tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
33971                 if (!b) { tmp(t++,y,z,v)/=_width; b = _width; }
33972                 if (!c) { ++s; c = sx; }
33973               }
33974             }
33975             tmp.move_to(res);
33976           }
33977           instance_first = false;
33978         }
33979 
33980         if (sy!=_height) {
33981           if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res);
33982           else {
33983             CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
33984             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
33985                                cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256))
33986             cimg_forXZC(tmp,x,z,v) {
33987               for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
33988                 const unsigned int d = std::min(b,c);
33989                 a-=d; b-=d; c-=d;
33990                 if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
33991                 else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
33992                 if (!b) { tmp(x,t++,z,v)/=_height; b = _height; }
33993                 if (!c) { ++s; c = sy; }
33994               }
33995             }
33996             tmp.move_to(res);
33997           }
33998           instance_first = false;
33999         }
34000 
34001         if (sz!=_depth) {
34002           if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res);
34003           else {
34004             CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
34005             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34006                                cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256))
34007             cimg_forXYC(tmp,x,y,v) {
34008               for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
34009                 const unsigned int d = std::min(b,c);
34010                 a-=d; b-=d; c-=d;
34011                 if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
34012                 else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
34013                 if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; }
34014                 if (!c) { ++s; c = sz; }
34015               }
34016             }
34017             tmp.move_to(res);
34018           }
34019           instance_first = false;
34020         }
34021 
34022         if (sc!=_spectrum) {
34023           if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res);
34024           else {
34025             CImg<Tfloat> tmp(sx,sy,sz,sc,0);
34026             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34027                                cimg_openmp_if(sc>=256 && _width*_height*_depth>=256))
34028             cimg_forXYZ(tmp,x,y,z) {
34029               for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
34030                 const unsigned int d = std::min(b,c);
34031                 a-=d; b-=d; c-=d;
34032                 if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
34033                 else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
34034                 if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; }
34035                 if (!c) { ++s; c = sc; }
34036               }
34037             }
34038             tmp.move_to(res);
34039           }
34040           instance_first = false;
34041         }
34042 
34043       } break;
34044 
34045         // Linear interpolation.
34046         //
34047       case 3 : {
34048         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34049         CImg<doubleT> foff(off._width);
34050         CImg<T> resx, resy, resz, resc;
34051         double curr, old;
34052 
34053         if (sx!=_width) {
34054           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34055           else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34056           else {
34057             const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34058               (double)_width/sx;
34059             resx.assign(sx,_height,_depth,_spectrum);
34060             curr = old = 0;
34061             {
34062               unsigned int *poff = off._data;
34063               double *pfoff = foff._data;
34064               cimg_forX(resx,x) {
34065                 *(pfoff++) = curr - (unsigned int)curr;
34066                 old = curr;
34067                 curr = std::min(width() - 1.,curr + fx);
34068                 *(poff++) = (unsigned int)curr - (unsigned int)old;
34069               }
34070             }
34071             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34072                                cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34073               cimg_forYZC(resx,y,z,c) {
34074               const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
34075               T *ptrd = resx.data(0,y,z,c);
34076               const unsigned int *poff = off._data;
34077               const double *pfoff = foff._data;
34078               cimg_forX(resx,x) {
34079                 const double alpha = *(pfoff++);
34080                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
34081                 *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
34082                 ptrs+=*(poff++);
34083               }
34084             }
34085           }
34086         } else resx.assign(*this,true);
34087 
34088         if (sy!=_height) {
34089           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34090           else {
34091             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34092             else {
34093               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34094                 (double)_height/sy;
34095               resy.assign(sx,sy,_depth,_spectrum);
34096               curr = old = 0;
34097               {
34098                 unsigned int *poff = off._data;
34099                 double *pfoff = foff._data;
34100                 cimg_forY(resy,y) {
34101                   *(pfoff++) = curr - (unsigned int)curr;
34102                   old = curr;
34103                   curr = std::min(height() - 1.,curr + fy);
34104                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34105                 }
34106               }
34107               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34108                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34109               cimg_forXZC(resy,x,z,c) {
34110                 const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
34111                 T *ptrd = resy.data(x,0,z,c);
34112                 const unsigned int *poff = off._data;
34113                 const double *pfoff = foff._data;
34114                 cimg_forY(resy,y) {
34115                   const double alpha = *(pfoff++);
34116                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
34117                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34118                   ptrd+=sx;
34119                   ptrs+=*(poff++);
34120                 }
34121               }
34122             }
34123           }
34124           resx.assign();
34125         } else resy.assign(resx,true);
34126 
34127         if (sz!=_depth) {
34128           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34129           else {
34130             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34131             else {
34132               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34133                 (double)_depth/sz;
34134               const unsigned int sxy = sx*sy;
34135               resz.assign(sx,sy,sz,_spectrum);
34136               curr = old = 0;
34137               {
34138                 unsigned int *poff = off._data;
34139                 double *pfoff = foff._data;
34140                 cimg_forZ(resz,z) {
34141                   *(pfoff++) = curr - (unsigned int)curr;
34142                   old = curr;
34143                   curr = std::min(depth() - 1.,curr + fz);
34144                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34145                 }
34146               }
34147               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34148                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34149               cimg_forXYC(resz,x,y,c) {
34150                 const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
34151                 T *ptrd = resz.data(x,y,0,c);
34152                 const unsigned int *poff = off._data;
34153                 const double *pfoff = foff._data;
34154                 cimg_forZ(resz,z) {
34155                   const double alpha = *(pfoff++);
34156                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
34157                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34158                   ptrd+=sxy;
34159                   ptrs+=*(poff++);
34160                 }
34161               }
34162             }
34163           }
34164           resy.assign();
34165         } else resz.assign(resy,true);
34166 
34167         if (sc!=_spectrum) {
34168           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34169           else {
34170             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34171             else {
34172               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34173                 (double)_spectrum/sc;
34174               const unsigned int sxyz = sx*sy*sz;
34175               resc.assign(sx,sy,sz,sc);
34176               curr = old = 0;
34177               {
34178                 unsigned int *poff = off._data;
34179                 double *pfoff = foff._data;
34180                 cimg_forC(resc,c) {
34181                   *(pfoff++) = curr - (unsigned int)curr;
34182                   old = curr;
34183                   curr = std::min(spectrum() - 1.,curr + fc);
34184                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34185                 }
34186               }
34187               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34188                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
34189               cimg_forXYZ(resc,x,y,z) {
34190                 const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
34191                 T *ptrd = resc.data(x,y,z,0);
34192                 const unsigned int *poff = off._data;
34193                 const double *pfoff = foff._data;
34194                 cimg_forC(resc,c) {
34195                   const double alpha = *(pfoff++);
34196                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
34197                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34198                   ptrd+=sxyz;
34199                   ptrs+=*(poff++);
34200                 }
34201               }
34202             }
34203           }
34204           resz.assign();
34205         } else resc.assign(resz,true);
34206         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34207       } break;
34208 
34209         // Grid interpolation.
34210         //
34211       case 4 : {
34212         CImg<T> resx, resy, resz, resc;
34213         if (sx!=_width) {
34214           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34215           else {
34216             resx.assign(sx,_height,_depth,_spectrum,(T)0);
34217             const int dx = (int)(2*sx), dy = 2*width();
34218             int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
34219             cimg_forX(resx,x) if ((err-=dy)<=0) {
34220               cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
34221               ++xs;
34222               err+=dx;
34223             }
34224           }
34225         } else resx.assign(*this,true);
34226 
34227         if (sy!=_height) {
34228           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34229           else {
34230             resy.assign(sx,sy,_depth,_spectrum,(T)0);
34231             const int dx = (int)(2*sy), dy = 2*height();
34232             int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
34233             cimg_forY(resy,y) if ((err-=dy)<=0) {
34234               cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
34235               ++ys;
34236               err+=dx;
34237             }
34238           }
34239           resx.assign();
34240         } else resy.assign(resx,true);
34241 
34242         if (sz!=_depth) {
34243           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34244           else {
34245             resz.assign(sx,sy,sz,_spectrum,(T)0);
34246             const int dx = (int)(2*sz), dy = 2*depth();
34247             int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
34248             cimg_forZ(resz,z) if ((err-=dy)<=0) {
34249               cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
34250               ++zs;
34251               err+=dx;
34252             }
34253           }
34254           resy.assign();
34255         } else resz.assign(resy,true);
34256 
34257         if (sc!=_spectrum) {
34258           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34259           else {
34260             resc.assign(sx,sy,sz,sc,(T)0);
34261             const int dx = (int)(2*sc), dy = 2*spectrum();
34262             int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
34263             cimg_forC(resc,c) if ((err-=dy)<=0) {
34264               cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
34265               ++cs;
34266               err+=dx;
34267             }
34268           }
34269           resz.assign();
34270         } else resc.assign(resz,true);
34271 
34272         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34273       } break;
34274 
34275         // Cubic interpolation.
34276         //
34277       case 5 : {
34278         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
34279         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34280         CImg<doubleT> foff(off._width);
34281         CImg<T> resx, resy, resz, resc;
34282         double curr, old;
34283 
34284         if (sx!=_width) {
34285           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34286           else {
34287             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34288             else {
34289               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34290                 (double)_width/sx;
34291               resx.assign(sx,_height,_depth,_spectrum);
34292               curr = old = 0;
34293               {
34294                 unsigned int *poff = off._data;
34295                 double *pfoff = foff._data;
34296                 cimg_forX(resx,x) {
34297                   *(pfoff++) = curr - (unsigned int)curr;
34298                   old = curr;
34299                   curr = std::min(width() - 1.,curr + fx);
34300                   *(poff++) = (unsigned int)curr - (unsigned int)old;
34301                 }
34302               }
34303               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34304                                  cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34305               cimg_forYZC(resx,y,z,c) {
34306                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
34307                 T *ptrd = resx.data(0,y,z,c);
34308                 const unsigned int *poff = off._data;
34309                 const double *pfoff = foff._data;
34310                 cimg_forX(resx,x) {
34311                   const double
34312                     t = *(pfoff++),
34313                     val1 = (double)*ptrs,
34314                     val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
34315                     val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
34316                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
34317                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34318                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34319                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
34320                   ptrs+=*(poff++);
34321                 }
34322               }
34323             }
34324           }
34325         } else resx.assign(*this,true);
34326 
34327         if (sy!=_height) {
34328           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34329           else {
34330             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34331             else {
34332               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34333                 (double)_height/sy;
34334               resy.assign(sx,sy,_depth,_spectrum);
34335               curr = old = 0;
34336               {
34337                 unsigned int *poff = off._data;
34338                 double *pfoff = foff._data;
34339                 cimg_forY(resy,y) {
34340                   *(pfoff++) = curr - (unsigned int)curr;
34341                   old = curr;
34342                   curr = std::min(height() - 1.,curr + fy);
34343                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34344                 }
34345               }
34346               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34347                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34348               cimg_forXZC(resy,x,z,c) {
34349                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
34350                 T *ptrd = resy.data(x,0,z,c);
34351                 const unsigned int *poff = off._data;
34352                 const double *pfoff = foff._data;
34353                 cimg_forY(resy,y) {
34354                   const double
34355                     t = *(pfoff++),
34356                     val1 = (double)*ptrs,
34357                     val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
34358                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
34359                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
34360                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34361                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34362                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34363                   ptrd+=sx;
34364                   ptrs+=*(poff++);
34365                 }
34366               }
34367             }
34368           }
34369           resx.assign();
34370         } else resy.assign(resx,true);
34371 
34372         if (sz!=_depth) {
34373           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34374           else {
34375             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34376             else {
34377               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34378                 (double)_depth/sz;
34379               const unsigned int sxy = sx*sy;
34380               resz.assign(sx,sy,sz,_spectrum);
34381               curr = old = 0;
34382               {
34383                 unsigned int *poff = off._data;
34384                 double *pfoff = foff._data;
34385                 cimg_forZ(resz,z) {
34386                   *(pfoff++) = curr - (unsigned int)curr;
34387                   old = curr;
34388                   curr = std::min(depth() - 1.,curr + fz);
34389                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34390                 }
34391               }
34392               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34393                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34394               cimg_forXYC(resz,x,y,c) {
34395                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
34396                 T *ptrd = resz.data(x,y,0,c);
34397                 const unsigned int *poff = off._data;
34398                 const double *pfoff = foff._data;
34399                 cimg_forZ(resz,z) {
34400                   const double
34401                     t = *(pfoff++),
34402                     val1 = (double)*ptrs,
34403                     val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
34404                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
34405                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
34406                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34407                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34408                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34409                   ptrd+=sxy;
34410                   ptrs+=*(poff++);
34411                 }
34412               }
34413             }
34414           }
34415           resy.assign();
34416         } else resz.assign(resy,true);
34417 
34418         if (sc!=_spectrum) {
34419           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34420           else {
34421             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34422             else {
34423               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34424                 (double)_spectrum/sc;
34425               const unsigned int sxyz = sx*sy*sz;
34426               resc.assign(sx,sy,sz,sc);
34427               curr = old = 0;
34428               {
34429                 unsigned int *poff = off._data;
34430                 double *pfoff = foff._data;
34431                 cimg_forC(resc,c) {
34432                   *(pfoff++) = curr - (unsigned int)curr;
34433                   old = curr;
34434                   curr = std::min(spectrum() - 1.,curr + fc);
34435                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34436                 }
34437               }
34438               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34439                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
34440               cimg_forXYZ(resc,x,y,z) {
34441                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
34442                 T *ptrd = resc.data(x,y,z,0);
34443                 const unsigned int *poff = off._data;
34444                 const double *pfoff = foff._data;
34445                 cimg_forC(resc,c) {
34446                   const double
34447                     t = *(pfoff++),
34448                     val1 = (double)*ptrs,
34449                     val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
34450                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
34451                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
34452                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34453                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34454                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34455                   ptrd+=sxyz;
34456                   ptrs+=*(poff++);
34457                 }
34458               }
34459             }
34460           }
34461           resz.assign();
34462         } else resc.assign(resz,true);
34463 
34464         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34465       } break;
34466 
34467         // Lanczos interpolation.
34468         //
34469       case 6 : {
34470         const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
34471         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34472         CImg<doubleT> foff(off._width);
34473         CImg<T> resx, resy, resz, resc;
34474         double curr, old;
34475 
34476         if (sx!=_width) {
34477           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34478           else {
34479             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34480             else {
34481               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34482                 (double)_width/sx;
34483               resx.assign(sx,_height,_depth,_spectrum);
34484               curr = old = 0;
34485               {
34486                 unsigned int *poff = off._data;
34487                 double *pfoff = foff._data;
34488                 cimg_forX(resx,x) {
34489                   *(pfoff++) = curr - (unsigned int)curr;
34490                   old = curr;
34491                   curr = std::min(width() - 1.,curr + fx);
34492                   *(poff++) = (unsigned int)curr - (unsigned int)old;
34493                 }
34494               }
34495               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34496                                  cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34497               cimg_forYZC(resx,y,z,c) {
34498                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
34499                   *const ptrsmax = ptrs0 + (_width - 2);
34500                 T *ptrd = resx.data(0,y,z,c);
34501                 const unsigned int *poff = off._data;
34502                 const double *pfoff = foff._data;
34503                 cimg_forX(resx,x) {
34504                   const double
34505                     t = *(pfoff++),
34506                     w0 = _cimg_lanczos(t + 2),
34507                     w1 = _cimg_lanczos(t + 1),
34508                     w2 = _cimg_lanczos(t),
34509                     w3 = _cimg_lanczos(t - 1),
34510                     w4 = _cimg_lanczos(t - 2),
34511                     val2 = (double)*ptrs,
34512                     val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
34513                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
34514                     val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
34515                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
34516                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34517                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
34518                   ptrs+=*(poff++);
34519                 }
34520               }
34521             }
34522           }
34523         } else resx.assign(*this,true);
34524 
34525         if (sy!=_height) {
34526           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34527           else {
34528             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34529             else {
34530               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34531                 (double)_height/sy;
34532               resy.assign(sx,sy,_depth,_spectrum);
34533               curr = old = 0;
34534               {
34535                 unsigned int *poff = off._data;
34536                 double *pfoff = foff._data;
34537                 cimg_forY(resy,y) {
34538                   *(pfoff++) = curr - (unsigned int)curr;
34539                   old = curr;
34540                   curr = std::min(height() - 1.,curr + fy);
34541                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34542                 }
34543               }
34544               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34545                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34546               cimg_forXZC(resy,x,z,c) {
34547                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
34548                   *const ptrsmax = ptrs0 + (_height - 2)*sx;
34549                 T *ptrd = resy.data(x,0,z,c);
34550                 const unsigned int *poff = off._data;
34551                 const double *pfoff = foff._data;
34552                 cimg_forY(resy,y) {
34553                   const double
34554                     t = *(pfoff++),
34555                     w0 = _cimg_lanczos(t + 2),
34556                     w1 = _cimg_lanczos(t + 1),
34557                     w2 = _cimg_lanczos(t),
34558                     w3 = _cimg_lanczos(t - 1),
34559                     w4 = _cimg_lanczos(t - 2),
34560                     val2 = (double)*ptrs,
34561                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
34562                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
34563                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
34564                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
34565                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34566                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34567                   ptrd+=sx;
34568                   ptrs+=*(poff++);
34569                 }
34570               }
34571             }
34572           }
34573           resx.assign();
34574         } else resy.assign(resx,true);
34575 
34576         if (sz!=_depth) {
34577           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34578           else {
34579             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34580             else {
34581               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34582                 (double)_depth/sz;
34583               const unsigned int sxy = sx*sy;
34584               resz.assign(sx,sy,sz,_spectrum);
34585               curr = old = 0;
34586               {
34587                 unsigned int *poff = off._data;
34588                 double *pfoff = foff._data;
34589                 cimg_forZ(resz,z) {
34590                   *(pfoff++) = curr - (unsigned int)curr;
34591                   old = curr;
34592                   curr = std::min(depth() - 1.,curr + fz);
34593                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34594                 }
34595               }
34596               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34597                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34598               cimg_forXYC(resz,x,y,c) {
34599                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
34600                   *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
34601                 T *ptrd = resz.data(x,y,0,c);
34602                 const unsigned int *poff = off._data;
34603                 const double *pfoff = foff._data;
34604                 cimg_forZ(resz,z) {
34605                   const double
34606                     t = *(pfoff++),
34607                     w0 = _cimg_lanczos(t + 2),
34608                     w1 = _cimg_lanczos(t + 1),
34609                     w2 = _cimg_lanczos(t),
34610                     w3 = _cimg_lanczos(t - 1),
34611                     w4 = _cimg_lanczos(t - 2),
34612                     val2 = (double)*ptrs,
34613                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
34614                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
34615                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
34616                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
34617                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34618                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34619                   ptrd+=sxy;
34620                   ptrs+=*(poff++);
34621                 }
34622               }
34623             }
34624           }
34625           resy.assign();
34626         } else resz.assign(resy,true);
34627 
34628         if (sc!=_spectrum) {
34629           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34630           else {
34631             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34632             else {
34633               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34634                 (double)_spectrum/sc;
34635               const unsigned int sxyz = sx*sy*sz;
34636               resc.assign(sx,sy,sz,sc);
34637               curr = old = 0;
34638               {
34639                 unsigned int *poff = off._data;
34640                 double *pfoff = foff._data;
34641                 cimg_forC(resc,c) {
34642                   *(pfoff++) = curr - (unsigned int)curr;
34643                   old = curr;
34644                   curr = std::min(spectrum() - 1.,curr + fc);
34645                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34646                 }
34647               }
34648               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34649                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
34650               cimg_forXYZ(resc,x,y,z) {
34651                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
34652                   *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
34653                 T *ptrd = resc.data(x,y,z,0);
34654                 const unsigned int *poff = off._data;
34655                 const double *pfoff = foff._data;
34656                 cimg_forC(resc,c) {
34657                   const double
34658                     t = *(pfoff++),
34659                     w0 = _cimg_lanczos(t + 2),
34660                     w1 = _cimg_lanczos(t + 1),
34661                     w2 = _cimg_lanczos(t),
34662                     w3 = _cimg_lanczos(t - 1),
34663                     w4 = _cimg_lanczos(t - 2),
34664                     val2 = (double)*ptrs,
34665                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
34666                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
34667                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
34668                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
34669                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34670                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34671                   ptrd+=sxyz;
34672                   ptrs+=*(poff++);
34673                 }
34674               }
34675             }
34676           }
34677           resz.assign();
34678         } else resc.assign(resz,true);
34679 
34680         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34681       } break;
34682 
34683         // Unknown interpolation.
34684         //
34685       default :
34686         throw CImgArgumentException(_cimg_instance
34687                                     "resize(): Invalid specified interpolation %d "
34688                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
34689                                     "5=cubic | 6=lanczos }).",
34690                                     cimg_instance,
34691                                     interpolation_type);
34692       }
34693       return res;
34694     }
34695 
34696     //! Resize image to dimensions of another image.
34697     /**
34698        \param src Reference image used for dimensions.
34699        \param interpolation_type Interpolation method.
34700        \param boundary_conditions Boundary conditions.
34701        \param centering_x Set centering type (only if \p interpolation_type=0).
34702        \param centering_y Set centering type (only if \p interpolation_type=0).
34703        \param centering_z Set centering type (only if \p interpolation_type=0).
34704        \param centering_c Set centering type (only if \p interpolation_type=0).
34705      **/
34706     template<typename t>
34707     CImg<T>& resize(const CImg<t>& src,
34708                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
34709                     const float centering_x = 0, const float centering_y = 0,
34710                     const float centering_z = 0, const float centering_c = 0) {
34711       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
34712                     centering_x,centering_y,centering_z,centering_c);
34713     }
34714 
34715     //! Resize image to dimensions of another image \newinstance.
34716     template<typename t>
34717     CImg<T> get_resize(const CImg<t>& src,
34718                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
34719                        const float centering_x = 0, const float centering_y = 0,
34720                        const float centering_z = 0, const float centering_c = 0) const {
34721       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
34722                         centering_x,centering_y,centering_z,centering_c);
34723     }
34724 
34725     //! Resize image to dimensions of a display window.
34726     /**
34727        \param disp Reference display window used for dimensions.
34728        \param interpolation_type Interpolation method.
34729        \param boundary_conditions Boundary conditions.
34730        \param centering_x Set centering type (only if \p interpolation_type=0).
34731        \param centering_y Set centering type (only if \p interpolation_type=0).
34732        \param centering_z Set centering type (only if \p interpolation_type=0).
34733        \param centering_c Set centering type (only if \p interpolation_type=0).
34734      **/
34735     CImg<T>& resize(const CImgDisplay& disp,
34736                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
34737                     const float centering_x = 0, const float centering_y = 0,
34738                     const float centering_z = 0, const float centering_c = 0) {
34739       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
34740                     centering_x,centering_y,centering_z,centering_c);
34741     }
34742 
34743     //! Resize image to dimensions of a display window \newinstance.
34744     CImg<T> get_resize(const CImgDisplay& disp,
34745                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
34746                        const float centering_x = 0, const float centering_y = 0,
34747                        const float centering_z = 0, const float centering_c = 0) const {
34748       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
34749                         centering_x,centering_y,centering_z,centering_c);
34750     }
34751 
34752     //! Resize image to half-size along XY axes, using an optimized filter.
34753     CImg<T>& resize_halfXY() {
34754       return get_resize_halfXY().move_to(*this);
34755     }
34756 
34757     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
34758     CImg<T> get_resize_halfXY() const {
34759       if (is_empty()) return *this;
34760       static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
34761                                         0.1231940459f,  0.1935127547f, 0.1231940459f,
34762                                         0.07842776544f, 0.1231940459f, 0.07842776544f };
34763       CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
34764       T *ptrd = res._data;
34765       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
34766         if (x%2 && y%2) *(ptrd++) = (T)
34767                           (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
34768                            I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
34769                            I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
34770       return res;
34771     }
34772 
34773     //! Resize image to double-size, using the Scale2X algorithm.
34774     /**
34775        \note Use anisotropic upscaling algorithm
34776        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
34777     **/
34778     CImg<T>& resize_doubleXY() {
34779       return get_resize_doubleXY().move_to(*this);
34780     }
34781 
34782     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
34783     CImg<T> get_resize_doubleXY() const {
34784 #define _cimg_gs2x_for3(bound,i) \
34785  for (int i = 0, _p1##i = 0, \
34786       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
34787       _n1##i<(int)(bound) || i==--_n1##i; \
34788       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
34789 
34790 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
34791   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
34792    _p1##x = 0, \
34793    _n1##x = (int)( \
34794    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
34795    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
34796    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
34797    1>=(img)._width?(img).width() - 1:1); \
34798    (_n1##x<(img).width() && ( \
34799    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
34800    (I[5] = (T)(img)(_n1##x,y,z,c)), \
34801    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
34802    x==--_n1##x; \
34803    I[1] = I[2], \
34804    I[3] = I[4], I[4] = I[5], \
34805    I[7] = I[8], \
34806    _p1##x = x++, ++_n1##x)
34807 
34808       if (is_empty()) return *this;
34809       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
34810       CImg_3x3(I,T);
34811       cimg_forZC(*this,z,c) {
34812         T
34813           *ptrd1 = res.data(0,0,z,c),
34814           *ptrd2 = ptrd1 + res._width;
34815         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
34816           if (Icp!=Icn && Ipc!=Inc) {
34817             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
34818             *(ptrd1++) = Icp==Inc?Inc:Icc;
34819             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
34820             *(ptrd2++) = Icn==Inc?Inc:Icc;
34821           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
34822         }
34823       }
34824       return res;
34825     }
34826 
34827     //! Resize image to triple-size, using the Scale3X algorithm.
34828     /**
34829        \note Use anisotropic upscaling algorithm
34830        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
34831     **/
34832     CImg<T>& resize_tripleXY() {
34833       return get_resize_tripleXY().move_to(*this);
34834     }
34835 
34836     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
34837     CImg<T> get_resize_tripleXY() const {
34838 #define _cimg_gs3x_for3(bound,i) \
34839  for (int i = 0, _p1##i = 0, \
34840       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
34841       _n1##i<(int)(bound) || i==--_n1##i; \
34842       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
34843 
34844 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
34845   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
34846    _p1##x = 0, \
34847    _n1##x = (int)( \
34848    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
34849    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
34850    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
34851    1>=(img)._width?(img).width() - 1:1); \
34852    (_n1##x<(img).width() && ( \
34853    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
34854    (I[5] = (T)(img)(_n1##x,y,z,c)), \
34855    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
34856    x==--_n1##x; \
34857    I[0] = I[1], I[1] = I[2], \
34858    I[3] = I[4], I[4] = I[5], \
34859    I[6] = I[7], I[7] = I[8], \
34860    _p1##x = x++, ++_n1##x)
34861 
34862       if (is_empty()) return *this;
34863       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
34864       CImg_3x3(I,T);
34865       cimg_forZC(*this,z,c) {
34866         T
34867           *ptrd1 = res.data(0,0,z,c),
34868           *ptrd2 = ptrd1 + res._width,
34869           *ptrd3 = ptrd2 + res._width;
34870         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
34871           if (Icp != Icn && Ipc != Inc) {
34872             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
34873             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
34874             *(ptrd1++) = Icp==Inc?Inc:Icc;
34875             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
34876             *(ptrd2++) = Icc;
34877             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
34878             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
34879             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
34880             *(ptrd3++) = Icn==Inc?Inc:Icc;
34881           } else {
34882             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
34883             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
34884             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
34885           }
34886         }
34887       }
34888       return res;
34889     }
34890 
34891     //! Mirror image content along specified axis.
34892     /**
34893        \param axis Mirror axis
34894     **/
34895     CImg<T>& mirror(const char axis) {
34896       if (is_empty()) return *this;
34897       T *pf, *pb, *buf = 0;
34898       switch (cimg::lowercase(axis)) {
34899       case 'x' : {
34900         pf = _data; pb = data(_width - 1);
34901         const unsigned int width2 = _width/2;
34902         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
34903           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
34904           pf+=_width - width2;
34905           pb+=_width + width2;
34906         }
34907       } break;
34908       case 'y' : {
34909         buf = new T[_width];
34910         pf = _data; pb = data(0,_height - 1);
34911         const unsigned int height2 = _height/2;
34912         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
34913           for (unsigned int y = 0; y<height2; ++y) {
34914             std::memcpy(buf,pf,_width*sizeof(T));
34915             std::memcpy(pf,pb,_width*sizeof(T));
34916             std::memcpy(pb,buf,_width*sizeof(T));
34917             pf+=_width;
34918             pb-=_width;
34919           }
34920           pf+=(ulongT)_width*(_height - height2);
34921           pb+=(ulongT)_width*(_height + height2);
34922         }
34923       } break;
34924       case 'z' : {
34925         buf = new T[(ulongT)_width*_height];
34926         pf = _data; pb = data(0,0,_depth - 1);
34927         const unsigned int depth2 = _depth/2;
34928         cimg_forC(*this,c) {
34929           for (unsigned int z = 0; z<depth2; ++z) {
34930             std::memcpy(buf,pf,_width*_height*sizeof(T));
34931             std::memcpy(pf,pb,_width*_height*sizeof(T));
34932             std::memcpy(pb,buf,_width*_height*sizeof(T));
34933             pf+=(ulongT)_width*_height;
34934             pb-=(ulongT)_width*_height;
34935           }
34936           pf+=(ulongT)_width*_height*(_depth - depth2);
34937           pb+=(ulongT)_width*_height*(_depth + depth2);
34938         }
34939       } break;
34940       case 'c' : {
34941         buf = new T[(ulongT)_width*_height*_depth];
34942         pf = _data; pb = data(0,0,0,_spectrum - 1);
34943         const unsigned int _spectrum2 = _spectrum/2;
34944         for (unsigned int v = 0; v<_spectrum2; ++v) {
34945           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
34946           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
34947           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
34948           pf+=(ulongT)_width*_height*_depth;
34949           pb-=(ulongT)_width*_height*_depth;
34950         }
34951       } break;
34952       default :
34953         throw CImgArgumentException(_cimg_instance
34954                                     "mirror(): Invalid specified axis '%c'.",
34955                                     cimg_instance,
34956                                     axis);
34957       }
34958       delete[] buf;
34959       return *this;
34960     }
34961 
34962     //! Mirror image content along specified axis \newinstance.
34963     CImg<T> get_mirror(const char axis) const {
34964       return (+*this).mirror(axis);
34965     }
34966 
34967     //! Mirror image content along specified axes.
34968     /**
34969        \param axes Mirror axes, as a C-string.
34970        \note \c axes may contains multiple characters, e.g. \c "xyz"
34971     **/
34972     CImg<T>& mirror(const char *const axes) {
34973       for (const char *s = axes; *s; ++s) mirror(*s);
34974       return *this;
34975     }
34976 
34977     //! Mirror image content along specified axes \newinstance.
34978     CImg<T> get_mirror(const char *const axes) const {
34979       return (+*this).mirror(axes);
34980     }
34981 
34982     //! Shift image content.
34983     /**
34984        \param delta_x Amount of displacement along the X-axis.
34985        \param delta_y Amount of displacement along the Y-axis.
34986        \param delta_z Amount of displacement along the Z-axis.
34987        \param delta_c Amount of displacement along the C-axis.
34988        \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
34989     **/
34990     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
34991                    const unsigned int boundary_conditions=0) {
34992       if (is_empty()) return *this;
34993       if (boundary_conditions==3)
34994         return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
34995                         width() - delta_x - 1,
34996                         height() - delta_y - 1,
34997                         depth() - delta_z - 1,
34998                         spectrum() - delta_c - 1,3).move_to(*this);
34999       if (delta_x) // Shift along X-axis
35000         switch (boundary_conditions) {
35001         case 2 : { // Periodic
35002           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
35003           if (!ndelta_x) return *this;
35004           CImg<T> buf(cimg::abs(ndelta_x));
35005           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
35006               std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
35007               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
35008               std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
35009             } else cimg_forYZC(*this,y,z,c) {
35010               std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
35011               std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
35012               std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
35013             }
35014         } break;
35015         case 1 : // Neumann
35016           if (delta_x<0) {
35017             const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
35018             if (!ndelta_x) return *this;
35019             cimg_forYZC(*this,y,z,c) {
35020               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
35021               T *ptrd = data(_width - 1,y,z,c);
35022               const T val = *ptrd;
35023               for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
35024             }
35025           } else {
35026             const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
35027             if (!ndelta_x) return *this;
35028             cimg_forYZC(*this,y,z,c) {
35029               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
35030               T *ptrd = data(0,y,z,c);
35031               const T val = *ptrd;
35032               for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
35033             }
35034           }
35035           break;
35036         default : // Dirichlet
35037           if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
35038           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
35039               std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
35040               std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
35041             } else cimg_forYZC(*this,y,z,c) {
35042               std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
35043               std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
35044             }
35045         }
35046 
35047       if (delta_y) // Shift along Y-axis
35048         switch (boundary_conditions) {
35049         case 2 : { // Periodic
35050           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
35051           if (!ndelta_y) return *this;
35052           CImg<T> buf(width(),cimg::abs(ndelta_y));
35053           if (ndelta_y>0) cimg_forZC(*this,z,c) {
35054               std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
35055               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
35056               std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
35057             } else cimg_forZC(*this,z,c) {
35058               std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
35059               std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
35060               std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
35061             }
35062         } break;
35063         case 1 : // Neumann
35064           if (delta_y<0) {
35065             const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
35066             if (!ndelta_y) return *this;
35067             cimg_forZC(*this,z,c) {
35068               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
35069               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
35070               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
35071             }
35072           } else {
35073             const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
35074             if (!ndelta_y) return *this;
35075             cimg_forZC(*this,z,c) {
35076               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
35077               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
35078               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
35079             }
35080           }
35081           break;
35082         default : // Dirichlet
35083           if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
35084           if (delta_y<0) cimg_forZC(*this,z,c) {
35085               std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
35086               std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
35087             } else cimg_forZC(*this,z,c) {
35088               std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
35089               std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
35090             }
35091         }
35092 
35093       if (delta_z) // Shift along Z-axis
35094         switch (boundary_conditions) {
35095         case 2 : { // Periodic
35096           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
35097           if (!ndelta_z) return *this;
35098           CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
35099           if (ndelta_z>0) cimg_forC(*this,c) {
35100               std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
35101               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35102               std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
35103             } else cimg_forC(*this,c) {
35104               std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
35105               std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
35106               std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
35107             }
35108         } break;
35109         case 1 : // Neumann
35110           if (delta_z<0) {
35111             const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
35112             if (!ndelta_z) return *this;
35113             cimg_forC(*this,c) {
35114               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35115               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
35116               for (int l = 0; l<ndelta_z - 1; ++l) {
35117                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
35118               }
35119             }
35120           } else {
35121             const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
35122             if (!ndelta_z) return *this;
35123             cimg_forC(*this,c) {
35124               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35125               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
35126               for (int l = 0; l<ndelta_z - 1; ++l) {
35127                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
35128               }
35129             }
35130           }
35131           break;
35132         default : // Dirichlet
35133           if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
35134           if (delta_z<0) cimg_forC(*this,c) {
35135               std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
35136               std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
35137             } else cimg_forC(*this,c) {
35138               std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
35139               std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
35140             }
35141         }
35142 
35143       if (delta_c) // Shift along C-axis
35144         switch (boundary_conditions) {
35145         case 2 : { // Periodic
35146           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
35147           if (!ndelta_c) return *this;
35148           CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
35149           if (ndelta_c>0) {
35150             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
35151             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35152             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
35153           } else {
35154             std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
35155             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
35156             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
35157           }
35158         } break;
35159         case 1 : // Neumann
35160           if (delta_c<0) {
35161             const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
35162             if (!ndelta_c) return *this;
35163             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35164             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
35165             for (int l = 0; l<ndelta_c - 1; ++l) {
35166               std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
35167             }
35168           } else {
35169             const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
35170             if (!ndelta_c) return *this;
35171             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35172             T *ptrd = data(0,0,0,1);
35173             for (int l = 0; l<ndelta_c - 1; ++l) {
35174               std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
35175             }
35176           }
35177           break;
35178         default : // Dirichlet
35179           if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
35180           if (delta_c<0) {
35181             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
35182             std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
35183           } else {
35184             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
35185             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
35186           }
35187         }
35188       return *this;
35189     }
35190 
35191     //! Shift image content \newinstance.
35192     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
35193                       const unsigned int boundary_conditions=0) const {
35194       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
35195     }
35196 
35197     //! Permute axes order.
35198     /**
35199        \param axes_order Axes permutations, as a C-string of 4 characters.
35200        This function permutes image content regarding the specified axes permutation.
35201     **/
35202     CImg<T>& permute_axes(const char *const axes_order) {
35203       return get_permute_axes(axes_order).move_to(*this);
35204     }
35205 
35206     //! Permute axes order \newinstance.
35207     CImg<T> get_permute_axes(const char *const axes_order) const {
35208       const T foo = (T)0;
35209       return _permute_axes(axes_order,foo);
35210     }
35211 
35212     template<typename t>
35213     CImg<t> _permute_axes(const char *const axes_order, const t&) const {
35214       if (is_empty() || !axes_order) return CImg<t>(*this,false);
35215       CImg<t> res;
35216       const T* ptrs = _data;
35217       unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
35218       for (unsigned int l = 0; axes_order[l]; ++l) {
35219         int c = cimg::lowercase(axes_order[l]);
35220         if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
35221         else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; }
35222       }
35223       if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
35224         const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
35225         ulongT wh, whd;
35226         switch (code) {
35227         case 0x0123 : // xyzc
35228           return +*this;
35229         case 0x0132 : // xycz
35230           res.assign(_width,_height,_spectrum,_depth);
35231           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35232           cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
35233           break;
35234         case 0x0213 : // xzyc
35235           res.assign(_width,_depth,_height,_spectrum);
35236           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35237           cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
35238           break;
35239         case 0x0231 : // xzcy
35240           res.assign(_width,_depth,_spectrum,_height);
35241           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35242           cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
35243           break;
35244         case 0x0312 : // xcyz
35245           res.assign(_width,_spectrum,_height,_depth);
35246           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35247           cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
35248           break;
35249         case 0x0321 : // xczy
35250           res.assign(_width,_spectrum,_depth,_height);
35251           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35252           cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
35253           break;
35254         case 0x1023 : // yxzc
35255           res.assign(_height,_width,_depth,_spectrum);
35256           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35257           cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
35258           break;
35259         case 0x1032 : // yxcz
35260           res.assign(_height,_width,_spectrum,_depth);
35261           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35262           cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
35263           break;
35264         case 0x1203 : // yzxc
35265           res.assign(_height,_depth,_width,_spectrum);
35266           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35267           cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
35268           break;
35269         case 0x1230 : // yzcx
35270           res.assign(_height,_depth,_spectrum,_width);
35271           switch (_width) {
35272           case 1 : {
35273             t *ptr_r = res.data(0,0,0,0);
35274             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35275               *(ptr_r++) = (t)*(ptrs++);
35276             }
35277           } break;
35278           case 2 : {
35279             t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
35280             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35281               *(ptr_r++) = (t)ptrs[0];
35282               *(ptr_g++) = (t)ptrs[1];
35283               ptrs+=2;
35284             }
35285           } break;
35286           case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
35287             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);
35288             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35289               *(ptr_r++) = (t)ptrs[0];
35290               *(ptr_g++) = (t)ptrs[1];
35291               *(ptr_b++) = (t)ptrs[2];
35292               ptrs+=3;
35293             }
35294           } break;
35295           case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
35296             t
35297               *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
35298               *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
35299             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35300               *(ptr_r++) = (t)ptrs[0];
35301               *(ptr_g++) = (t)ptrs[1];
35302               *(ptr_b++) = (t)ptrs[2];
35303               *(ptr_a++) = (t)ptrs[3];
35304               ptrs+=4;
35305             }
35306           } break;
35307           default : {
35308             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35309             cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
35310             return res;
35311           }
35312           }
35313           break;
35314         case 0x1302 : // ycxz
35315           res.assign(_height,_spectrum,_width,_depth);
35316           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35317           cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
35318           break;
35319         case 0x1320 : // yczx
35320           res.assign(_height,_spectrum,_depth,_width);
35321           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35322           cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
35323           break;
35324         case 0x2013 : // zxyc
35325           res.assign(_depth,_width,_height,_spectrum);
35326           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35327           cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
35328           break;
35329         case 0x2031 : // zxcy
35330           res.assign(_depth,_width,_spectrum,_height);
35331           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35332           cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
35333           break;
35334         case 0x2103 : // zyxc
35335           res.assign(_depth,_height,_width,_spectrum);
35336           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35337           cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
35338           break;
35339         case 0x2130 : // zycx
35340           res.assign(_depth,_height,_spectrum,_width);
35341           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35342           cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
35343           break;
35344         case 0x2301 : // zcxy
35345           res.assign(_depth,_spectrum,_width,_height);
35346           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35347           cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
35348           break;
35349         case 0x2310 : // zcyx
35350           res.assign(_depth,_spectrum,_height,_width);
35351           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35352           cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
35353           break;
35354         case 0x3012 : // cxyz
35355           res.assign(_spectrum,_width,_height,_depth);
35356           switch (_spectrum) {
35357           case 1 : {
35358             const T *ptr_r = data(0,0,0,0);
35359             t *ptrd = res._data;
35360             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
35361           } break;
35362           case 2 : {
35363             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
35364             t *ptrd = res._data;
35365             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35366               ptrd[0] = (t)*(ptr_r++);
35367               ptrd[1] = (t)*(ptr_g++);
35368               ptrd+=2;
35369             }
35370           } break;
35371           case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
35372             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35373             t *ptrd = res._data;
35374             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35375               ptrd[0] = (t)*(ptr_r++);
35376               ptrd[1] = (t)*(ptr_g++);
35377               ptrd[2] = (t)*(ptr_b++);
35378               ptrd+=3;
35379             }
35380           } break;
35381           case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
35382             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);
35383             t *ptrd = res._data;
35384             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35385               ptrd[0] = (t)*(ptr_r++);
35386               ptrd[1] = (t)*(ptr_g++);
35387               ptrd[2] = (t)*(ptr_b++);
35388               ptrd[3] = (t)*(ptr_a++);
35389               ptrd+=4;
35390             }
35391           } break;
35392           default : {
35393             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35394             cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
35395           }
35396           }
35397           break;
35398         case 0x3021 : // cxzy
35399           res.assign(_spectrum,_width,_depth,_height);
35400           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35401           cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
35402           break;
35403         case 0x3102 : // cyxz
35404           res.assign(_spectrum,_height,_width,_depth);
35405           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35406           cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
35407           break;
35408         case 0x3120 : // cyzx
35409           res.assign(_spectrum,_height,_depth,_width);
35410           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35411           cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
35412           break;
35413         case 0x3201 : // czxy
35414           res.assign(_spectrum,_depth,_width,_height);
35415           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35416           cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
35417           break;
35418         case 0x3210 : // czyx
35419           res.assign(_spectrum,_depth,_height,_width);
35420           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35421           cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
35422           break;
35423         }
35424       }
35425       if (!res)
35426         throw CImgArgumentException(_cimg_instance
35427                                     "permute_axes(): Invalid specified axes order '%s'.",
35428                                     cimg_instance,
35429                                     axes_order);
35430       return res;
35431     }
35432 
35433     //! Unroll pixel values along specified axis.
35434     /**
35435        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
35436     **/
35437     CImg<T>& unroll(const char axis) {
35438       const unsigned int siz = (unsigned int)size();
35439       if (siz) switch (cimg::lowercase(axis)) {
35440       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
35441       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
35442       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
35443       case 'c' : _spectrum = siz; _width = _height = _depth = 1; break;
35444       }
35445       return *this;
35446     }
35447 
35448     //! Unroll pixel values along specified axis \newinstance.
35449     CImg<T> get_unroll(const char axis) const {
35450       return (+*this).unroll(axis);
35451     }
35452 
35453     //! Rotate image with arbitrary angle.
35454     /**
35455        \param angle Rotation angle, in degrees.
35456        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35457        \param boundary_conditions Boundary conditions.
35458               Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35459        \note The size of the image is modified.
35460     **/
35461     CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
35462                     const unsigned int boundary_conditions=0) {
35463       const float nangle = cimg::mod(angle,360.f);
35464       if (nangle==0.f) return *this;
35465       return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
35466     }
35467 
35468     //! Rotate image with arbitrary angle \newinstance.
35469     CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
35470                        const unsigned int boundary_conditions=0) const {
35471       if (is_empty()) return *this;
35472       CImg<T> res;
35473       const float nangle = cimg::mod(angle,360.f);
35474       if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles
35475         const int wm1 = width() - 1, hm1 = height() - 1;
35476         const int iangle = (int)nangle/90;
35477         switch (iangle) {
35478         case 1 : { // 90 deg
35479           res.assign(_height,_width,_depth,_spectrum);
35480           T *ptrd = res._data;
35481           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
35482         } break;
35483         case 2 : { // 180 deg
35484           res.assign(_width,_height,_depth,_spectrum);
35485           T *ptrd = res._data;
35486           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
35487         } break;
35488         case 3 : { // 270 deg
35489           res.assign(_height,_width,_depth,_spectrum);
35490           T *ptrd = res._data;
35491           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
35492         } break;
35493         default : // 0 deg
35494           return *this;
35495         }
35496       } else { // Generic angle
35497         const float
35498           rad = (float)(nangle*cimg::PI/180.),
35499           ca = (float)std::cos(rad), sa = (float)std::sin(rad),
35500           ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
35501           vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
35502           w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
35503         res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
35504         const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
35505         _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
35506       }
35507       return res;
35508     }
35509 
35510     //! Rotate image with arbitrary angle, around a center point.
35511     /**
35512        \param angle Rotation angle, in degrees.
35513        \param cx X-coordinate of the rotation center.
35514        \param cy Y-coordinate of the rotation center.
35515        \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
35516        \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35517     **/
35518     CImg<T>& rotate(const float angle, const float cx, const float cy,
35519                     const unsigned int interpolation, const unsigned int boundary_conditions=0) {
35520       return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
35521     }
35522 
35523     //! Rotate image with arbitrary angle, around a center point \newinstance.
35524     CImg<T> get_rotate(const float angle, const float cx, const float cy,
35525                        const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
35526       if (is_empty()) return *this;
35527       CImg<T> res(_width,_height,_depth,_spectrum);
35528       _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
35529       return res;
35530     }
35531 
35532     // [internal] Perform 2D rotation with arbitrary angle.
35533     void _rotate(CImg<T>& res, const float angle,
35534                  const unsigned int interpolation, const unsigned int boundary_conditions,
35535                  const float w2, const float h2,
35536                  const float rw2, const float rh2) const {
35537       const float
35538         rad = (float)(angle*cimg::PI/180.),
35539         ca = (float)std::cos(rad), sa = (float)std::sin(rad);
35540 
35541       switch (boundary_conditions) {
35542       case 3 : { // Mirror
35543 
35544         switch (interpolation) {
35545         case 2 : { // Cubic interpolation
35546           const float ww = 2.f*width(), hh = 2.f*height();
35547           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35548             cimg_forXYZC(res,x,y,z,c) {
35549             const float xc = x - rw2, yc = y - rh2,
35550               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35551               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35552             res(x,y,z,c) = _cubic_atXY_c(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35553           }
35554         } break;
35555         case 1 : { // Linear interpolation
35556           const float ww = 2.f*width(), hh = 2.f*height();
35557           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35558             cimg_forXYZC(res,x,y,z,c) {
35559             const float xc = x - rw2, yc = y - rh2,
35560               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35561               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35562             res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35563           }
35564         } break;
35565         default : { // Nearest-neighbor interpolation
35566           const int ww = 2*width(), hh = 2*height();
35567           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35568             cimg_forXYZC(res,x,y,z,c) {
35569             const float xc = x - rw2, yc = y - rh2,
35570               mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
35571               my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
35572             res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35573           }
35574         }
35575         }
35576       } break;
35577 
35578       case 2 : // Periodic
35579         switch (interpolation) {
35580         case 2 : { // Cubic interpolation
35581           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35582             cimg_forXYZC(res,x,y,z,c) {
35583             const float xc = x - rw2, yc = y - rh2;
35584             res(x,y,z,c) = _cubic_atXY_pc(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35585           }
35586         } break;
35587         case 1 : { // Linear interpolation
35588           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35589             cimg_forXYZC(res,x,y,z,c) {
35590             const float xc = x - rw2, yc = y - rh2;
35591             res(x,y,z,c) = (T)_linear_atXY_p(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35592           }
35593         } break;
35594         default : { // Nearest-neighbor interpolation
35595           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35596             cimg_forXYZC(res,x,y,z,c) {
35597             const float xc = x - rw2, yc = y - rh2;
35598             res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
35599                                    cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
35600           }
35601         }
35602         } break;
35603 
35604       case 1 : // Neumann
35605         switch (interpolation) {
35606         case 2 : { // Cubic interpolation
35607           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35608           cimg_forXYZC(res,x,y,z,c) {
35609             const float xc = x - rw2, yc = y - rh2;
35610             res(x,y,z,c) = _cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35611           }
35612         } break;
35613         case 1 : { // Linear interpolation
35614           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35615           cimg_forXYZC(res,x,y,z,c) {
35616             const float xc = x - rw2, yc = y - rh2;
35617             res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35618           }
35619         } break;
35620         default : { // Nearest-neighbor interpolation
35621           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35622           cimg_forXYZC(res,x,y,z,c) {
35623             const float xc = x - rw2, yc = y - rh2;
35624             res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
35625                                  (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
35626           }
35627         }
35628         } break;
35629 
35630       default : // Dirichlet
35631         switch (interpolation) {
35632         case 2 : { // Cubic interpolation
35633           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35634           cimg_forXYZC(res,x,y,z,c) {
35635             const float xc = x - rw2, yc = y - rh2;
35636             res(x,y,z,c) = cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
35637           }
35638         } break;
35639         case 1 : { // Linear interpolation
35640           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35641           cimg_forXYZC(res,x,y,z,c) {
35642             const float xc = x - rw2, yc = y - rh2;
35643             res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
35644           }
35645         } break;
35646         default : { // Nearest-neighbor interpolation
35647           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35648           cimg_forXYZC(res,x,y,z,c) {
35649             const float xc = x - rw2, yc = y - rh2;
35650             res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
35651                                 (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
35652           }
35653         }
35654         }
35655       }
35656     }
35657 
35658     //! Rotate volumetric image with arbitrary angle and axis.
35659     /**
35660        \param u X-coordinate of the 3D rotation axis.
35661        \param v Y-coordinate of the 3D rotation axis.
35662        \param w Z-coordinate of the 3D rotation axis.
35663        \param angle Rotation angle, in degrees.
35664        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35665        \param boundary_conditions Boundary conditions.
35666               Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35667        \note Most of the time, size of the image is modified.
35668     **/
35669     CImg<T> rotate(const float u, const float v, const float w, const float angle,
35670                    const unsigned int interpolation, const unsigned int boundary_conditions) {
35671       const float nangle = cimg::mod(angle,360.f);
35672       if (nangle==0.f) return *this;
35673       return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
35674     }
35675 
35676     //! Rotate volumetric image with arbitrary angle and axis \newinstance.
35677     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
35678                        const unsigned int interpolation, const unsigned int boundary_conditions) const {
35679       if (is_empty()) return *this;
35680       CImg<T> res;
35681       const float
35682         w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
35683         w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
35684       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
35685       const CImg<Tfloat>
35686         X = R*CImg<Tfloat>(8,3,1,1,
35687                            0.f,w1,w1,0.f,0.f,w1,w1,0.f,
35688                            0.f,0.f,h1,h1,0.f,0.f,h1,h1,
35689                            0.f,0.f,0.f,0.f,d1,d1,d1,d1);
35690       float
35691         xm, xM = X.get_shared_row(0).max_min(xm),
35692         ym, yM = X.get_shared_row(1).max_min(ym),
35693         zm, zM = X.get_shared_row(2).max_min(zm);
35694       const int
35695         dx = (int)cimg::round(xM - xm),
35696         dy = (int)cimg::round(yM - ym),
35697         dz = (int)cimg::round(zM - zm);
35698       R.transpose();
35699       res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
35700       const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
35701       _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
35702       return res;
35703     }
35704 
35705     //! Rotate volumetric image with arbitrary angle and axis, around a center point.
35706     /**
35707        \param u X-coordinate of the 3D rotation axis.
35708        \param v Y-coordinate of the 3D rotation axis.
35709        \param w Z-coordinate of the 3D rotation axis.
35710        \param angle Rotation angle, in degrees.
35711        \param cx X-coordinate of the rotation center.
35712        \param cy Y-coordinate of the rotation center.
35713        \param cz Z-coordinate of the rotation center.
35714        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
35715        \param boundary_conditions Boundary conditions. Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic }</tt>.
35716        \note Most of the time, size of the image is modified.
35717     **/
35718     CImg<T> rotate(const float u, const float v, const float w, const float angle,
35719                    const float cx, const float cy, const float cz,
35720                    const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
35721       const float nangle = cimg::mod(angle,360.f);
35722       if (nangle==0.f) return *this;
35723       return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
35724     }
35725 
35726     //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
35727     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
35728                        const float cx, const float cy, const float cz,
35729                        const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
35730       if (is_empty()) return *this;
35731       CImg<T> res(_width,_height,_depth,_spectrum);
35732       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
35733       _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
35734       return res;
35735     }
35736 
35737     // [internal] Perform 3D rotation with arbitrary axis and angle.
35738     void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
35739                  const unsigned int interpolation, const unsigned int boundary_conditions,
35740                  const float w2, const float h2, const float d2,
35741                  const float rw2, const float rh2, const float rd2) const {
35742       switch (boundary_conditions) {
35743       case 3 : // Mirror
35744         switch (interpolation) {
35745         case 2 : { // Cubic interpolation
35746           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
35747           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35748           cimg_forXYZ(res,x,y,z) {
35749             const float
35750               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35751               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35752               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35753               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35754             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X<width()?X:ww - X - 1,
35755                                                            Y<height()?Y:hh - Y - 1,
35756                                                            Z<depth()?Z:dd - Z - z,c);
35757           }
35758         } break;
35759         case 1 : { // Linear interpolation
35760           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
35761           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35762           cimg_forXYZ(res,x,y,z) {
35763             const float
35764               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35765               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35766               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35767               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35768             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
35769                                                              Y<height()?Y:hh - Y - 1,
35770                                                              Z<depth()?Z:dd - Z - 1,c);
35771           }
35772         } break;
35773         default : { // Nearest-neighbor interpolation
35774           const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
35775           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35776           cimg_forXYZ(res,x,y,z) {
35777             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35778             const int
35779               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35780               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35781               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35782             cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
35783                                                     Y<height()?Y:hh - Y - 1,
35784                                                     Z<depth()?Z:dd - Z -  1,c);
35785           }
35786         }
35787         } break;
35788 
35789       case 2 : // Periodic
35790         switch (interpolation) {
35791         case 2 : { // Cubic interpolation
35792           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35793           cimg_forXYZ(res,x,y,z) {
35794             const float
35795               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35796               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35797               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35798               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35799             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_pc(X,Y,Z,c);
35800           }
35801         } break;
35802         case 1 : { // Linear interpolation
35803           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35804           cimg_forXYZ(res,x,y,z) {
35805             const float
35806               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35807               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35808               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35809               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35810             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ_p(X,Y,Z,c);
35811           }
35812         } break;
35813         default : { // Nearest-neighbor interpolation
35814           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35815           cimg_forXYZ(res,x,y,z) {
35816             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35817             const int
35818               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
35819               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
35820               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
35821             cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
35822           }
35823         }
35824         } break;
35825 
35826       case 1 : // Neumann
35827         switch (interpolation) {
35828         case 2 : { // Cubic interpolation
35829           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35830           cimg_forXYZ(res,x,y,z) {
35831             const float
35832               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35833               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35834               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35835               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35836             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X,Y,Z,c);
35837           }
35838         } break;
35839         case 1 : { // Linear interpolation
35840           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35841           cimg_forXYZ(res,x,y,z) {
35842             const float
35843               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35844               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35845               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35846               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35847             cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
35848           }
35849         } break;
35850         default : { // Nearest-neighbor interpolation
35851           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35852           cimg_forXYZ(res,x,y,z) {
35853             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35854             const int
35855               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
35856               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
35857               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
35858             cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
35859           }
35860         }
35861         } break;
35862 
35863       default : // Dirichlet
35864         switch (interpolation) {
35865         case 2 : { // Cubic interpolation
35866           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35867           cimg_forXYZ(res,x,y,z) {
35868             const float
35869               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35870               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35871               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35872               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35873             cimg_forC(res,c) res(x,y,z,c) = cubic_atXYZ_c(X,Y,Z,c,(T)0);
35874           }
35875         } break;
35876         case 1 : { // Linear interpolation
35877           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35878           cimg_forXYZ(res,x,y,z) {
35879             const float
35880               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35881               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35882               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35883               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35884             cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
35885           }
35886         } break;
35887         default : { // Nearest-neighbor interpolation
35888           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35889           cimg_forXYZ(res,x,y,z) {
35890             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35891             const int
35892               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
35893               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
35894               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
35895             cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
35896           }
35897         }
35898         } break;
35899       }
35900     }
35901 
35902     //! Warp image content by a warping field.
35903     /**
35904        \param warp Warping field.
35905        \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
35906        \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35907        \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35908     **/
35909     template<typename t>
35910     CImg<T>& warp(const CImg<t>& p_warp, const unsigned int mode=0,
35911                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
35912       return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this);
35913     }
35914 
35915     //! Warp image content by a warping field \newinstance
35916     template<typename t>
35917     CImg<T> get_warp(const CImg<t>& p_warp, const unsigned int mode=0,
35918                      const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
35919       if (is_empty() || !p_warp) return *this;
35920       if (mode && !is_sameXYZ(p_warp))
35921         throw CImgArgumentException(_cimg_instance
35922                                     "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
35923                                     "have different XYZ dimensions.",
35924                                     cimg_instance,
35925                                     p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data);
35926 
35927       CImg<T> res(p_warp._width,p_warp._height,p_warp._depth,_spectrum);
35928 
35929       if (p_warp._spectrum==1) { // 1D warping
35930         if (mode>=3) { // Forward-relative warp
35931           res.fill((T)0);
35932           if (interpolation>=1) // Linear interpolation
35933             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35934             cimg_forYZC(res,y,z,c) {
35935               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35936               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
35937             }
35938           else // Nearest-neighbor interpolation
35939             cimg_forYZC(res,y,z,c) {
35940               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35941               cimg_forX(res,x) {
35942                 const int X = x + (int)cimg::round(*(ptrs0++));
35943                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
35944               }
35945             }
35946         } else if (mode==2) { // Forward-absolute warp
35947           res.fill((T)0);
35948           if (interpolation>=1) // Linear interpolation
35949             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35950             cimg_forYZC(res,y,z,c) {
35951               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35952               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
35953             }
35954           else // Nearest-neighbor interpolation
35955             cimg_forYZC(res,y,z,c) {
35956               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35957               cimg_forX(res,x) {
35958                 const int X = (int)cimg::round(*(ptrs0++));
35959                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
35960               }
35961             }
35962         } else if (mode==1) { // Backward-relative warp
35963           if (interpolation==2) // Cubic interpolation
35964             switch (boundary_conditions) {
35965             case 3 : { // Mirror
35966               const float w2 = 2.f*width();
35967               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35968               cimg_forYZC(res,y,z,c) {
35969                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35970                 cimg_forX(res,x) {
35971                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
35972                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,y,z,c);
35973                 }
35974               }
35975             } break;
35976             case 2 : // Periodic
35977               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35978               cimg_forYZC(res,y,z,c) {
35979                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35980                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc(x - (float)*(ptrs0++),y,z,c);
35981               }
35982               break;
35983             case 1 : // Neumann
35984               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35985               cimg_forYZC(res,y,z,c) {
35986                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35987                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c(x - (float)*(ptrs0++),y,z,c);
35988               }
35989               break;
35990             default : // Dirichlet
35991               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35992               cimg_forYZC(res,y,z,c) {
35993                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35994                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c(x - (float)*(ptrs0++),y,z,c,(T)0);
35995               }
35996             }
35997           else if (interpolation==1) // Linear interpolation
35998             switch (boundary_conditions) {
35999             case 3 : { // Mirror
36000               const float w2 = 2.f*width();
36001               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36002               cimg_forYZC(res,y,z,c) {
36003                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36004                 cimg_forX(res,x) {
36005                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
36006                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
36007                 }
36008               }
36009             } break;
36010             case 2 : // Periodic
36011               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36012               cimg_forYZC(res,y,z,c) {
36013                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36014                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p(x - (float)*(ptrs0++),y,z,c);
36015               }
36016               break;
36017             case 1 : // Neumann
36018               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36019               cimg_forYZC(res,y,z,c) {
36020                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36021                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
36022               }
36023               break;
36024             default : // Dirichlet
36025               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36026               cimg_forYZC(res,y,z,c) {
36027                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36028                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
36029               }
36030             }
36031           else // Nearest-neighbor interpolation
36032             switch (boundary_conditions) {
36033             case 3 : { // Mirror
36034               const int w2 = 2*width();
36035               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36036               cimg_forYZC(res,y,z,c) {
36037                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36038                 cimg_forX(res,x) {
36039                   const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
36040                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
36041                 }
36042               }
36043             } break;
36044             case 2 : // Periodic
36045               cimg_forYZC(res,y,z,c) {
36046                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36047                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),y,z,c);
36048               }
36049               break;
36050             case 1 : // Neumann
36051               cimg_forYZC(res,y,z,c) {
36052                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36053                 cimg_forX(res,x) *(ptrd++) = _atX(x - (int)cimg::round(*(ptrs0++)),y,z,c);
36054               }
36055               break;
36056             default : // Dirichlet
36057               cimg_forYZC(res,y,z,c) {
36058                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36059                 cimg_forX(res,x) *(ptrd++) = atX(x - (int)cimg::round(*(ptrs0++)),y,z,c,(T)0);
36060               }
36061             }
36062         }
36063         else { // Backward-absolute warp
36064           if (interpolation==2) // Cubic interpolation
36065             switch (boundary_conditions) {
36066             case 3 : { // Mirror
36067               const float w2 = 2.f*width();
36068               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36069                 cimg_forYZC(res,y,z,c) {
36070                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36071                 cimg_forX(res,x) {
36072                   const float mx = cimg::mod((float)*(ptrs0++),w2);
36073                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,0,0,c);
36074                 }
36075               }
36076             } break;
36077             case 2 : // Periodic
36078               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36079               cimg_forYZC(res,y,z,c) {
36080                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36081                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc((float)*(ptrs0++),0,0,c);
36082               }
36083               break;
36084             case 1 : // Neumann
36085               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36086               cimg_forYZC(res,y,z,c) {
36087                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36088                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c((float)*(ptrs0++),0,0,c);
36089               }
36090               break;
36091             default : // Dirichlet
36092               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36093               cimg_forYZC(res,y,z,c) {
36094                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36095                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c((float)*(ptrs0++),0,0,c,(T)0);
36096               }
36097             }
36098           else if (interpolation==1) // Linear interpolation
36099             switch (boundary_conditions) {
36100             case 3 : { // Mirror
36101               const float w2 = 2.f*width();
36102               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36103                 cimg_forYZC(res,y,z,c) {
36104                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36105                 cimg_forX(res,x) {
36106                   const float mx = cimg::mod((float)*(ptrs0++),w2);
36107                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
36108                 }
36109               }
36110             } break;
36111             case 2 : // Periodic
36112               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36113               cimg_forYZC(res,y,z,c) {
36114                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36115                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p((float)*(ptrs0++),0,0,c);
36116               }
36117               break;
36118             case 1 : // Neumann
36119               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36120               cimg_forYZC(res,y,z,c) {
36121                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36122                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
36123               }
36124               break;
36125             default : // Dirichlet
36126               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36127               cimg_forYZC(res,y,z,c) {
36128                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36129                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
36130               }
36131             }
36132           else // Nearest-neighbor interpolation
36133             switch (boundary_conditions) {
36134             case 3 : { // Mirror
36135               const int w2 = 2*width();
36136               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36137                 cimg_forYZC(res,y,z,c) {
36138                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36139                 cimg_forX(res,x) {
36140                   const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
36141                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
36142                 }
36143               }
36144             } break;
36145             case 2 : // Periodic
36146               cimg_forYZC(res,y,z,c) {
36147                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36148                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),0,0,c);
36149               }
36150               break;
36151             case 1 : // Neumann
36152               cimg_forYZC(res,y,z,c) {
36153                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36154                 cimg_forX(res,x) *(ptrd++) = _atX((int)cimg::round(*(ptrs0++)),0,0,c);
36155               }
36156               break;
36157             default : // Dirichlet
36158               cimg_forYZC(res,y,z,c) {
36159                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36160                 cimg_forX(res,x) *(ptrd++) = atX((int)cimg::round(*(ptrs0++)),0,0,c,(T)0);
36161               }
36162             }
36163         }
36164 
36165       } else if (p_warp._spectrum==2) { // 2D warping
36166         if (mode>=3) { // Forward-relative warp
36167           res.fill((T)0);
36168           if (interpolation>=1) // Linear interpolation
36169             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36170             cimg_forYZC(res,y,z,c) {
36171               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
36172               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
36173             }
36174           else // Nearest-neighbor interpolation
36175             cimg_forYZC(res,y,z,c) {
36176               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
36177               cimg_forX(res,x) {
36178                 const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
36179                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
36180               }
36181             }
36182         } else if (mode==2) { // Forward-absolute warp
36183           res.fill((T)0);
36184           if (interpolation>=1) // Linear interpolation
36185             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36186             cimg_forYZC(res,y,z,c) {
36187               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
36188               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
36189             }
36190           else // Nearest-neighbor interpolation
36191             cimg_forYZC(res,y,z,c) {
36192               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
36193               cimg_forX(res,x) {
36194                 const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
36195                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
36196               }
36197             }
36198         } else if (mode==1) { // Backward-relative warp
36199           if (interpolation==2) // Cubic interpolation
36200             switch (boundary_conditions) {
36201             case 3 : { // Mirror
36202               const float w2 = 2.f*width(), h2 = 2.f*height();
36203               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36204               cimg_forYZC(res,y,z,c) {
36205                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36206                 cimg_forX(res,x) {
36207                   const float
36208                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36209                     my = cimg::mod(y - (float)*(ptrs1++),h2);
36210                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36211                 }
36212               }
36213             } break;
36214             case 2 : // Periodic
36215               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36216               cimg_forYZC(res,y,z,c) {
36217                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36218                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36219               }
36220               break;
36221             case 1 : // Neumann
36222               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36223               cimg_forYZC(res,y,z,c) {
36224                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36225                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36226               }
36227               break;
36228             default : // Dirichlet
36229               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36230               cimg_forYZC(res,y,z,c) {
36231                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36232                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
36233               }
36234             }
36235           else if (interpolation==1) // Linear interpolation
36236             switch (boundary_conditions) {
36237             case 3 : { // Mirror
36238               const float w2 = 2.f*width(), h2 = 2.f*height();
36239               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36240               cimg_forYZC(res,y,z,c) {
36241                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36242                 cimg_forX(res,x) {
36243                   const float
36244                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36245                     my = cimg::mod(y - (float)*(ptrs1++),h2);
36246                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36247                 }
36248               }
36249             } break;
36250             case 2 : // Periodic
36251               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36252               cimg_forYZC(res,y,z,c) {
36253                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36254                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36255               }
36256               break;
36257             case 1 : // Neumann
36258               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36259               cimg_forYZC(res,y,z,c) {
36260                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36261                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36262               }
36263               break;
36264             default : // Dirichlet
36265               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36266               cimg_forYZC(res,y,z,c) {
36267                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36268                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
36269               }
36270             }
36271           else // Nearest-neighbor interpolation
36272             switch (boundary_conditions) {
36273             case 3 : { // Mirror
36274               const int w2 = 2*width(), h2 = 2*height();
36275               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36276               cimg_forYZC(res,y,z,c) {
36277                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36278                 cimg_forX(res,x) {
36279                   const int
36280                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
36281                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
36282                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36283                 }
36284               }
36285             } break;
36286             case 2 : // Periodic
36287               cimg_forYZC(res,y,z,c) {
36288                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36289                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
36290                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),z,c);
36291               }
36292               break;
36293             case 1 : // Neumann
36294               cimg_forYZC(res,y,z,c) {
36295                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36296                 cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)cimg::round(*(ptrs0++)),
36297                                                    y - (int)cimg::round(*(ptrs1++)),z,c);
36298               }
36299               break;
36300             default : // Dirichlet
36301               cimg_forYZC(res,y,z,c) {
36302                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36303                 cimg_forX(res,x) *(ptrd++) = atXY(x - (int)cimg::round(*(ptrs0++)),
36304                                                   y - (int)cimg::round(*(ptrs1++)),z,c,(T)0);
36305               }
36306             }
36307         } else { // Backward-absolute warp
36308           if (interpolation==2) // Cubic interpolation
36309             switch (boundary_conditions) {
36310             case 3 : { // Mirror
36311               const float w2 = 2.f*width(), h2 = 2.f*height();
36312               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36313               cimg_forYZC(res,y,z,c) {
36314                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36315                 cimg_forX(res,x) {
36316                   const float
36317                     mx = cimg::mod((float)*(ptrs0++),w2),
36318                     my = cimg::mod((float)*(ptrs1++),h2);
36319                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36320                 }
36321               }
36322             } break;
36323             case 2 : // Periodic
36324               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36325               cimg_forYZC(res,y,z,c) {
36326                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36327                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36328               }
36329               break;
36330             case 1 : // Neumann
36331               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36332               cimg_forYZC(res,y,z,c) {
36333                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36334                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36335               }
36336               break;
36337             default : // Dirichlet
36338               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36339               cimg_forYZC(res,y,z,c) {
36340                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36341                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
36342               }
36343             }
36344           else if (interpolation==1) // Linear interpolation
36345             switch (boundary_conditions) {
36346             case 3 : { // Mirror
36347               const float w2 = 2.f*width(), h2 = 2.f*height();
36348               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36349               cimg_forYZC(res,y,z,c) {
36350                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36351                 cimg_forX(res,x) {
36352                   const float
36353                     mx = cimg::mod((float)*(ptrs0++),w2),
36354                     my = cimg::mod((float)*(ptrs1++),h2);
36355                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36356                 }
36357               }
36358             } break;
36359             case 2 : // Periodic
36360               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36361               cimg_forYZC(res,y,z,c) {
36362                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36363                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36364               }
36365               break;
36366             case 1 : // Neumann
36367               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36368               cimg_forYZC(res,y,z,c) {
36369                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36370                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36371               }
36372               break;
36373             default : // Dirichlet
36374               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36375               cimg_forYZC(res,y,z,c) {
36376                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36377                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
36378               }
36379             }
36380           else // Nearest-neighbor interpolation
36381             switch (boundary_conditions) {
36382             case 3 : { // Mirror
36383               const int w2 = 2*width(), h2 = 2*height();
36384               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36385               cimg_forYZC(res,y,z,c) {
36386                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36387                 cimg_forX(res,x) {
36388                   const int
36389                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
36390                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
36391                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36392                 }
36393               }
36394             } break;
36395             case 2 : // Periodic
36396               cimg_forYZC(res,y,z,c) {
36397                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36398                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
36399                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),0,c);
36400               }
36401               break;
36402             case 1 : // Neumann
36403               cimg_forYZC(res,y,z,c) {
36404                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36405                 cimg_forX(res,x) *(ptrd++) = _atXY((int)cimg::round(*(ptrs0++)),
36406                                                    (int)cimg::round(*(ptrs1++)),0,c);
36407               }
36408               break;
36409             default : // Dirichlet
36410               cimg_forYZC(res,y,z,c) {
36411                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
36412                 cimg_forX(res,x) *(ptrd++) = atXY((int)cimg::round(*(ptrs0++)),
36413                                                   (int)cimg::round(*(ptrs1++)),0,c,(T)0);
36414               }
36415             }
36416         }
36417 
36418       } else { // 3D warping
36419         if (mode>=3) { // Forward-relative warp
36420           res.fill((T)0);
36421           if (interpolation>=1) // Linear interpolation
36422             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36423             cimg_forYZC(res,y,z,c) {
36424               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36425               const T *ptrs = data(0,y,z,c);
36426               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
36427                                                     z + (float)*(ptrs2++),c);
36428             }
36429           else // Nearest-neighbor interpolation
36430             cimg_forYZC(res,y,z,c) {
36431               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36432               const T *ptrs = data(0,y,z,c);
36433               cimg_forX(res,x) {
36434                 const int
36435                   X = x + (int)cimg::round(*(ptrs0++)),
36436                   Y = y + (int)cimg::round(*(ptrs1++)),
36437                   Z = z + (int)cimg::round(*(ptrs2++));
36438                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36439               }
36440             }
36441         } else if (mode==2) { // Forward-absolute warp
36442           res.fill((T)0);
36443           if (interpolation>=1) // Linear interpolation
36444             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36445             cimg_forYZC(res,y,z,c) {
36446               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36447               const T *ptrs = data(0,y,z,c);
36448               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36449             }
36450           else // Nearest-neighbor interpolation
36451             cimg_forYZC(res,y,z,c) {
36452               const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36453               const T *ptrs = data(0,y,z,c);
36454               cimg_forX(res,x) {
36455                 const int
36456                   X = (int)cimg::round(*(ptrs0++)),
36457                   Y = (int)cimg::round(*(ptrs1++)),
36458                   Z = (int)cimg::round(*(ptrs2++));
36459                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36460               }
36461             }
36462         } else if (mode==1) { // Backward-relative warp
36463           if (interpolation==2) // Cubic interpolation
36464             switch (boundary_conditions) {
36465             case 3 : { // Mirror
36466               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36467               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36468               cimg_forYZC(res,y,z,c) {
36469                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36470                 T *ptrd = res.data(0,y,z,c);
36471                 cimg_forX(res,x) {
36472                   const float
36473                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36474                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36475                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36476                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36477                                              my<height()?my:h2 - my - 1,
36478                                              mz<depth()?mz:d2 - mz - 1,c);
36479                 }
36480               }
36481             } break;
36482             case 2 : // Periodic
36483               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36484               cimg_forYZC(res,y,z,c) {
36485                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36486                 T *ptrd = res.data(0,y,z,c);
36487                 cimg_forX(res,x)
36488                   *(ptrd++) = _cubic_atXYZ_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36489               }
36490               break;
36491             case 1 : // Neumann
36492               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36493               cimg_forYZC(res,y,z,c) {
36494                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36495                 T *ptrd = res.data(0,y,z,c);
36496                 cimg_forX(res,x)
36497                   *(ptrd++) = _cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36498               }
36499               break;
36500             default : // Dirichlet
36501               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36502               cimg_forYZC(res,y,z,c) {
36503                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36504                 T *ptrd = res.data(0,y,z,c);
36505                 cimg_forX(res,x)
36506                   *(ptrd++) = cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36507               }
36508             }
36509           else if (interpolation==1) // Linear interpolation
36510             switch (boundary_conditions) {
36511             case 3 : { // Mirror
36512               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36513               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36514               cimg_forYZC(res,y,z,c) {
36515                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36516                 T *ptrd = res.data(0,y,z,c);
36517                 cimg_forX(res,x) {
36518                   const float
36519                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36520                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36521                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36522                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
36523                                                my<height()?my:h2 - my - 1,
36524                                                mz<depth()?mz:d2 - mz - 1,c);
36525                 }
36526               }
36527             } break;
36528             case 2 : // Periodic
36529               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36530               cimg_forYZC(res,y,z,c) {
36531                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36532                 T *ptrd = res.data(0,y,z,c);
36533                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),
36534                                                                 z - (float)*(ptrs2++),c);
36535               }
36536               break;
36537             case 1 : // Neumann
36538               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36539               cimg_forYZC(res,y,z,c) {
36540                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36541                 T *ptrd = res.data(0,y,z,c);
36542                 cimg_forX(res,x)
36543                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36544               }
36545               break;
36546             default : // Dirichlet
36547               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36548               cimg_forYZC(res,y,z,c) {
36549                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36550                 T *ptrd = res.data(0,y,z,c);
36551                 cimg_forX(res,x)
36552                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36553               }
36554             }
36555           else // Nearest neighbor interpolation
36556             switch (boundary_conditions) {
36557             case 3 : { // Mirror
36558               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
36559               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36560               cimg_forYZC(res,y,z,c) {
36561                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36562                 T *ptrd = res.data(0,y,z,c);
36563                 cimg_forX(res,x) {
36564                   const int
36565                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
36566                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
36567                     mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
36568                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
36569                                       my<height()?my:h2 - my - 1,
36570                                       mz<depth()?mz:d2 - mz - 1,c);
36571                 }
36572               }
36573             } break;
36574             case 2 : // Periodic
36575               cimg_forYZC(res,y,z,c) {
36576                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36577                 T *ptrd = res.data(0,y,z,c);
36578                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
36579                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),
36580                                                      cimg::mod(z - (int)cimg::round(*(ptrs2++)),depth()),c);
36581               }
36582               break;
36583             case 1 : // Neumann
36584               cimg_forYZC(res,y,z,c) {
36585                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36586                 T *ptrd = res.data(0,y,z,c);
36587                 cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)cimg::round(*(ptrs0++)),
36588                                                     y - (int)cimg::round(*(ptrs1++)),
36589                                                     z - (int)cimg::round(*(ptrs2++)),c);
36590               }
36591               break;
36592             default : // Dirichlet
36593               cimg_forYZC(res,y,z,c) {
36594                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36595                 T *ptrd = res.data(0,y,z,c);
36596                 cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)cimg::round(*(ptrs0++)),
36597                                                    y - (int)cimg::round(*(ptrs1++)),
36598                                                    z - (int)cimg::round(*(ptrs2++)),c,(T)0);
36599               }
36600             }
36601         } else { // Backward-absolute warp
36602           if (interpolation==2) // Cubic interpolation
36603             switch (boundary_conditions) {
36604             case 3 : { // Mirror
36605               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36606               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36607               cimg_forYZC(res,y,z,c) {
36608                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36609                 T *ptrd = res.data(0,y,z,c);
36610                 cimg_forX(res,x) {
36611                   const float
36612                     mx = cimg::mod((float)*(ptrs0++),w2),
36613                     my = cimg::mod((float)*(ptrs1++),h2),
36614                     mz = cimg::mod((float)*(ptrs2++),d2);
36615                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36616                                              my<height()?my:h2 - my - 1,
36617                                              mz<depth()?mz:d2 - mz - 1,c);
36618                 }
36619               }
36620             } break;
36621             case 2 : // Periodic
36622               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36623               cimg_forYZC(res,y,z,c) {
36624                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36625                 T *ptrd = res.data(0,y,z,c);
36626                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_pc((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36627               }
36628               break;
36629             case 1 : // Neumann
36630               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36631               cimg_forYZC(res,y,z,c) {
36632                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36633                 T *ptrd = res.data(0,y,z,c);
36634                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36635               }
36636               break;
36637             default : // Dirichlet
36638               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36639               cimg_forYZC(res,y,z,c) {
36640                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36641                 T *ptrd = res.data(0,y,z,c);
36642                 cimg_forX(res,x) *(ptrd++) = cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
36643                                                            c,(T)0);
36644               }
36645             }
36646           else if (interpolation==1) // Linear interpolation
36647             switch (boundary_conditions) {
36648             case 3 : { // Mirror
36649               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36650               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36651               cimg_forYZC(res,y,z,c) {
36652                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36653                 T *ptrd = res.data(0,y,z,c);
36654                 cimg_forX(res,x) {
36655                   const float
36656                     mx = cimg::mod((float)*(ptrs0++),w2),
36657                     my = cimg::mod((float)*(ptrs1++),h2),
36658                     mz = cimg::mod((float)*(ptrs2++),d2);
36659                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
36660                                                my<height()?my:h2 - my - 1,
36661                                                mz<depth()?mz:d2 - mz - 1,c);
36662                 }
36663               }
36664             } break;
36665             case 2 :// Periodic
36666               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36667               cimg_forYZC(res,y,z,c) {
36668                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36669                 T *ptrd = res.data(0,y,z,c);
36670                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p((float)*(ptrs0++),(float)*(ptrs1++),
36671                                                                 (float)*(ptrs2++),c);
36672               }
36673               break;
36674             case 1 : // Neumann
36675               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36676               cimg_forYZC(res,y,z,c) {
36677                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36678                 T *ptrd = res.data(0,y,z,c);
36679                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36680               }
36681               break;
36682             default : // Dirichlet
36683               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36684               cimg_forYZC(res,y,z,c) {
36685                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36686                 T *ptrd = res.data(0,y,z,c);
36687                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
36688                                                              c,(T)0);
36689               }
36690             }
36691           else // Nearest-neighbor interpolation
36692             switch (boundary_conditions) {
36693             case 3 : { // Mirror
36694               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
36695               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36696               cimg_forYZC(res,y,z,c) {
36697                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36698                 T *ptrd = res.data(0,y,z,c);
36699                 cimg_forX(res,x) {
36700                   const int
36701                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
36702                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
36703                     mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
36704                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
36705                                       my<height()?my:h2 - my - 1,
36706                                       mz<depth()?mz:d2 - mz - 1,c);
36707                 }
36708               }
36709             } break;
36710             case 2 : // Periodic
36711               cimg_forYZC(res,y,z,c) {
36712                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36713                 T *ptrd = res.data(0,y,z,c);
36714                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
36715                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),
36716                                                      cimg::mod((int)cimg::round(*(ptrs2++)),depth()),c);
36717               }
36718               break;
36719             case 1 : // Neumann
36720               cimg_forYZC(res,y,z,c) {
36721                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36722                 T *ptrd = res.data(0,y,z,c);
36723                 cimg_forX(res,x) *(ptrd++) = _atXYZ((int)cimg::round(*(ptrs0++)),
36724                                                     (int)cimg::round(*(ptrs1++)),
36725                                                     (int)cimg::round(*(ptrs2++)),c);
36726               }
36727               break;
36728             default : // Dirichlet
36729               cimg_forYZC(res,y,z,c) {
36730                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36731                 T *ptrd = res.data(0,y,z,c);
36732                 cimg_forX(res,x) *(ptrd++) = atXYZ((int)cimg::round(*(ptrs0++)),
36733                                                    (int)cimg::round(*(ptrs1++)),
36734                                                    (int)cimg::round(*(ptrs2++)),c,(T)0);
36735               }
36736             }
36737         }
36738       }
36739       return res;
36740     }
36741 
36742     //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views.
36743     /**
36744        \param x0 X-coordinate of the projection point.
36745        \param y0 Y-coordinate of the projection point.
36746        \param z0 Z-coordinate of the projection point.
36747     **/
36748     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
36749       if (is_empty() || _depth<2) return +*this;
36750       const unsigned int
36751         _x0 = (x0>=_width)?_width - 1:x0,
36752         _y0 = (y0>=_height)?_height - 1:y0,
36753         _z0 = (z0>=_depth)?_depth - 1:z0;
36754       const CImg<T>
36755         img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
36756         img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
36757         resize(_depth,_height,1,-100,-1),
36758         img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
36759       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
36760         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
36761         draw_image(0,img_xy._height,img_xz);
36762     }
36763 
36764     //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace.
36765     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
36766       if (_depth<2) return *this;
36767       return get_projections2d(x0,y0,z0).move_to(*this);
36768     }
36769 
36770     //! Crop image region.
36771     /**
36772        \param x0 = X-coordinate of the upper-left crop rectangle corner.
36773        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
36774        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
36775        \param c0 = C-coordinate of the upper-left crop rectangle corner.
36776        \param x1 = X-coordinate of the lower-right crop rectangle corner.
36777        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
36778        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
36779        \param c1 = C-coordinate of the lower-right crop rectangle corner.
36780        \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
36781     **/
36782     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
36783                   const int x1, const int y1, const int z1, const int c1,
36784                   const unsigned int boundary_conditions=0) {
36785       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
36786     }
36787 
36788     //! Crop image region \newinstance.
36789     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
36790                      const int x1, const int y1, const int z1, const int c1,
36791                      const unsigned int boundary_conditions=0) const {
36792       if (is_empty())
36793         throw CImgInstanceException(_cimg_instance
36794                                     "crop(): Empty instance.",
36795                                     cimg_instance);
36796       const int
36797         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
36798         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
36799         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
36800         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
36801       const unsigned int
36802         _boundary_conditions = nx0>=0 && nx1<width() &&
36803                                ny0>=0 && ny1<height() &&
36804                                nz0>=0 && nz1<depth() &&
36805                                nc0>=0 && nc1<spectrum()?0:boundary_conditions;
36806       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
36807       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
36808         switch (_boundary_conditions) {
36809         case 3 : { // Mirror
36810           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
36811           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36812                                                                      _height*_depth*_spectrum>=4))
36813           cimg_forXYZC(res,x,y,z,c) {
36814             const int
36815               mx = cimg::mod(nx0 + x,w2),
36816               my = cimg::mod(ny0 + y,h2),
36817               mz = cimg::mod(nz0 + z,d2),
36818               mc = cimg::mod(nc0 + c,s2);
36819             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
36820                                    my<height()?my:h2 - my - 1,
36821                                    mz<depth()?mz:d2 - mz - 1,
36822                                    mc<spectrum()?mc:s2 - mc - 1);
36823           }
36824         } break;
36825         case 2 : { // Periodic
36826           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36827                                                                      _height*_depth*_spectrum>=4))
36828           cimg_forXYZC(res,x,y,z,c) {
36829             res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
36830                                    cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
36831           }
36832         } break;
36833         case 1 : // Neumann
36834           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36835                                                                      _height*_depth*_spectrum>=4))
36836           cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
36837           break;
36838         default : // Dirichlet
36839           res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
36840         }
36841       else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
36842       return res;
36843     }
36844 
36845     //! Crop image region \overloading.
36846     CImg<T>& crop(const int x0, const int y0, const int z0,
36847                   const int x1, const int y1, const int z1,
36848                   const unsigned int boundary_conditions=0) {
36849       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
36850     }
36851 
36852     //! Crop image region \newinstance.
36853     CImg<T> get_crop(const int x0, const int y0, const int z0,
36854                      const int x1, const int y1, const int z1,
36855                      const unsigned int boundary_conditions=0) const {
36856       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
36857     }
36858 
36859     //! Crop image region \overloading.
36860     CImg<T>& crop(const int x0, const int y0,
36861                   const int x1, const int y1,
36862                   const unsigned int boundary_conditions=0) {
36863       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
36864     }
36865 
36866     //! Crop image region \newinstance.
36867     CImg<T> get_crop(const int x0, const int y0,
36868                      const int x1, const int y1,
36869                      const unsigned int boundary_conditions=0) const {
36870       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
36871     }
36872 
36873     //! Crop image region \overloading.
36874     CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
36875       return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
36876     }
36877 
36878     //! Crop image region \newinstance.
36879     CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
36880       return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
36881     }
36882 
36883     //! Autocrop image region, regarding the specified background value.
36884     CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
36885       if (is_empty()) return *this;
36886       for (const char *s = axes; *s; ++s) {
36887         const char axis = cimg::lowercase(*s);
36888         const CImg<intT> coords = _autocrop(value,axis);
36889         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
36890         else switch (axis) {
36891         case 'x' : {
36892           const int x0 = coords[0], x1 = coords[1];
36893           if (x0>=0 && x1>=0) crop(x0,x1);
36894         } break;
36895         case 'y' : {
36896           const int y0 = coords[0], y1 = coords[1];
36897           if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
36898         } break;
36899         case 'z' : {
36900           const int z0 = coords[0], z1 = coords[1];
36901           if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
36902         } break;
36903         default : {
36904           const int c0 = coords[0], c1 = coords[1];
36905           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
36906         }
36907         }
36908       }
36909       return *this;
36910     }
36911 
36912     //! Autocrop image region, regarding the specified background value \newinstance.
36913     CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
36914       return (+*this).autocrop(value,axes);
36915     }
36916 
36917     //! Autocrop image region, regarding the specified background color.
36918     /**
36919        \param color Color used for the crop. If \c 0, color is guessed.
36920        \param axes Axes used for the crop.
36921     **/
36922     CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
36923       if (is_empty()) return *this;
36924       if (!color) { // Guess color
36925         const CImg<T> col1 = get_vector_at(0,0,0);
36926         const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
36927         autocrop(col1,axes);
36928         if (_width==w && _height==h && _depth==d && _spectrum==s) {
36929           const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
36930           autocrop(col2,axes);
36931         }
36932         return *this;
36933       }
36934       for (const char *s = axes; *s; ++s) {
36935         const char axis = cimg::lowercase(*s);
36936         switch (axis) {
36937         case 'x' : {
36938           int x0 = width(), x1 = -1;
36939           cimg_forC(*this,c) {
36940             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
36941             const int nx0 = coords[0], nx1 = coords[1];
36942             if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
36943           }
36944           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
36945         } break;
36946         case 'y' : {
36947           int y0 = height(), y1 = -1;
36948           cimg_forC(*this,c) {
36949             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
36950             const int ny0 = coords[0], ny1 = coords[1];
36951             if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
36952           }
36953           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
36954         } break;
36955         default : {
36956           int z0 = depth(), z1 = -1;
36957           cimg_forC(*this,c) {
36958             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
36959             const int nz0 = coords[0], nz1 = coords[1];
36960             if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
36961           }
36962           if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
36963         }
36964         }
36965       }
36966       return *this;
36967     }
36968 
36969     //! Autocrop image region, regarding the specified background color \newinstance.
36970     CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
36971       return (+*this).autocrop(color,axes);
36972     }
36973 
36974     CImg<intT> _autocrop(const T& value, const char axis) const {
36975       CImg<intT> res;
36976       switch (cimg::lowercase(axis)) {
36977       case 'x' : {
36978         int x0 = -1, x1 = -1;
36979         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
36980           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
36981         if (x0>=0) {
36982           for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
36983             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
36984         }
36985         res = CImg<intT>::vector(x0,x1);
36986       } break;
36987       case 'y' : {
36988         int y0 = -1, y1 = -1;
36989         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
36990           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
36991         if (y0>=0) {
36992           for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
36993             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
36994         }
36995         res = CImg<intT>::vector(y0,y1);
36996       } break;
36997       case 'z' : {
36998         int z0 = -1, z1 = -1;
36999         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
37000           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
37001         if (z0>=0) {
37002           for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
37003             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
37004         }
37005         res = CImg<intT>::vector(z0,z1);
37006       } break;
37007       default : {
37008         int c0 = -1, c1 = -1;
37009         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
37010           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
37011         if (c0>=0) {
37012           for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
37013             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
37014         }
37015         res = CImg<intT>::vector(c0,c1);
37016       }
37017       }
37018       return res;
37019     }
37020 
37021     //! Return specified image column.
37022     /**
37023        \param x0 Image column.
37024     **/
37025     CImg<T> get_column(const int x0) const {
37026       return get_columns(x0,x0);
37027     }
37028 
37029     //! Return specified image column \inplace.
37030     CImg<T>& column(const int x0) {
37031       return columns(x0,x0);
37032     }
37033 
37034     //! Return specified range of image columns.
37035     /**
37036        \param x0 Starting image column.
37037        \param x1 Ending image column.
37038     **/
37039     CImg<T>& columns(const int x0, const int x1) {
37040       return get_columns(x0,x1).move_to(*this);
37041     }
37042 
37043     //! Return specified range of image columns \inplace.
37044     CImg<T> get_columns(const int x0, const int x1) const {
37045       return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
37046     }
37047 
37048     //! Return specified image row.
37049     CImg<T> get_row(const int y0) const {
37050       return get_rows(y0,y0);
37051     }
37052 
37053     //! Return specified image row \inplace.
37054     /**
37055        \param y0 Image row.
37056     **/
37057     CImg<T>& row(const int y0) {
37058       return rows(y0,y0);
37059     }
37060 
37061     //! Return specified range of image rows.
37062     /**
37063        \param y0 Starting image row.
37064        \param y1 Ending image row.
37065     **/
37066     CImg<T> get_rows(const int y0, const int y1) const {
37067       return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
37068     }
37069 
37070     //! Return specified range of image rows \inplace.
37071     CImg<T>& rows(const int y0, const int y1) {
37072       return get_rows(y0,y1).move_to(*this);
37073     }
37074 
37075     //! Return specified image slice.
37076     /**
37077        \param z0 Image slice.
37078     **/
37079     CImg<T> get_slice(const int z0) const {
37080       return get_slices(z0,z0);
37081     }
37082 
37083     //! Return specified image slice \inplace.
37084     CImg<T>& slice(const int z0) {
37085       return slices(z0,z0);
37086     }
37087 
37088     //! Return specified range of image slices.
37089     /**
37090        \param z0 Starting image slice.
37091        \param z1 Ending image slice.
37092     **/
37093     CImg<T> get_slices(const int z0, const int z1) const {
37094       return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
37095     }
37096 
37097     //! Return specified range of image slices \inplace.
37098     CImg<T>& slices(const int z0, const int z1) {
37099       return get_slices(z0,z1).move_to(*this);
37100     }
37101 
37102     //! Return specified image channel.
37103     /**
37104        \param c0 Image channel.
37105     **/
37106     CImg<T> get_channel(const int c0) const {
37107       return get_channels(c0,c0);
37108     }
37109 
37110     //! Return specified image channel \inplace.
37111     CImg<T>& channel(const int c0) {
37112       return channels(c0,c0);
37113     }
37114 
37115     //! Return specified range of image channels.
37116     /**
37117        \param c0 Starting image channel.
37118        \param c1 Ending image channel.
37119     **/
37120     CImg<T> get_channels(const int c0, const int c1) const {
37121       return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
37122     }
37123 
37124     //! Return specified range of image channels \inplace.
37125     CImg<T>& channels(const int c0, const int c1) {
37126       return get_channels(c0,c1).move_to(*this);
37127     }
37128 
37129     //! Return stream line of a 2D or 3D vector field.
37130     CImg<floatT> get_streamline(const float x, const float y, const float z,
37131                                 const float L=256, const float dl=0.1f,
37132                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
37133                                 const bool is_oriented_only=false) const {
37134       if (_spectrum!=2 && _spectrum!=3)
37135         throw CImgInstanceException(_cimg_instance
37136                                     "streamline(): Instance is not a 2D or 3D vector field.",
37137                                     cimg_instance);
37138       if (_spectrum==2) {
37139         if (is_oriented_only) {
37140           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
37141           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
37142                             0,0,0,_width - 1.f,_height - 1.f,0.f);
37143         } else {
37144           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
37145           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
37146                             0,0,0,_width - 1.f,_height - 1.f,0.f);
37147         }
37148       }
37149       if (is_oriented_only) {
37150         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
37151         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
37152                           0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
37153       }
37154       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
37155       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
37156                         0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
37157     }
37158 
37159     //! Return stream line of a 3D vector field.
37160     /**
37161        \param func Vector field function.
37162        \param x X-coordinate of the starting point of the streamline.
37163        \param y Y-coordinate of the starting point of the streamline.
37164        \param z Z-coordinate of the starting point of the streamline.
37165        \param L Streamline length.
37166        \param dl Streamline length increment.
37167        \param interpolation_type Type of interpolation.
37168          Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
37169        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
37170        \param is_oriented_only Tells if the direction of the vectors must be ignored.
37171        \param x0 X-coordinate of the first bounding-box vertex.
37172        \param y0 Y-coordinate of the first bounding-box vertex.
37173        \param z0 Z-coordinate of the first bounding-box vertex.
37174        \param x1 X-coordinate of the second bounding-box vertex.
37175        \param y1 Y-coordinate of the second bounding-box vertex.
37176        \param z1 Z-coordinate of the second bounding-box vertex.
37177     **/
37178     template<typename tfunc>
37179     static CImg<floatT> streamline(const tfunc& func,
37180                                    const float x, const float y, const float z,
37181                                    const float L=256, const float dl=0.1f,
37182                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
37183                                    const bool is_oriented_only=false,
37184                                    const float x0=0, const float y0=0, const float z0=0,
37185                                    const float x1=0, const float y1=0, const float z1=0) {
37186       if (dl<=0)
37187         throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
37188                                     "(should be >0).",
37189                                     pixel_type(),
37190                                     dl);
37191 
37192       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
37193       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
37194       const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
37195       CImg<floatT> coordinates(size_L,3);
37196       const float dl2 = dl/2;
37197       float
37198         *ptr_x = coordinates.data(0,0),
37199         *ptr_y = coordinates.data(0,1),
37200         *ptr_z = coordinates.data(0,2),
37201         pu = (float)(dl*func(x,y,z,0)),
37202         pv = (float)(dl*func(x,y,z,1)),
37203         pw = (float)(dl*func(x,y,z,2)),
37204         X = x, Y = y, Z = z;
37205 
37206       switch (interpolation_type) {
37207       case 0 : { // Nearest integer interpolation
37208         cimg_forX(coordinates,l) {
37209           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37210           const int
37211             xi = (int)(X>0?X + 0.5f:X - 0.5f),
37212             yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
37213             zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
37214           float
37215             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
37216             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
37217             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
37218           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37219           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37220           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37221         }
37222       } break;
37223       case 1 : { // First-order interpolation
37224         cimg_forX(coordinates,l) {
37225           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37226           float
37227             u = (float)(dl*func(X,Y,Z,0)),
37228             v = (float)(dl*func(X,Y,Z,1)),
37229             w = (float)(dl*func(X,Y,Z,2));
37230           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37231           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37232           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37233         }
37234       } break;
37235       case 2 : { // Second order interpolation
37236         cimg_forX(coordinates,l) {
37237           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37238           float
37239             u0 = (float)(dl2*func(X,Y,Z,0)),
37240             v0 = (float)(dl2*func(X,Y,Z,1)),
37241             w0 = (float)(dl2*func(X,Y,Z,2));
37242           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
37243           float
37244             u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
37245             v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
37246             w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
37247           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37248           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37249           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37250         }
37251       } break;
37252       default : { // Fourth order interpolation
37253         cimg_forX(coordinates,k) {
37254           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37255           float
37256             u0 = (float)(dl2*func(X,Y,Z,0)),
37257             v0 = (float)(dl2*func(X,Y,Z,1)),
37258             w0 = (float)(dl2*func(X,Y,Z,2));
37259           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
37260           float
37261             u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
37262             v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
37263             w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
37264           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
37265           float
37266             u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
37267             v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
37268             w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
37269           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
37270           float
37271             u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
37272             v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
37273             w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
37274           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
37275           const float
37276             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
37277             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
37278             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
37279           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37280           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37281         }
37282       }
37283       }
37284       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
37285       return coordinates;
37286     }
37287 
37288     //! Return stream line of a 3D vector field \overloading.
37289     static CImg<floatT> streamline(const char *const expression,
37290                                    const float x, const float y, const float z,
37291                                    const float L=256, const float dl=0.1f,
37292                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
37293                                    const bool is_oriented_only=false,
37294                                    const float x0=0, const float y0=0, const float z0=0,
37295                                    const float x1=0, const float y1=0, const float z1=0) {
37296       _functor4d_streamline_expr func(expression);
37297       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
37298     }
37299 
37300     struct _functor4d_streamline2d_directed {
37301       const CImg<T>& ref;
37302       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
37303       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37304         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
37305       }
37306     };
37307 
37308     struct _functor4d_streamline3d_directed {
37309       const CImg<T>& ref;
37310       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
37311       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37312         return (float)ref._linear_atXYZ(x,y,z,c);
37313       }
37314     };
37315 
37316     struct _functor4d_streamline2d_oriented {
37317       const CImg<T>& ref;
37318       CImg<floatT> *pI;
37319       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
37320       ~_functor4d_streamline2d_oriented() { delete pI; }
37321       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37322 #define _cimg_vecalign2d(i,j) \
37323         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); }
37324         int
37325           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
37326           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
37327           zi = (int)z;
37328         const float
37329           dx = x - xi,
37330           dy = y - yi;
37331         if (c==0) {
37332           CImg<floatT>& I = *pI;
37333           if (xi<0) xi = 0;
37334           if (nxi<0) nxi = 0;
37335           if (xi>=ref.width()) xi = ref.width() - 1;
37336           if (nxi>=ref.width()) nxi = ref.width() - 1;
37337           if (yi<0) yi = 0;
37338           if (nyi<0) nyi = 0;
37339           if (yi>=ref.height()) yi = ref.height() - 1;
37340           if (nyi>=ref.height()) nyi = ref.height() - 1;
37341           I(0,0,0) = (float)ref(xi,yi,zi,0);   I(0,0,1) = (float)ref(xi,yi,zi,1);
37342           I(1,0,0) = (float)ref(nxi,yi,zi,0);  I(1,0,1) = (float)ref(nxi,yi,zi,1);
37343           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
37344           I(0,1,0) = (float)ref(xi,nyi,zi,0);  I(0,1,1) = (float)ref(xi,nyi,zi,1);
37345           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
37346         }
37347         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
37348       }
37349     };
37350 
37351     struct _functor4d_streamline3d_oriented {
37352       const CImg<T>& ref;
37353       CImg<floatT> *pI;
37354       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
37355       ~_functor4d_streamline3d_oriented() { delete pI; }
37356       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37357 #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) { \
37358   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); }
37359         int
37360           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
37361           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
37362           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
37363         const float
37364           dx = x - xi,
37365           dy = y - yi,
37366           dz = z - zi;
37367         if (c==0) {
37368           CImg<floatT>& I = *pI;
37369           if (xi<0) xi = 0;
37370           if (nxi<0) nxi = 0;
37371           if (xi>=ref.width()) xi = ref.width() - 1;
37372           if (nxi>=ref.width()) nxi = ref.width() - 1;
37373           if (yi<0) yi = 0;
37374           if (nyi<0) nyi = 0;
37375           if (yi>=ref.height()) yi = ref.height() - 1;
37376           if (nyi>=ref.height()) nyi = ref.height() - 1;
37377           if (zi<0) zi = 0;
37378           if (nzi<0) nzi = 0;
37379           if (zi>=ref.depth()) zi = ref.depth() - 1;
37380           if (nzi>=ref.depth()) nzi = ref.depth() - 1;
37381           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
37382           I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
37383           I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
37384           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
37385           I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
37386           I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
37387           I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
37388           I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
37389           I(1,0,1,1) = (float)ref(nxi,yi,nzi,1);  I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
37390           I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
37391           I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
37392           I(0,1,1,1) = (float)ref(xi,nyi,nzi,1);  I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
37393           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
37394           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
37395         }
37396         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
37397       }
37398     };
37399 
37400     struct _functor4d_streamline_expr {
37401       _cimg_math_parser *mp;
37402       ~_functor4d_streamline_expr() { mp->end(); delete mp; }
37403       _functor4d_streamline_expr(const char *const expr):mp(0) {
37404         mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
37405       }
37406       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37407         return (float)(*mp)(x,y,z,c);
37408       }
37409     };
37410 
37411     //! Return a shared-memory image referencing a range of pixels of the image instance.
37412     /**
37413        \param x0 X-coordinate of the starting pixel.
37414        \param x1 X-coordinate of the ending pixel.
37415        \param y0 Y-coordinate.
37416        \param z0 Z-coordinate.
37417        \param c0 C-coordinate.
37418      **/
37419     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37420                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
37421       const ulongT
37422         beg = (ulongT)offset(x0,y0,z0,c0),
37423         end = (ulongT)offset(x1,y0,z0,c0);
37424       if (beg>end || beg>=size() || end>=size())
37425         throw CImgArgumentException(_cimg_instance
37426                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37427                                     cimg_instance,
37428                                     x0,x1,y0,z0,c0);
37429       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37430     }
37431 
37432     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
37433     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37434                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
37435       const ulongT
37436         beg = (ulongT)offset(x0,y0,z0,c0),
37437         end = (ulongT)offset(x1,y0,z0,c0);
37438       if (beg>end || beg>=size() || end>=size())
37439         throw CImgArgumentException(_cimg_instance
37440                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37441                                     cimg_instance,
37442                                     x0,x1,y0,z0,c0);
37443       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37444     }
37445 
37446     //! Return a shared-memory image referencing a range of rows of the image instance.
37447     /**
37448        \param y0 Y-coordinate of the starting row.
37449        \param y1 Y-coordinate of the ending row.
37450        \param z0 Z-coordinate.
37451        \param c0 C-coordinate.
37452     **/
37453     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37454                              const unsigned int z0=0, const unsigned int c0=0) {
37455       const ulongT
37456         beg = (ulongT)offset(0,y0,z0,c0),
37457         end = (ulongT)offset(0,y1,z0,c0);
37458       if (beg>end || beg>=size() || end>=size())
37459         throw CImgArgumentException(_cimg_instance
37460                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37461                                     "(0->%u,%u->%u,%u,%u).",
37462                                     cimg_instance,
37463                                     _width - 1,y0,y1,z0,c0);
37464       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37465     }
37466 
37467     //! Return a shared-memory image referencing a range of rows of the image instance \const.
37468     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37469                                    const unsigned int z0=0, const unsigned int c0=0) const {
37470       const ulongT
37471         beg = (ulongT)offset(0,y0,z0,c0),
37472         end = (ulongT)offset(0,y1,z0,c0);
37473       if (beg>end || beg>=size() || end>=size())
37474         throw CImgArgumentException(_cimg_instance
37475                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37476                                     "(0->%u,%u->%u,%u,%u).",
37477                                     cimg_instance,
37478                                     _width - 1,y0,y1,z0,c0);
37479       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37480     }
37481 
37482     //! Return a shared-memory image referencing one row of the image instance.
37483     /**
37484        \param y0 Y-coordinate.
37485        \param z0 Z-coordinate.
37486        \param c0 C-coordinate.
37487     **/
37488     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
37489       return get_shared_rows(y0,y0,z0,c0);
37490     }
37491 
37492     //! Return a shared-memory image referencing one row of the image instance \const.
37493     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
37494       return get_shared_rows(y0,y0,z0,c0);
37495     }
37496 
37497     //! Return a shared memory image referencing a range of slices of the image instance.
37498     /**
37499        \param z0 Z-coordinate of the starting slice.
37500        \param z1 Z-coordinate of the ending slice.
37501        \param c0 C-coordinate.
37502     **/
37503     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
37504       const ulongT
37505         beg = (ulongT)offset(0,0,z0,c0),
37506         end = (ulongT)offset(0,0,z1,c0);
37507       if (beg>end || beg>=size() || end>=size())
37508         throw CImgArgumentException(_cimg_instance
37509                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37510                                     "(0->%u,0->%u,%u->%u,%u).",
37511                                     cimg_instance,
37512                                     _width - 1,_height - 1,z0,z1,c0);
37513       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37514     }
37515 
37516     //! Return a shared memory image referencing a range of slices of the image instance \const.
37517     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
37518       const ulongT
37519         beg = (ulongT)offset(0,0,z0,c0),
37520         end = (ulongT)offset(0,0,z1,c0);
37521       if (beg>end || beg>=size() || end>=size())
37522         throw CImgArgumentException(_cimg_instance
37523                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37524                                     "(0->%u,0->%u,%u->%u,%u).",
37525                                     cimg_instance,
37526                                     _width - 1,_height - 1,z0,z1,c0);
37527       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37528     }
37529 
37530     //! Return a shared-memory image referencing one slice of the image instance.
37531     /**
37532        \param z0 Z-coordinate.
37533        \param c0 C-coordinate.
37534     **/
37535     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
37536       return get_shared_slices(z0,z0,c0);
37537     }
37538 
37539     //! Return a shared-memory image referencing one slice of the image instance \const.
37540     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
37541       return get_shared_slices(z0,z0,c0);
37542     }
37543 
37544     //! Return a shared-memory image referencing a range of channels of the image instance.
37545     /**
37546        \param c0 C-coordinate of the starting channel.
37547        \param c1 C-coordinate of the ending channel.
37548     **/
37549     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
37550       const ulongT
37551         beg = (ulongT)offset(0,0,0,c0),
37552         end = (ulongT)offset(0,0,0,c1);
37553       if (beg>end || beg>=size() || end>=size())
37554         throw CImgArgumentException(_cimg_instance
37555                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37556                                     "(0->%u,0->%u,0->%u,%u->%u).",
37557                                     cimg_instance,
37558                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37559       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37560     }
37561 
37562     //! Return a shared-memory image referencing a range of channels of the image instance \const.
37563     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
37564       const ulongT
37565         beg = (ulongT)offset(0,0,0,c0),
37566         end = (ulongT)offset(0,0,0,c1);
37567       if (beg>end || beg>=size() || end>=size())
37568         throw CImgArgumentException(_cimg_instance
37569                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37570                                     "(0->%u,0->%u,0->%u,%u->%u).",
37571                                     cimg_instance,
37572                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37573       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37574     }
37575 
37576     //! Return a shared-memory image referencing one channel of the image instance.
37577     /**
37578        \param c0 C-coordinate.
37579     **/
37580     CImg<T> get_shared_channel(const unsigned int c0) {
37581       return get_shared_channels(c0,c0);
37582     }
37583 
37584     //! Return a shared-memory image referencing one channel of the image instance \const.
37585     const CImg<T> get_shared_channel(const unsigned int c0) const {
37586       return get_shared_channels(c0,c0);
37587     }
37588 
37589     //! Return a shared-memory version of the image instance.
37590     CImg<T> get_shared() {
37591       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37592     }
37593 
37594     //! Return a shared-memory version of the image instance \const.
37595     const CImg<T> get_shared() const {
37596       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37597     }
37598 
37599     //! Split image into a list along specified axis.
37600     /**
37601        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37602        \param nb Number of split parts.
37603        \note
37604        - If \c nb==0, instance image is split into blocs of egal values along the specified axis.
37605        - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide.
37606        - If \c nb>0, instance image is split into \c nb blocs.
37607     **/
37608     CImgList<T> get_split(const char axis, const int nb=-1) const {
37609       CImgList<T> res;
37610       if (is_empty()) return res;
37611       const char _axis = cimg::lowercase(axis);
37612 
37613       if (nb<0) { // Split by bloc size
37614         const unsigned int dp = (unsigned int)(nb?-nb:1);
37615         switch (_axis) {
37616         case 'x': {
37617           if (_width>dp) {
37618             res.assign(_width/dp + (_width%dp?1:0),1,1);
37619             const unsigned int pe = _width - dp;
37620             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37621                                                            _height*_depth*_spectrum>=128))
37622             for (int p = 0; p<(int)pe; p+=dp)
37623               get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
37624             get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37625           } else res.assign(*this);
37626         } break;
37627         case 'y': {
37628           if (_height>dp) {
37629             res.assign(_height/dp + (_height%dp?1:0),1,1);
37630             const unsigned int pe = _height - dp;
37631             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37632                                                            _width*_depth*_spectrum>=128))
37633             for (int p = 0; p<(int)pe; p+=dp)
37634               get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
37635             get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37636           } else res.assign(*this);
37637         } break;
37638         case 'z': {
37639           if (_depth>dp) {
37640             res.assign(_depth/dp + (_depth%dp?1:0),1,1);
37641             const unsigned int pe = _depth - dp;
37642             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37643                                                            _width*_height*_spectrum>=128))
37644             for (int p = 0; p<(int)pe; p+=dp)
37645               get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
37646             get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37647           } else res.assign(*this);
37648         } break;
37649         case 'c' : {
37650           if (_spectrum>dp) {
37651             res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
37652             const unsigned int pe = _spectrum - dp;
37653             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37654                                                            _width*_height*_depth>=128))
37655             for (int p = 0; p<(int)pe; p+=dp)
37656               get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
37657             get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37658           } else res.assign(*this);
37659         }
37660         }
37661       } else if (nb>0) { // Split by number of (non-homogeneous) blocs
37662         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
37663         if ((unsigned int)nb>siz)
37664           throw CImgArgumentException(_cimg_instance
37665                                       "get_split(): Instance cannot be split along %c-axis into %u blocs.",
37666                                       cimg_instance,
37667                                       axis,nb);
37668         if (nb==1) res.assign(*this);
37669         else {
37670           int err = (int)siz;
37671           unsigned int _p = 0;
37672           switch (_axis) {
37673           case 'x' : {
37674             cimg_forX(*this,p) if ((err-=nb)<=0) {
37675               get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
37676               err+=(int)siz;
37677               _p = p + 1U;
37678             }
37679           } break;
37680           case 'y' : {
37681             cimg_forY(*this,p) if ((err-=nb)<=0) {
37682               get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
37683               err+=(int)siz;
37684               _p = p + 1U;
37685             }
37686           } break;
37687           case 'z' : {
37688             cimg_forZ(*this,p) if ((err-=nb)<=0) {
37689               get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
37690               err+=(int)siz;
37691               _p = p + 1U;
37692             }
37693           } break;
37694           case 'c' : {
37695             cimg_forC(*this,p) if ((err-=nb)<=0) {
37696               get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
37697               err+=(int)siz;
37698               _p = p + 1U;
37699             }
37700           }
37701           }
37702         }
37703       } else { // Split by egal values according to specified axis
37704         T current = *_data;
37705         switch (_axis) {
37706         case 'x' : {
37707           int i0 = 0;
37708           cimg_forX(*this,i)
37709             if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
37710           get_columns(i0,width() - 1).move_to(res);
37711         } break;
37712         case 'y' : {
37713           int i0 = 0;
37714           cimg_forY(*this,i)
37715             if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
37716           get_rows(i0,height() - 1).move_to(res);
37717         } break;
37718         case 'z' : {
37719           int i0 = 0;
37720           cimg_forZ(*this,i)
37721             if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
37722           get_slices(i0,depth() - 1).move_to(res);
37723         } break;
37724         case 'c' : {
37725           int i0 = 0;
37726           cimg_forC(*this,i)
37727             if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
37728           get_channels(i0,spectrum() - 1).move_to(res);
37729         } break;
37730         default : {
37731           longT i0 = 0;
37732           cimg_foroff(*this,i)
37733             if ((*this)[i]!=current) {
37734               CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
37735               i0 = (longT)i; current = (*this)[i];
37736             }
37737           CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
37738         }
37739         }
37740       }
37741       return res;
37742     }
37743 
37744     //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
37745     /**
37746        \param values Splitting value sequence.
37747        \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
37748        \param keep_values Tells if the splitting sequence must be kept in the split blocs.
37749      **/
37750     template<typename t>
37751     CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
37752       typedef _cimg_Tt Tt;
37753 
37754       CImgList<T> res;
37755       if (is_empty()) return res;
37756       const ulongT vsiz = values.size();
37757       const char _axis = cimg::lowercase(axis);
37758       if (!vsiz) return CImgList<T>(*this);
37759       if (vsiz==1) { // Split according to a single value
37760         const T value = (T)*values;
37761         switch (_axis) {
37762         case 'x' : {
37763           unsigned int i0 = 0, i = 0;
37764           do {
37765             while (i<_width && (*this)(i)==value) ++i;
37766             if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
37767             while (i<_width && (*this)(i)!=value) ++i;
37768             if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
37769           } while (i<_width);
37770         } break;
37771         case 'y' : {
37772           unsigned int i0 = 0, i = 0;
37773           do {
37774             while (i<_height && (*this)(0,i)==value) ++i;
37775             if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
37776             while (i<_height && (*this)(0,i)!=value) ++i;
37777             if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
37778           } while (i<_height);
37779         } break;
37780         case 'z' : {
37781           unsigned int i0 = 0, i = 0;
37782           do {
37783             while (i<_depth && (*this)(0,0,i)==value) ++i;
37784             if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
37785             while (i<_depth && (*this)(0,0,i)!=value) ++i;
37786             if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
37787           } while (i<_depth);
37788         } break;
37789         case 'c' : {
37790           unsigned int i0 = 0, i = 0;
37791           do {
37792             while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
37793             if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
37794             while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
37795             if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
37796           } while (i<_spectrum);
37797         } break;
37798         default : {
37799           const ulongT siz = size();
37800           ulongT i0 = 0, i = 0;
37801           do {
37802             while (i<siz && (*this)[i]==value) ++i;
37803             if (i>i0) {
37804               if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
37805               i0 = i;
37806             }
37807             while (i<siz && (*this)[i]!=value) ++i;
37808             if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
37809           } while (i<siz);
37810         }
37811         }
37812       } else { // Split according to multiple values
37813         ulongT j = 0;
37814         switch (_axis) {
37815         case 'x' : {
37816           unsigned int i0 = 0, i1 = 0, i = 0;
37817           do {
37818             if ((Tt)(*this)(i)==(Tt)*values) {
37819               i1 = i; j = 0;
37820               while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37821               i-=j;
37822               if (i>i1) {
37823                 if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
37824                 if (keep_values) get_columns(i1,i - 1).move_to(res);
37825                 i0 = i;
37826               } else ++i;
37827             } else ++i;
37828           } while (i<_width);
37829           if (i0<_width) get_columns(i0,width() - 1).move_to(res);
37830         } break;
37831         case 'y' : {
37832           unsigned int i0 = 0, i1 = 0, i = 0;
37833           do {
37834             if ((Tt)(*this)(0,i)==(Tt)*values) {
37835               i1 = i; j = 0;
37836               while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37837               i-=j;
37838               if (i>i1) {
37839                 if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
37840                 if (keep_values) get_rows(i1,i - 1).move_to(res);
37841                 i0 = i;
37842               } else ++i;
37843             } else ++i;
37844           } while (i<_height);
37845           if (i0<_height) get_rows(i0,height() - 1).move_to(res);
37846         } break;
37847         case 'z' : {
37848           unsigned int i0 = 0, i1 = 0, i = 0;
37849           do {
37850             if ((Tt)(*this)(0,0,i)==(Tt)*values) {
37851               i1 = i; j = 0;
37852               while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37853               i-=j;
37854               if (i>i1) {
37855                 if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
37856                 if (keep_values) get_slices(i1,i - 1).move_to(res);
37857                 i0 = i;
37858               } else ++i;
37859             } else ++i;
37860           } while (i<_depth);
37861           if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
37862         } break;
37863         case 'c' : {
37864           unsigned int i0 = 0, i1 = 0, i = 0;
37865           do {
37866             if ((Tt)(*this)(0,0,0,i)==(Tt)*values) {
37867               i1 = i; j = 0;
37868               while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37869               i-=j;
37870               if (i>i1) {
37871                 if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
37872                 if (keep_values) get_channels(i1,i - 1).move_to(res);
37873                 i0 = i;
37874               } else ++i;
37875             } else ++i;
37876           } while (i<_spectrum);
37877           if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
37878         } break;
37879         default : {
37880           const ulongT siz = size();
37881           ulongT i0 = 0, i1 = 0, i = 0;
37882           do {
37883             if ((Tt)(*this)[i]==(Tt)*values) {
37884               i1 = i; j = 0;
37885               while (i<siz && (Tt)(*this)[i]==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37886               i-=j;
37887               if (i>i1) {
37888                 if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
37889                 if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
37890                 i0 = i;
37891               } else ++i;
37892             } else ++i;
37893           } while (i<siz);
37894           if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
37895         } break;
37896         }
37897       }
37898       return res;
37899     }
37900 
37901     //! Append two images along specified axis.
37902     /**
37903        \param img Image to append with instance image.
37904        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37905        \param align Append alignment in \c [0,1].
37906     **/
37907     template<typename t>
37908     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
37909       if (is_empty()) return assign(img,false);
37910       if (!img) return *this;
37911       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
37912     }
37913 
37914     //! Append two images along specified axis \specialization.
37915     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
37916       if (is_empty()) return assign(img,false);
37917       if (!img) return *this;
37918       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
37919     }
37920 
37921     //! Append two images along specified axis \const.
37922     template<typename t>
37923     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
37924       if (is_empty()) return +img;
37925       if (!img) return +*this;
37926       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
37927     }
37928 
37929     //! Append two images along specified axis \specialization.
37930     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
37931       if (is_empty()) return +img;
37932       if (!img) return +*this;
37933       return CImgList<T>(*this,img,true).get_append(axis,align);
37934     }
37935 
37936     //@}
37937     //---------------------------------------
37938     //
37939     //! \name Filtering / Transforms
37940     //@{
37941     //---------------------------------------
37942 
37943     //! Correlate image by a kernel.
37944     /**
37945        \param kernel = the correlation kernel.
37946        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
37947        \param is_normalized = enable local normalization.
37948        \param channel_mode Channel processing mode.
37949                            Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
37950        \param xcenter X-coordinate of the kernel center (~0U>>1 means 'centered').
37951        \param ycenter Y-coordinate of the kernel center (~0U>>1 means 'centered').
37952        \param zcenter Z-coordinate of the kernel center (~0U>>1 means 'centered').
37953        \param xstart Starting X-coordinate of the instance image.
37954        \param ystart Starting Y-coordinate of the instance image.
37955        \param zstart Starting Z-coordinate of the instance image.
37956        \param xend Ending X-coordinate of the instance image.
37957        \param yend Ending Y-coordinate of the instance image.
37958        \param zend Ending Z-coordinate of the instance image.
37959        \param xstride Stride along the X-axis.
37960        \param ystride Stride along the Y-axis.
37961        \param zstride Stride along the Z-axis.
37962        \param xdilation Dilation along the X-axis.
37963        \param ydilation Dilation along the Y-axis.
37964        \param zdilation Dilation along the Z-axis.
37965        \param interpolation_type Can be { false=nearest | true=linear }.
37966        \note
37967        - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
37968        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j -
37969                     c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k).
37970     **/
37971     template<typename t>
37972     CImg<T>& correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
37973                        const bool is_normalized=false, const unsigned int channel_mode=1,
37974                        const int xcenter=(int)(~0U>>1),
37975                        const int ycenter=(int)(~0U>>1),
37976                        const int zcenter=(int)(~0U>>1),
37977                        const int xstart=0,
37978                        const int ystart=0,
37979                        const int zstart=0,
37980                        const int xend=(int)(~0U>>1),
37981                        const int yend=(int)(~0U>>1),
37982                        const int zend=(int)(~0U>>1),
37983                        const float xstride=1, const float ystride=1, const float zstride=1,
37984                        const float xdilation=1, const float ydilation=1, const float zdilation=1,
37985                        const bool interpolation_type=false) {
37986       if (is_empty() || !kernel) return *this;
37987       return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode,
37988                            xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
37989                            xstride,ystride,zstride,xdilation,ydilation,zdilation,
37990                            interpolation_type).move_to(*this);
37991     }
37992 
37993     template<typename t>
37994     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
37995                                       const bool is_normalized=false, const unsigned int channel_mode=1,
37996                                       const int xcenter=(int)(~0U>>1),
37997                                       const int ycenter=(int)(~0U>>1),
37998                                       const int zcenter=(int)(~0U>>1),
37999                                       const int xstart=0,
38000                                       const int ystart=0,
38001                                       const int zstart=0,
38002                                       const int xend=(int)(~0U>>1),
38003                                       const int yend=(int)(~0U>>1),
38004                                       const int zend=(int)(~0U>>1),
38005                                       const float xstride=1, const float ystride=1, const float zstride=1,
38006                                       const float xdilation=1, const float ydilation=1, const float zdilation=1,
38007                                       const bool interpolation_type=false) const {
38008       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38009                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38010                         xstride,ystride,zstride,xdilation,ydilation,zdilation,
38011                         interpolation_type,false);
38012     }
38013 
38014     //! Correlate image by a kernel \newinstance.
38015     template<typename t>
38016     CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const unsigned int boundary_conditions,
38017                                    const bool is_normalized, const unsigned int channel_mode,
38018                                    const int xcenter, const int ycenter, const int zcenter,
38019                                    const int xstart, const int ystart, const int zstart,
38020                                    const int xend, const int yend, const int zend,
38021                                    const float xstride, const float ystride, const float zstride,
38022                                    const float xdilation, const float ydilation, const float zdilation,
38023                                    const bool interpolation_type, const bool is_convolve) const {
38024       typedef _cimg_Ttfloat Ttfloat;
38025       CImg<Ttfloat> res;
38026       _cimg_abort_init_openmp;
38027       cimg_abort_init;
38028 
38029       if (xstart>xend || ystart>yend || zstart>zend)
38030         throw CImgArgumentException(_cimg_instance
38031                                     "%s(): Invalid xyz-start/end arguments (start = (%d,%d,%d), end = (%d,%d,%d)).",
38032                                     cimg_instance,
38033                                     is_convolve?"convolve":"correlate",
38034                                     xstart,ystart,zstart,xend,yend,zend);
38035       if (xstride<=0 || ystride<=0 || zstride<=0)
38036         throw CImgArgumentException(_cimg_instance
38037                                     "%s(): Invalid stride arguments (%g,%g,%g).",
38038                                     cimg_instance,
38039                                     is_convolve?"convolve":"correlate",
38040                                     xstride,ystride,zstride);
38041 
38042       if (is_empty() || !kernel) return *this;
38043       int
38044         _xcenter = xcenter==(int)(~0U>>1)?kernel.width()/2 - 1 + (kernel.width()%2):
38045         std::min(xcenter,kernel.width() - 1),
38046         _ycenter = ycenter==(int)(~0U>>1)?kernel.height()/2 - 1 + (kernel.height()%2):
38047         std::min(ycenter,kernel.height() - 1),
38048         _zcenter = zcenter==(int)(~0U>>1)?kernel.depth()/2 - 1 + (kernel.depth()%2):
38049         std::min(zcenter,kernel.depth() - 1);
38050       float _xdilation = xdilation, _ydilation = ydilation, _zdilation = zdilation;
38051 
38052       CImg<t> _kernel;
38053       if (is_convolve) { // If convolution, go back to correlation
38054         if (kernel.size()/kernel.spectrum()<=27) {
38055           _kernel = CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
38056             get_mirror('x').resize(kernel,-1);
38057           _xcenter = kernel.width() - 1 - _xcenter;
38058           _ycenter = kernel.height() - 1 - _ycenter;
38059           _zcenter = kernel.depth() - _zcenter - 1;
38060         } else { _kernel = kernel.get_shared(); _xdilation*=-1; _ydilation*=-1; _zdilation*=-1; }
38061       } else _kernel = kernel.get_shared();
38062 
38063       const int
38064         _xend = xend==(int)(~0U>>1)?width() - 1:xend,
38065         _yend = yend==(int)(~0U>>1)?height() - 1:yend,
38066         _zend = zend==(int)(~0U>>1)?depth() - 1:zend,
38067         i_xstride = (int)cimg::round(xstride),
38068         i_ystride = (int)cimg::round(ystride),
38069         i_zstride = (int)cimg::round(zstride),
38070         i_xdilation = (int)cimg::round(_xdilation),
38071         i_ydilation = (int)cimg::round(_ydilation),
38072         i_zdilation = (int)cimg::round(_zdilation),
38073         res_width = _xend - xstart + 1,
38074         res_height = _yend - ystart + 1,
38075         res_depth = _zend + zstart + 1,
38076         smin = std::min(spectrum(),_kernel.spectrum()),
38077         smax = std::max(spectrum(),_kernel.spectrum()),
38078         cend = !channel_mode?spectrum()*_kernel.spectrum():smax;
38079       const ulongT
38080         res_wh = (ulongT)res_width*res_height,
38081         res_whd = res_wh*res_depth,
38082         res_siz = res_whd*res._spectrum;
38083 
38084       if (!res_whd) return CImg<Ttfloat>();
38085       res.assign(res_width,res_height,res_depth,
38086                  !channel_mode?_spectrum*_kernel._spectrum:
38087                  channel_mode==1?smax:
38088                  channel_mode==2?(int)std::ceil((float)smax/smin):1);
38089       if (channel_mode>=2) res.fill(0);
38090 
38091       const bool
38092 #if cimg_use_openmp==1
38093         is_master_thread = !omp_get_thread_num(),
38094 #else
38095         is_master_thread = true,
38096 #endif
38097         is_outer_parallel = is_master_thread &&
38098         (res._spectrum>=cimg::nb_cpus() || res_siz<=(cimg_openmp_sizefactor)*32768),
38099         is_inner_parallel = is_master_thread &&
38100         (!is_outer_parallel && res_whd>=(cimg_openmp_sizefactor)*32768),
38101         is_int_stride_dilation = xstride==i_xstride && ystride==i_ystride && zstride==i_zstride &&
38102         _xdilation==i_xdilation && _ydilation==i_ydilation && _zdilation==i_zdilation;
38103       cimg::unused(is_inner_parallel,is_outer_parallel);
38104       const int
38105         w = width(), h = height(), d = depth(),
38106         w1 = w  - 1, h1 = h - 1, d1 = d - 1,
38107         w2 = 2*w, h2 = 2*h, d2 = 2*h;
38108       const ulongT
38109         wh = (ulongT)w*h, whd = wh*d,
38110         K_wh = (ulongT)kernel.width()*kernel.height(), K_whd = K_wh*kernel.depth();
38111 
38112       // Reshape kernel to enable optimizations for a few cases.
38113       if (boundary_conditions==1 &&
38114           _kernel._width>1 && _kernel._height>1 &&
38115           ((_kernel._depth==1 && _kernel._width<=5 && _kernel._height<=5) ||
38116            (_kernel._depth<=3 && _kernel._width<=3 && _kernel._height<=3)) &&
38117           xstart>=0 && ystart>=0 && zstart>=0 &&
38118           _xend<width() && _yend<height() && _zend<depth() &&
38119           is_int_stride_dilation &&
38120           xstride==1 && ystride==1 && zstride==1 &&
38121           i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
38122         const unsigned int M = cimg::max(_kernel._width,_kernel._height,_kernel._depth);
38123         _kernel.assign(_kernel.get_resize(M + 1 - (M%2),M + 1 - (M%2),_kernel._depth>1?M + 1 - (M%2):1,-100,
38124                                           0,0,
38125                                           1,1,1),false);
38126         _xcenter = _ycenter = (int)M/2;
38127         if (_kernel._depth>1) _ycenter = (int)M/2;
38128       }
38129 
38130       // Optimized version for a few particular cases (3x3, 5x5 and 3x3x3 kernels, with a few other conditions).
38131       if (boundary_conditions==1 &&
38132           _kernel._width==_kernel._height &&
38133           ((_kernel._depth==1 && (_kernel._width==3 || _kernel._width==5)) ||
38134            (_kernel._depth==_kernel._width && _kernel._width==3)) &&
38135           _xcenter==_kernel.width()/2 && _ycenter==_kernel.height()/2 && _zcenter==_kernel.depth()/2 &&
38136           xstart>=0 && ystart>=0 && zstart>=0 &&
38137           _xend<width() && _yend<height() && _zend<depth() &&
38138           is_int_stride_dilation &&
38139           xstride==1 && ystride==1 && zstride==1 &&
38140           i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
38141 
38142         switch (_kernel._depth) {
38143         case 3 : { // 3x3x3 centered kernel
38144           cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38145           for (int c = 0; c<cend; ++c) {
38146             cimg_abort_test;
38147             const CImg<T> I = get_shared_channel(c%_spectrum);
38148             const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38149             CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38150               CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38151             if (is_normalized) {
38152               const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38153               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38154                 cimg_forXYZ(res,X,Y,Z) {
38155                 const int
38156                   x = xstart + X, y = ystart + Y, z = zstart + Z,
38157                   px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38158                   py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
38159                   pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
38160                 const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
38161                                       cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
38162                                       cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
38163                                       cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
38164                                       cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
38165                                       cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
38166                                       cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
38167                                       cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
38168                                       cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
38169                 _res(X,Y,Z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
38170                                            K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
38171                                            K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
38172                                            K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
38173                                            K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
38174                                            K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
38175                                            K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
38176                                            K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
38177                                            K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
38178               }
38179             } else {
38180               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38181                 cimg_forXYZ(res,X,Y,Z) {
38182                 const int
38183                   x = xstart + X, y = ystart + Y, z = zstart + Z,
38184                   px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38185                   py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
38186                   pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
38187                 _res(X,Y,Z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
38188                                         K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
38189                                         K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
38190                                         K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
38191                                         K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
38192                                         K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
38193                                         K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
38194                                         K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
38195                                         K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
38196               }
38197             }
38198             if (channel_mode==2)
38199               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38200             else if (channel_mode==3)
38201               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38202           }
38203         } break;
38204 
38205         default :
38206         case 1 :
38207           switch (_kernel._width) {
38208           case 5 : { // 5x5 centered kernel
38209             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38210             for (int c = 0; c<cend; ++c) {
38211               cimg_abort_test;
38212               const CImg<T> I = get_shared_channel(c%_spectrum);
38213               const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38214               CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38215                 CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38216               if (is_normalized) {
38217                 const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38218                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38219                   cimg_forXYZ(res,X,Y,z) {
38220                   const int
38221                     x = xstart + X, y = ystart + Y,
38222                     px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
38223                     nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
38224                     py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
38225                     ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
38226                   const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
38227                                         cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
38228                                         cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
38229                                         cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
38230                                         cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
38231                                         cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
38232                                         cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
38233                                         cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
38234                                         cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
38235                                         cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
38236                   _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
38237                                              K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
38238                                              K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
38239                                              K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
38240                                              K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
38241                                              K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
38242                                              K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
38243                                              K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
38244                                              K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
38245                                              K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
38246                 }
38247               } else {
38248                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38249                   cimg_forXYZ(res,X,Y,z) {
38250                   const int
38251                     x = xstart + X, y = ystart + Y,
38252                     px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
38253                     nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
38254                     py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
38255                     ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
38256                   _res(X,Y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
38257                                           K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
38258                                           K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
38259                                           K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
38260                                           K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
38261                                           K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
38262                                           K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
38263                                           K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
38264                                           K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
38265                                           K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
38266                 }
38267               }
38268             if (channel_mode==2)
38269               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38270             else if (channel_mode==3)
38271               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38272             }
38273           } break;
38274 
38275           case 3 : { // 3x3 centered kernel
38276             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38277             for (int c = 0; c<cend; ++c) {
38278               cimg_abort_test;
38279               const CImg<T> I = get_shared_channel(c%_spectrum);
38280               const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38281               CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38282                 CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38283               if (is_normalized) {
38284                 const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38285                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38286                   cimg_forXYZ(res,X,Y,z) {
38287                   const int
38288                     x = xstart + X, y = ystart + Y,
38289                     px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38290                     py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
38291                   const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
38292                                         cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
38293                                         cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
38294                   _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
38295                                              K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
38296                                              K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
38297                 }
38298               } else {
38299                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38300                   cimg_forXYZ(res,X,Y,z) {
38301                   const int
38302                     x = xstart + X, y = ystart + Y,
38303                     px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38304                     py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
38305                   _res(X,Y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
38306                                           K[3]*I(px,y,z)  + K[4]*I(x,y,z)  + K[5]*I(nx,y,z) +
38307                                           K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
38308                 }
38309               }
38310             if (channel_mode==2)
38311               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38312             else if (channel_mode==3)
38313               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38314             }
38315           } break;
38316           }
38317         }
38318       } else if (_kernel._width==1 && _kernel._height==1 && _kernel._depth==1 &&
38319                  !_xcenter && !_ycenter && !_zcenter &&
38320                  xstart>=0 && ystart>=0 && zstart>=0 &&
38321                  _xend<width() && _yend<height() && _zend<depth() &&
38322                  xstride==1 && ystride==1 && zstride==1) {
38323 
38324         // Special optimization for 1x1 kernel.
38325         cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38326         for (int c = 0; c<cend; ++c) {
38327           const t valK = _kernel[!channel_mode?c/_spectrum:c%_kernel._spectrum];
38328           CImg<T> I = get_crop(xstart,ystart,zstart,c%_spectrum,_xend,_yend,_zend,c%_spectrum)*=valK;
38329           if (is_normalized) I.sign();
38330           switch (channel_mode) {
38331           case 0 : // All
38332           case 1 : // One for one
38333             res.get_shared_channel(c) = I;
38334             break;
38335           case 2 : // Partial sum
38336             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=I;
38337             break;
38338           case 3 : // Full sum
38339             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=I;
38340             break;
38341           }
38342         }
38343       } else { // Generic version
38344         cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38345           for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp {
38346           cimg_abort_test;
38347           const CImg<T> I = get_shared_channel(c%_spectrum);
38348           const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38349           CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38350             CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38351           Ttfloat M = 0, M2 = 0;
38352           if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = cimg::sqr(M); }
38353 
38354 #define _cimg_correlate_x_int const int ix = xstart + i_xstride*x + i_xdilation*(p - _xcenter)
38355 #define _cimg_correlate_y_int const int iy = ystart + i_ystride*y + i_ydilation*(q - _ycenter)
38356 #define _cimg_correlate_z_int const int iz = zstart + i_zstride*z + i_zdilation*(r - _zcenter)
38357 #define _cimg_correlate_x_float const float ix = xstart + xstride*x + _xdilation*(p - _xcenter)
38358 #define _cimg_correlate_y_float const float iy = ystart + ystride*y + _ydilation*(q - _ycenter)
38359 #define _cimg_correlate_z_float const float iz = zstart + zstride*z + _zdilation*(r - _zcenter)
38360 
38361 #define _cimg_correlate_x_int_dirichlet const bool is_in_x = ix>=0 && ix<w
38362 #define _cimg_correlate_y_int_dirichlet const bool is_in_y = iy>=0 && iy<h
38363 #define _cimg_correlate_z_int_dirichlet const bool is_in_z = iz>=0 && iz<d
38364 #define _cimg_correlate_x_float_dirichlet _cimg_correlate_x_int_dirichlet
38365 #define _cimg_correlate_y_float_dirichlet _cimg_correlate_y_int_dirichlet
38366 #define _cimg_correlate_z_float_dirichlet _cimg_correlate_z_int_dirichlet
38367 
38368 #define _cimg_correlate_x_int_neumann const int nix = cimg::cut(ix,0,w1)
38369 #define _cimg_correlate_y_int_neumann const int niy = cimg::cut(iy,0,h1)
38370 #define _cimg_correlate_z_int_neumann const int niz = cimg::cut(iz,0,d1)
38371 #define _cimg_correlate_x_float_neumann const float nix = cimg::cut(ix,0,w1)
38372 #define _cimg_correlate_y_float_neumann const float niy = cimg::cut(iy,0,h1)
38373 #define _cimg_correlate_z_float_neumann const float niz = cimg::cut(iz,0,d1)
38374 
38375 #define _cimg_correlate_x_int_periodic const int nix = cimg::mod(ix,w)
38376 #define _cimg_correlate_y_int_periodic const int niy = cimg::mod(iy,h)
38377 #define _cimg_correlate_z_int_periodic const int niz = cimg::mod(iz,d)
38378 #define _cimg_correlate_x_float_periodic const float nix = cimg::mod(ix,w)
38379 #define _cimg_correlate_y_float_periodic const float niy = cimg::mod(iy,h)
38380 #define _cimg_correlate_z_float_periodic const float niz = cimg::mod(iz,d)
38381 
38382 #define _cimg_correlate_x_int_mirror const int mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
38383 #define _cimg_correlate_y_int_mirror const int my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
38384 #define _cimg_correlate_z_int_mirror const int mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
38385 #define _cimg_correlate_x_float_mirror const float mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
38386 #define _cimg_correlate_y_float_mirror const float my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
38387 #define _cimg_correlate_z_float_mirror const float mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
38388 
38389 #define _cimg_correlate(type,boundary,access) \
38390           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
38391           cimg_forXYZ(res,x,y,z) { \
38392             Ttfloat val = 0; \
38393             cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
38394               cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
38395                 cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
38396                   val+=K(p,q,r,0,K_wh,K_whd)*(access); \
38397                 } \
38398               } \
38399             } \
38400             _res(x,y,z,0,res_wh,res_whd) = val; \
38401           }
38402 
38403 #define _cimg_correlate_n(type,boundary,access) \
38404           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
38405           cimg_forXYZ(res,x,y,z) { \
38406             Ttfloat val = 0, N = 0; \
38407             cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
38408               cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
38409                 cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
38410                   Ttfloat _val = access; \
38411                   val+=K(p,q,r,0,K_wh,K_whd)*_val; \
38412                   _val*=_val; N+=_val; \
38413                 } \
38414               } \
38415             } \
38416             N*=M2; _res(x,y,z,0,res_wh,res_whd) = N?val/std::sqrt(N):0; \
38417           }
38418 
38419           if (is_normalized) { // Normalized convolution/correlation
38420             if (is_int_stride_dilation) // Integer stride and dilation
38421               switch (boundary_conditions) {
38422               case 0 : // Dirichlet
38423                 _cimg_correlate_n(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):0);
38424                 break;
38425               case 1 : // Neumann
38426                 _cimg_correlate_n(int,neumann,I(nix,niy,niz,0,wh,whd));
38427                 break;
38428               case 2 : // Periodic
38429                 _cimg_correlate_n(int,periodic,I(nix,niy,niz,0,wh,whd));
38430                 break;
38431               case 3 : // Mirror
38432                 _cimg_correlate_n(int,mirror,I(nix,niy,niz,0,wh,whd));
38433                 break;
38434               }
38435             else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
38436               switch (boundary_conditions) {
38437               case 0 : // Dirichlet
38438                 _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
38439                 break;
38440               case 1 : // Neumann
38441                 _cimg_correlate_n(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
38442                 break;
38443               case 2 : // Periodic
38444                 _cimg_correlate_n(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
38445                 break;
38446               case 3 : // Mirror
38447                 _cimg_correlate_n(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
38448                 break;
38449               }
38450             else // Non-integer stride or dilation, nearest-neighbor interpolation
38451               switch (boundary_conditions) {
38452               case 0 : // Dirichlet
38453                 _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):0);
38454                 break;
38455               case 1 : // Neumann
38456                 _cimg_correlate_n(float,neumann,I((int)nix,(int)niy,(int)niz,0));
38457                 break;
38458               case 2 : // Periodic
38459                 _cimg_correlate_n(float,periodic,I((int)nix,(int)niy,(int)niz,0));
38460                 break;
38461               case 3 : // Mirror
38462                 _cimg_correlate_n(float,mirror,I((int)nix,(int)niy,(int)niz,0));
38463                 break;
38464               }
38465           } else { // Standard convolution/correlation
38466             if (is_int_stride_dilation) // Integer stride and dilation
38467               switch (boundary_conditions) {
38468               case 0 : // Dirichlet
38469                 _cimg_correlate(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):0);
38470                 break;
38471               case 1 : // Neumann
38472                 _cimg_correlate(int,neumann,I(nix,niy,niz,0,wh,whd));
38473                 break;
38474               case 2 : // Periodic
38475                 _cimg_correlate(int,periodic,I(nix,niy,niz,0,wh,whd));
38476                 break;
38477               case 3 : // Mirror
38478                 _cimg_correlate(int,mirror,I(nix,niy,niz,0,wh,whd));
38479                 break;
38480               }
38481             else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
38482               switch (boundary_conditions) {
38483               case 0 : // Dirichlet
38484                 _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
38485                 break;
38486               case 1 : // Neumann
38487                 _cimg_correlate(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
38488                 break;
38489               case 2 : // Periodic
38490                 _cimg_correlate(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
38491                 break;
38492               case 3 : // Mirror
38493                 _cimg_correlate(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
38494                 break;
38495               }
38496             else // Non-integer stride or dilation, nearest-neighbor interpolation
38497               switch (boundary_conditions) {
38498               case 0 : // Dirichlet
38499                 _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):0);
38500                 break;
38501               case 1 : // Neumann
38502                 _cimg_correlate(float,neumann,I((int)nix,(int)niy,(int)niz,0));
38503                 break;
38504               case 2 : // Periodic
38505                 _cimg_correlate(float,periodic,I((int)nix,(int)niy,(int)niz,0));
38506                 break;
38507               case 3 : // Mirror
38508                 _cimg_correlate(float,mirror,I((int)nix,(int)niy,(int)niz,0));
38509                 break;
38510               }
38511           }
38512           if (channel_mode==2)
38513             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38514           else if (channel_mode==3)
38515             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38516 
38517         } _cimg_abort_catch_openmp
38518           cimg_abort_test;
38519       }
38520       return res;
38521     }
38522 
38523     //! Convolve image by a kernel.
38524     /**
38525        \param kernel = the correlation kernel.
38526        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
38527        \param is_normalized = enable local normalization.
38528        \param channel_mode Channel processing mode.
38529                            Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
38530        \param xcenter X-coordinate of the kernel center (~0U means 'centered').
38531        \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
38532        \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
38533        \param xstart Starting X-coordinate of the instance image.
38534        \param ystart Starting Y-coordinate of the instance image.
38535        \param zstart Starting Z-coordinate of the instance image.
38536        \param xend Ending X-coordinate of the instance image.
38537        \param yend Ending Y-coordinate of the instance image.
38538        \param zend Ending Z-coordinate of the instance image.
38539        \param xstride Stride along the X-axis.
38540        \param ystride Stride along the Y-axis.
38541        \param zstride Stride along the Z-axis.
38542        \param xdilation Dilation along the X-axis.
38543        \param ydilation Dilation along the Y-axis.
38544        \param zdilation Dilation along the Z-axis.
38545        \param interpolation_type Can be { false=nearest | true=linear }.
38546        \note
38547        - The convolution of the image instance \p *this by the kernel \p kernel is defined to be:
38548        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x - \beta_x\;(i - c_x),\alpha_y\;y
38549                     - \beta_y\;(j - c_y),\alpha_z\;z - \beta_z\;(k - c_z))*kernel(i,j,k).
38550     **/
38551     template<typename t>
38552     CImg<T>& convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38553                       const bool is_normalized=false, const unsigned int channel_mode=1,
38554                       const int xcenter=(int)(~0U>>1),
38555                       const int ycenter=(int)(~0U>>1),
38556                       const int zcenter=(int)(~0U>>1),
38557                       const int xstart=0,
38558                       const int ystart=0,
38559                       const int zstart=0,
38560                       const int xend=(int)(~0U>>1),
38561                       const int yend=(int)(~0U>>1),
38562                       const int zend=(int)(~0U>>1),
38563                       const float xstride=1, const float ystride=1, const float zstride=1,
38564                       const float xdilation=1, const float ydilation=1, const float zdilation=1,
38565                       const bool interpolation_type=false) {
38566       if (is_empty() || !kernel) return *this;
38567       return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode,
38568                           xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38569                           xstride,ystride,zstride,xdilation,ydilation,zdilation,
38570                           interpolation_type).move_to(*this);
38571     }
38572 
38573     //! Convolve image by a kernel \newinstance.
38574     template<typename t>
38575     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38576                                      const bool is_normalized=false, const unsigned int channel_mode=1,
38577                                      const int xcenter=(int)(~0U>>1),
38578                                      const int ycenter=(int)(~0U>>1),
38579                                      const int zcenter=(int)(~0U>>1),
38580                                      const int xstart=0,
38581                                      const int ystart=0,
38582                                      const int zstart=0,
38583                                      const int xend=(int)(~0U>>1),
38584                                      const int yend=(int)(~0U>>1),
38585                                      const int zend=(int)(~0U>>1),
38586                                      const float xstride=1, const float ystride=1, const float zstride=1,
38587                                      const float xdilation=1, const float ydilation=1, const float zdilation=1,
38588                                      const bool interpolation_type=false) const {
38589       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38590                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38591                         xstride,ystride,zstride,xdilation,ydilation,zdilation,
38592                         interpolation_type,true);
38593     }
38594 
38595     //! Cumulate image values, optionally along specified axis.
38596     /**
38597        \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
38598     **/
38599     CImg<T>& cumulate(const char axis=0) {
38600       switch (cimg::lowercase(axis)) {
38601       case 'x' :
38602         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
38603                                                                    _height*_depth*_spectrum>=16))
38604         cimg_forYZC(*this,y,z,c) {
38605           T *ptrd = data(0,y,z,c);
38606           Tlong cumul = (Tlong)0;
38607           cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
38608         }
38609         break;
38610       case 'y' : {
38611         const ulongT w = (ulongT)_width;
38612         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 &&
38613                                                                    _width*_depth*_spectrum>=16))
38614         cimg_forXZC(*this,x,z,c) {
38615           T *ptrd = data(x,0,z,c);
38616           Tlong cumul = (Tlong)0;
38617           cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
38618         }
38619       } break;
38620       case 'z' : {
38621         const ulongT wh = (ulongT)_width*_height;
38622         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 &&
38623                                                                    _width*_depth*_spectrum>=16))
38624         cimg_forXYC(*this,x,y,c) {
38625           T *ptrd = data(x,y,0,c);
38626           Tlong cumul = (Tlong)0;
38627           cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
38628         }
38629       } break;
38630       case 'c' : {
38631         const ulongT whd = (ulongT)_width*_height*_depth;
38632         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
38633                            cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16))
38634         cimg_forXYZ(*this,x,y,z) {
38635           T *ptrd = data(x,y,z,0);
38636           Tlong cumul = (Tlong)0;
38637           cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
38638         }
38639       } break;
38640       default : { // Global cumulation
38641         Tlong cumul = (Tlong)0;
38642         cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
38643       }
38644       }
38645       return *this;
38646     }
38647 
38648     //! Cumulate image values, optionally along specified axis \newinstance.
38649     CImg<Tlong> get_cumulate(const char axis=0) const {
38650       return CImg<Tlong>(*this,false).cumulate(axis);
38651     }
38652 
38653     //! Cumulate image values, along specified axes.
38654     /**
38655        \param axes Cumulation axes, as a C-string.
38656        \note \c axes may contains multiple characters, e.g. \c "xyz"
38657     **/
38658     CImg<T>& cumulate(const char *const axes) {
38659       for (const char *s = axes; *s; ++s) cumulate(*s);
38660       return *this;
38661     }
38662 
38663     //! Cumulate image values, along specified axes \newinstance.
38664     CImg<Tlong> get_cumulate(const char *const axes) const {
38665       return CImg<Tlong>(*this,false).cumulate(axes);
38666     }
38667 
38668     //! Erode image by a structuring element.
38669     /**
38670        \param kernel Structuring element.
38671        \param boundary_conditions Boundary conditions.
38672        \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
38673     **/
38674     template<typename t>
38675     CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true,
38676                    const bool is_real=false) {
38677       if (is_empty() || !kernel) return *this;
38678       return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
38679     }
38680 
38681     //! Erode image by a structuring element \newinstance.
38682     template<typename t>
38683     CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true,
38684                              const bool is_real=false) const {
38685       if (is_empty() || !kernel) return *this;
38686       if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
38687       typedef _cimg_Tt Tt;
38688       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
38689       const int
38690         mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
38691         mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
38692         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
38693       const bool
38694         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
38695         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
38696       cimg::unused(is_inner_parallel,is_outer_parallel);
38697       _cimg_abort_init_openmp;
38698       cimg_abort_init;
38699       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
38700       cimg_forC(res,c) _cimg_abort_try_openmp {
38701         cimg_abort_test;
38702         const CImg<T> img = get_shared_channel(c%_spectrum);
38703         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
38704         if (is_real) { // Real erosion
38705           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38706           for (int z = mz1; z<mze; ++z)
38707             for (int y = my1; y<mye; ++y)
38708               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38709                 cimg_abort_test2;
38710                 Tt min_val = cimg::type<Tt>::max();
38711                 for (int zm = -mz1; zm<=mz2; ++zm)
38712                   for (int ym = -my1; ym<=my2; ++ym)
38713                     for (int xm = -mx1; xm<=mx2; ++xm) {
38714                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38715                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
38716                       if (cval<min_val) min_val = cval;
38717                     }
38718                 res(x,y,z,c) = min_val;
38719               } _cimg_abort_catch_openmp2
38720           if (boundary_conditions)
38721             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38722             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38723               cimg_abort_test2;
38724               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38725                 Tt min_val = cimg::type<Tt>::max();
38726                 for (int zm = -mz1; zm<=mz2; ++zm)
38727                   for (int ym = -my1; ym<=my2; ++ym)
38728                     for (int xm = -mx1; xm<=mx2; ++xm) {
38729                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38730                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval);
38731                       if (cval<min_val) min_val = cval;
38732                     }
38733                 res(x,y,z,c) = min_val;
38734               }
38735             } _cimg_abort_catch_openmp2
38736           else
38737             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38738             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38739               cimg_abort_test2;
38740               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38741                 Tt min_val = cimg::type<Tt>::max();
38742                 for (int zm = -mz1; zm<=mz2; ++zm)
38743                   for (int ym = -my1; ym<=my2; ++ym)
38744                     for (int xm = -mx1; xm<=mx2; ++xm) {
38745                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38746                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval);
38747                       if (cval<min_val) min_val = cval;
38748                     }
38749                 res(x,y,z,c) = min_val;
38750               }
38751             } _cimg_abort_catch_openmp2
38752 
38753         } else { // Binary erosion
38754           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38755           for (int z = mz1; z<mze; ++z)
38756             for (int y = my1; y<mye; ++y)
38757               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38758                 cimg_abort_test2;
38759                 Tt min_val = cimg::type<Tt>::max();
38760                 for (int zm = -mz1; zm<=mz2; ++zm)
38761                   for (int ym = -my1; ym<=my2; ++ym)
38762                     for (int xm = -mx1; xm<=mx2; ++xm)
38763                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38764                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
38765                         if (cval<min_val) min_val = cval;
38766                       }
38767                 res(x,y,z,c) = min_val;
38768               } _cimg_abort_catch_openmp2
38769           if (boundary_conditions)
38770             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38771             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38772               cimg_abort_test2;
38773               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38774                 Tt min_val = cimg::type<Tt>::max();
38775                 for (int zm = -mz1; zm<=mz2; ++zm)
38776                   for (int ym = -my1; ym<=my2; ++ym)
38777                     for (int xm = -mx1; xm<=mx2; ++xm)
38778                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38779                         const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
38780                         if (cval<min_val) min_val = cval;
38781                       }
38782                 res(x,y,z,c) = min_val;
38783               }
38784             } _cimg_abort_catch_openmp2
38785           else
38786             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38787             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38788               cimg_abort_test2;
38789               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38790                 Tt min_val = cimg::type<Tt>::max();
38791                 for (int zm = -mz1; zm<=mz2; ++zm)
38792                   for (int ym = -my1; ym<=my2; ++ym)
38793                     for (int xm = -mx1; xm<=mx2; ++xm)
38794                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38795                         const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
38796                         if (cval<min_val) min_val = cval;
38797                       }
38798                 res(x,y,z,c) = min_val;
38799               }
38800             } _cimg_abort_catch_openmp2
38801         }
38802       } _cimg_abort_catch_openmp
38803       cimg_abort_test;
38804       return res;
38805     }
38806 
38807     //! Erode image by a rectangular structuring element of specified size.
38808     /**
38809        \param sx Width of the structuring element.
38810        \param sy Height of the structuring element.
38811        \param sz Depth of the structuring element.
38812     **/
38813     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
38814       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
38815       if (sx>1 && _width>1) { // Along X-axis
38816         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;
38817         CImg<T> buf(L);
38818         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38819         cimg_forYZC(*this,y,z,c) {
38820           T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
38821           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38822           T cur = *ptrs; ptrs+=off; bool is_first = true;
38823           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38824             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
38825           *(ptrd++) = cur;
38826           if (ptrs>=ptrse) {
38827             T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38828           } else {
38829             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38830               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38831               *(ptrd++) = cur;
38832             }
38833             for (int p = L - s - 1; p>0; --p) {
38834               const T val = *ptrs; ptrs+=off;
38835               if (is_first) {
38836                 const T *nptrs = ptrs - off; cur = val;
38837                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38838                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38839               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38840               *(ptrd++) = cur;
38841             }
38842             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38843             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38844               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38845             }
38846             *(ptrd--) = cur;
38847             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38848               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38849             }
38850             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38851           }
38852         }
38853       }
38854 
38855       if (sy>1 && _height>1) { // Along Y-axis
38856         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
38857           s2 = _s2>L?L:_s2;
38858         CImg<T> buf(L);
38859         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38860         cimg_forXZC(*this,x,z,c) {
38861           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38862           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38863           T cur = *ptrs; ptrs+=off; bool is_first = true;
38864           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38865             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38866           }
38867           *(ptrd++) = cur;
38868           if (ptrs>=ptrse) {
38869             T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38870           } else {
38871             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38872               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38873               *(ptrd++) = cur;
38874             }
38875             for (int p = L - s - 1; p>0; --p) {
38876               const T val = *ptrs; ptrs+=off;
38877               if (is_first) {
38878                 const T *nptrs = ptrs - off; cur = val;
38879                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38880                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38881               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38882               *(ptrd++) = cur;
38883             }
38884             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38885             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38886               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38887             }
38888             *(ptrd--) = cur;
38889             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38890               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38891             }
38892             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38893           }
38894         }
38895       }
38896 
38897       if (sz>1 && _depth>1) { // Along Z-axis
38898         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
38899           s2 = _s2>L?L:_s2;
38900         CImg<T> buf(L);
38901         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38902         cimg_forXYC(*this,x,y,c) {
38903           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38904           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38905           T cur = *ptrs; ptrs+=off; bool is_first = true;
38906           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38907             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38908           }
38909           *(ptrd++) = cur;
38910           if (ptrs>=ptrse) {
38911             T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38912           } else {
38913             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38914               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38915               *(ptrd++) = cur;
38916             }
38917             for (int p = L - s - 1; p>0; --p) {
38918               const T val = *ptrs; ptrs+=off;
38919               if (is_first) {
38920                 const T *nptrs = ptrs - off; cur = val;
38921                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38922                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38923               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38924               *(ptrd++) = cur;
38925             }
38926             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38927             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38928               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38929             }
38930             *(ptrd--) = cur;
38931             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38932               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38933             }
38934             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38935           }
38936         }
38937       }
38938       return *this;
38939     }
38940 
38941     //! Erode image by a rectangular structuring element of specified size \newinstance.
38942     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
38943       return (+*this).erode(sx,sy,sz);
38944     }
38945 
38946     //! Erode the image by a square structuring element of specified size.
38947     /**
38948        \param s Size of the structuring element.
38949     **/
38950     CImg<T>& erode(const unsigned int s) {
38951       return erode(s,s,s);
38952     }
38953 
38954     //! Erode the image by a square structuring element of specified size \newinstance.
38955     CImg<T> get_erode(const unsigned int s) const {
38956       return (+*this).erode(s);
38957     }
38958 
38959     //! Dilate image by a structuring element.
38960     /**
38961        \param kernel Structuring element.
38962        \param boundary_conditions Boundary conditions.
38963        \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
38964     **/
38965     template<typename t>
38966     CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
38967                     const bool is_real=false) {
38968       if (is_empty() || !kernel) return *this;
38969       return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
38970     }
38971 
38972     //! Dilate image by a structuring element \newinstance.
38973     template<typename t>
38974     CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
38975                               const bool is_real=false) const {
38976       if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
38977       typedef _cimg_Tt Tt;
38978       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
38979       const int
38980         mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
38981         mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
38982         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
38983       const bool
38984         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
38985         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
38986       cimg::unused(is_inner_parallel,is_outer_parallel);
38987       _cimg_abort_init_openmp;
38988       cimg_abort_init;
38989       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
38990       cimg_forC(res,c) _cimg_abort_try_openmp {
38991         cimg_abort_test;
38992         const CImg<T> img = get_shared_channel(c%_spectrum);
38993         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
38994         if (is_real) { // Real dilation
38995           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38996           for (int z = mz1; z<mze; ++z)
38997             for (int y = my1; y<mye; ++y)
38998               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38999                 cimg_abort_test2;
39000                 Tt max_val = cimg::type<Tt>::min();
39001                 for (int zm = -mz1; zm<=mz2; ++zm)
39002                   for (int ym = -my1; ym<=my2; ++ym)
39003                     for (int xm = -mx1; xm<=mx2; ++xm) {
39004                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
39005                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
39006                       if (cval>max_val) max_val = cval;
39007                     }
39008                 res(x,y,z,c) = max_val;
39009               } _cimg_abort_catch_openmp2
39010           if (boundary_conditions)
39011             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39012             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39013               cimg_abort_test2;
39014               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39015                 Tt max_val = cimg::type<Tt>::min();
39016                 for (int zm = -mz1; zm<=mz2; ++zm)
39017                   for (int ym = -my1; ym<=my2; ++ym)
39018                     for (int xm = -mx1; xm<=mx2; ++xm) {
39019                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
39020                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval);
39021                       if (cval>max_val) max_val = cval;
39022                     }
39023                 res(x,y,z,c) = max_val;
39024               }
39025             } _cimg_abort_catch_openmp2
39026           else
39027             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39028             cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
39029               cimg_abort_test2;
39030               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39031                 Tt max_val = cimg::type<Tt>::min();
39032                 for (int zm = -mz1; zm<=mz2; ++zm)
39033                   for (int ym = -my1; ym<=my2; ++ym)
39034                     for (int xm = -mx1; xm<=mx2; ++xm) {
39035                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
39036                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval);
39037                       if (cval>max_val) max_val = cval;
39038                     }
39039                 res(x,y,z,c) = max_val;
39040               }
39041             } _cimg_abort_catch_openmp2
39042         } else { // Binary dilation
39043           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
39044           for (int z = mz1; z<mze; ++z)
39045             for (int y = my1; y<mye; ++y)
39046               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
39047                 cimg_abort_test2;
39048                 Tt max_val = cimg::type<Tt>::min();
39049                 for (int zm = -mz1; zm<=mz2; ++zm)
39050                   for (int ym = -my1; ym<=my2; ++ym)
39051                     for (int xm = -mx1; xm<=mx2; ++xm)
39052                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
39053                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
39054                         if (cval>max_val) max_val = cval;
39055                       }
39056                 res(x,y,z,c) = max_val;
39057               } _cimg_abort_catch_openmp2
39058           if (boundary_conditions)
39059             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39060             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39061               cimg_abort_test2;
39062               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39063                 Tt max_val = cimg::type<Tt>::min();
39064                 for (int zm = -mz1; zm<=mz2; ++zm)
39065                   for (int ym = -my1; ym<=my2; ++ym)
39066                     for (int xm = -mx1; xm<=mx2; ++xm)
39067                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
39068                         const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
39069                         if (cval>max_val) max_val = cval;
39070                       }
39071                 res(x,y,z,c) = max_val;
39072               }
39073             } _cimg_abort_catch_openmp2
39074           else
39075             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39076             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39077               cimg_abort_test2;
39078               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39079                 Tt max_val = cimg::type<Tt>::min();
39080                 for (int zm = -mz1; zm<=mz2; ++zm)
39081                   for (int ym = -my1; ym<=my2; ++ym)
39082                     for (int xm = -mx1; xm<=mx2; ++xm)
39083                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
39084                         const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
39085                         if (cval>max_val) max_val = cval;
39086                       }
39087                 res(x,y,z,c) = max_val;
39088               }
39089             } _cimg_abort_catch_openmp2
39090         }
39091       } _cimg_abort_catch_openmp
39092       cimg_abort_test;
39093       return res;
39094     }
39095 
39096     //! Dilate image by a rectangular structuring element of specified size.
39097     /**
39098        \param sx Width of the structuring element.
39099        \param sy Height of the structuring element.
39100        \param sz Depth of the structuring element.
39101     **/
39102     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
39103       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
39104       if (sx>1 && _width>1) { // Along X-axis
39105         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;
39106         CImg<T> buf(L);
39107         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39108         cimg_forYZC(*this,y,z,c) {
39109           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39110           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39111           T cur = *ptrs; ptrs+=off; bool is_first = true;
39112           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39113             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39114           }
39115           *(ptrd++) = cur;
39116           if (ptrs>=ptrse) {
39117             T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39118           } else {
39119             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39120               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39121               *(ptrd++) = cur;
39122             }
39123             for (int p = L - s - 1; p>0; --p) {
39124               const T val = *ptrs; ptrs+=off;
39125               if (is_first) {
39126                 const T *nptrs = ptrs - off; cur = val;
39127                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39128                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39129               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39130               *(ptrd++) = cur;
39131             }
39132             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39133             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39134               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39135             }
39136             *(ptrd--) = cur;
39137             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39138               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39139             }
39140             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39141           }
39142         }
39143       }
39144 
39145       if (sy>1 && _height>1) { // Along Y-axis
39146         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
39147           s2 = _s2>L?L:_s2;
39148         CImg<T> buf(L);
39149         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39150         cimg_forXZC(*this,x,z,c) {
39151           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39152           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39153           T cur = *ptrs; ptrs+=off; bool is_first = true;
39154           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39155             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39156           }
39157           *(ptrd++) = cur;
39158           if (ptrs>=ptrse) {
39159             T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39160           } else {
39161             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39162               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39163               *(ptrd++) = cur;
39164             }
39165             for (int p = L - s - 1; p>0; --p) {
39166               const T val = *ptrs; ptrs+=off;
39167               if (is_first) {
39168                 const T *nptrs = ptrs - off; cur = val;
39169                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39170                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39171               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39172               *(ptrd++) = cur;
39173             }
39174             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39175             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39176               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39177             }
39178             *(ptrd--) = cur;
39179             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39180               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39181             }
39182             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39183           }
39184         }
39185       }
39186 
39187       if (sz>1 && _depth>1) { // Along Z-axis
39188         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
39189           s2 = _s2>L?L:_s2;
39190         CImg<T> buf(L);
39191         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39192         cimg_forXYC(*this,x,y,c) {
39193           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39194           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39195           T cur = *ptrs; ptrs+=off; bool is_first = true;
39196           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39197             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39198           }
39199           *(ptrd++) = cur;
39200           if (ptrs>=ptrse) {
39201             T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39202           } else {
39203             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39204               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39205               *(ptrd++) = cur;
39206             }
39207             for (int p = L - s - 1; p>0; --p) {
39208               const T val = *ptrs; ptrs+=off;
39209               if (is_first) {
39210                 const T *nptrs = ptrs - off; cur = val;
39211                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39212                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39213               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39214               *(ptrd++) = cur;
39215             }
39216             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39217             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39218               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39219             }
39220             *(ptrd--) = cur;
39221             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39222               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39223             }
39224             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39225           }
39226         }
39227       }
39228       return *this;
39229     }
39230 
39231     //! Dilate image by a rectangular structuring element of specified size \newinstance.
39232     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
39233       return (+*this).dilate(sx,sy,sz);
39234     }
39235 
39236     //! Dilate image by a square structuring element of specified size.
39237     /**
39238        \param s Size of the structuring element.
39239     **/
39240     CImg<T>& dilate(const unsigned int s) {
39241       return dilate(s,s,s);
39242     }
39243 
39244     //! Dilate image by a square structuring element of specified size \newinstance.
39245     CImg<T> get_dilate(const unsigned int s) const {
39246       return (+*this).dilate(s);
39247     }
39248 
39249     //! Compute watershed transform.
39250     /**
39251        \param priority Priority map.
39252        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
39253        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
39254        \note Non-zero values of the instance instance are propagated to zero-valued ones according to
39255        specified the priority map.
39256     **/
39257     template<typename t>
39258     CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
39259 #define _cimg_watershed_init(cond,X,Y,Z) \
39260       if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
39261 
39262 #define _cimg_watershed_propagate(cond,X,Y,Z) \
39263       if (cond) { \
39264         if ((*this)(X,Y,Z)) { \
39265           ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
39266           d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
39267           if (d<dmin) { dmin = d; nmin = ns; nlabel = (*this)(xs,ys,zs); } \
39268         } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
39269       }
39270 
39271       if (is_empty()) return *this;
39272       if (!is_sameXYZ(priority))
39273         throw CImgArgumentException(_cimg_instance
39274                                     "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
39275                                     "have different dimensions.",
39276                                     cimg_instance,
39277                                     priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
39278       if (_spectrum!=1) {
39279         cimg_forC(*this,c)
39280           get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
39281         return *this;
39282       }
39283 
39284       CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
39285       CImg<typename cimg::superset2<T,t,int>::type> Q;
39286       unsigned int sizeQ = 0;
39287       int px, nx, py, ny, pz, nz;
39288       bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
39289       const bool is_3d = _depth>1;
39290 
39291       // Find seed points and insert them in priority queue.
39292       unsigned int nb_seeds = 0;
39293       const T *ptrs = _data;
39294       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version
39295         if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
39296         seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
39297         px = x - 1; nx = x + 1;
39298         py = y - 1; ny = y + 1;
39299         pz = z - 1; nz = z + 1;
39300         is_px = px>=0; is_nx = nx<width();
39301         is_py = py>=0; is_ny = ny<height();
39302         is_pz = pz>=0; is_nz = nz<depth();
39303         _cimg_watershed_init(is_px,px,y,z);
39304         _cimg_watershed_init(is_nx,nx,y,z);
39305         _cimg_watershed_init(is_py,x,py,z);
39306         _cimg_watershed_init(is_ny,x,ny,z);
39307         if (is_3d) {
39308           _cimg_watershed_init(is_pz,x,y,pz);
39309           _cimg_watershed_init(is_nz,x,y,nz);
39310         }
39311         if (is_high_connectivity) {
39312           _cimg_watershed_init(is_px && is_py,px,py,z);
39313           _cimg_watershed_init(is_nx && is_py,nx,py,z);
39314           _cimg_watershed_init(is_px && is_ny,px,ny,z);
39315           _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
39316           if (is_3d) {
39317             _cimg_watershed_init(is_px && is_pz,px,y,pz);
39318             _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
39319             _cimg_watershed_init(is_px && is_nz,px,y,nz);
39320             _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
39321             _cimg_watershed_init(is_py && is_pz,x,py,pz);
39322             _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
39323             _cimg_watershed_init(is_py && is_nz,x,py,nz);
39324             _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
39325             _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
39326             _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
39327             _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
39328             _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
39329             _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
39330             _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
39331             _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
39332             _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
39333           }
39334         }
39335         labels(x,y,z) = nb_seeds;
39336       }
39337 
39338       // Start watershed computation.
39339       while (sizeQ) {
39340 
39341         // Get and remove point with maximal priority from the queue.
39342         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
39343         const unsigned int n = labels(x,y,z);
39344         px = x - 1; nx = x + 1;
39345         py = y - 1; ny = y + 1;
39346         pz = z - 1; nz = z + 1;
39347         is_px = px>=0; is_nx = nx<width();
39348         is_py = py>=0; is_ny = ny<height();
39349         is_pz = pz>=0; is_nz = nz<depth();
39350 
39351         // Check labels of the neighbors.
39352         Q._priority_queue_remove(sizeQ);
39353 
39354         unsigned int xs, ys, zs, ns, nmin = 0;
39355         float d, dmin = cimg::type<float>::inf();
39356         T nlabel = (T)0;
39357         _cimg_watershed_propagate(is_px,px,y,z);
39358         _cimg_watershed_propagate(is_nx,nx,y,z);
39359         _cimg_watershed_propagate(is_py,x,py,z);
39360         _cimg_watershed_propagate(is_ny,x,ny,z);
39361         if (is_3d) {
39362           _cimg_watershed_propagate(is_pz,x,y,pz);
39363           _cimg_watershed_propagate(is_nz,x,y,nz);
39364         }
39365         if (is_high_connectivity) {
39366           _cimg_watershed_propagate(is_px && is_py,px,py,z);
39367           _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
39368           _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
39369           _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
39370           if (is_3d) {
39371             _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
39372             _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
39373             _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
39374             _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
39375             _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
39376             _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
39377             _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
39378             _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
39379             _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
39380             _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
39381             _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
39382             _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
39383             _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
39384             _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
39385             _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
39386             _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
39387           }
39388         }
39389         (*this)(x,y,z) = nlabel;
39390         labels(x,y,z) = ++nmin;
39391       }
39392       return *this;
39393     }
39394 
39395     //! Compute watershed transform \newinstance.
39396     template<typename t>
39397     CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
39398       return (+*this).watershed(priority,is_high_connectivity);
39399     }
39400 
39401     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
39402     template<typename tq, typename tv>
39403     bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
39404                                 const unsigned int x, const unsigned int y, const unsigned int z,
39405                                 const unsigned int n=1) {
39406       if (is_queued(x,y,z)) return false;
39407       is_queued(x,y,z) = (tq)n;
39408       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
39409       (*this)(siz - 1,0) = (T)value;
39410       (*this)(siz - 1,1) = (T)x;
39411       (*this)(siz - 1,2) = (T)y;
39412       (*this)(siz - 1,3) = (T)z;
39413       for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
39414         cimg::swap((*this)(pos,0),(*this)(par,0));
39415         cimg::swap((*this)(pos,1),(*this)(par,1));
39416         cimg::swap((*this)(pos,2),(*this)(par,2));
39417         cimg::swap((*this)(pos,3),(*this)(par,3));
39418       }
39419       return true;
39420     }
39421 
39422     CImg<T>& _priority_queue_remove(unsigned int& siz) {
39423       (*this)(0,0) = (*this)(--siz,0);
39424       (*this)(0,1) = (*this)(siz,1);
39425       (*this)(0,2) = (*this)(siz,2);
39426       (*this)(0,3) = (*this)(siz,3);
39427       const float value = (*this)(0,0);
39428       unsigned int pos = 0, swap = 0;
39429       do {
39430         const unsigned int left = 2*pos + 1, right = left + 1;
39431         if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
39432         else if (left<siz && value<(*this)(left,0)) swap = left;
39433         else break;
39434         cimg::swap((*this)(pos,0),(*this)(swap,0));
39435         cimg::swap((*this)(pos,1),(*this)(swap,1));
39436         cimg::swap((*this)(pos,2),(*this)(swap,2));
39437         cimg::swap((*this)(pos,3),(*this)(swap,3));
39438         pos = swap;
39439       } while (true);
39440       return *this;
39441     }
39442 
39443     //! Apply recursive Deriche filter.
39444     /**
39445        \param sigma Standard deviation of the filter.
39446        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
39447        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
39448        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39449     **/
39450     CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
39451                      const bool boundary_conditions=true) {
39452 #define _cimg_deriche_apply \
39453   CImg<doubleT> Y(N); \
39454   double *ptrY = Y._data, yb = 0, yp = 0; \
39455   T xp = (T)0; \
39456   if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \
39457   for (int m = 0; m<N; ++m) { \
39458     const T xc = *ptrX; ptrX+=off; \
39459     const double yc = *(ptrY++) = (double)(a0*xc + a1*xp - b1*yp - b2*yb); \
39460     xp = xc; yb = yp; yp = yc; \
39461   } \
39462   T xn = (T)0, xa = (T)0; \
39463   double yn = 0, ya = 0; \
39464   if (boundary_conditions) { xn = xa = *(ptrX - off); yn = ya = (double)coefn*xn; } \
39465   for (int n = N - 1; n>=0; --n) { \
39466     const T xc = *(ptrX-=off); \
39467     const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \
39468     xa = xn; xn = xc; ya = yn; yn = yc; \
39469     *ptrX = (T)(*(--ptrY)+yc); \
39470   }
39471       const char naxis = cimg::lowercase(axis);
39472       const double nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
39473                                                    naxis=='y'?_height:
39474                                                    naxis=='z'?_depth:_spectrum)/100;
39475       if (is_empty() || (nsigma<0.1f && !order)) return *this;
39476       const double
39477         nnsigma = nsigma<0.1f?0.1f:nsigma,
39478         alpha = 1.695f/nnsigma,
39479         ema = std::exp(-alpha),
39480         ema2 = std::exp(-2*alpha),
39481         b1 = -2*ema,
39482         b2 = ema2;
39483       double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
39484       switch (order) {
39485       case 0 : {
39486         const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
39487         a0 = k;
39488         a1 = k*(alpha - 1)*ema;
39489         a2 = k*(alpha + 1)*ema;
39490         a3 = -k*ema2;
39491       } break;
39492       case 1 : {
39493         const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
39494         a0 = a3 = 0;
39495         a1 = k*ema;
39496         a2 = -a1;
39497       } break;
39498       case 2 : {
39499         const double
39500           ea = std::exp(-alpha),
39501           k = -(ema2 - 1)/(2*alpha*ema),
39502           kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
39503         a0 = kn;
39504         a1 = -kn*(1 + k*alpha)*ema;
39505         a2 = kn*(1 - k*alpha)*ema;
39506         a3 = -kn*ema2;
39507       } break;
39508       default :
39509         throw CImgArgumentException(_cimg_instance
39510                                     "deriche(): Invalid specified filter order %u "
39511                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
39512                                     cimg_instance,
39513                                     order);
39514       }
39515       coefp = (a0 + a1)/(1 + b1 + b2);
39516       coefn = (a2 + a3)/(1 + b1 + b2);
39517       switch (naxis) {
39518       case 'x' : {
39519         const int N = width();
39520         const ulongT off = 1U;
39521         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39522                                                                    _height*_depth*_spectrum>=16))
39523         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
39524       } break;
39525       case 'y' : {
39526         const int N = height();
39527         const ulongT off = (ulongT)_width;
39528         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39529                                                                    _height*_depth*_spectrum>=16))
39530         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
39531       } break;
39532       case 'z' : {
39533         const int N = depth();
39534         const ulongT off = (ulongT)_width*_height;
39535         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39536                                                                    _height*_depth*_spectrum>=16))
39537         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
39538       } break;
39539       default : {
39540         const int N = spectrum();
39541         const ulongT off = (ulongT)_width*_height*_depth;
39542         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39543                                                                    _height*_depth*_spectrum>=16))
39544         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
39545       }
39546       }
39547       return *this;
39548     }
39549 
39550     //! Apply recursive Deriche filter \newinstance.
39551     CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
39552                              const bool boundary_conditions=true) const {
39553       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
39554     }
39555 
39556     // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
39557     /*
39558        \param ptr the pointer of the data
39559        \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
39560        \param N size of the data
39561        \param off the offset between two data point
39562        \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative
39563        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39564        \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
39565     */
39566     static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
39567                                       const unsigned int order, const bool boundary_conditions) {
39568       double val[4] = { 0 };  // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
39569       const double
39570         sumsq = filter[0], sum = sumsq * sumsq,
39571         a1 = filter[1], a2 = filter[2], a3 = filter[3],
39572         scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) );
39573       double M[9]; // Triggs matrix
39574       M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2);
39575       M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
39576       M[2] = scaleM * a3 * (a1 + a3 * a2);
39577       M[3] = scaleM * (a1 + a3 * a2);
39578       M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1);
39579       M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.);
39580       M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
39581       M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
39582       M[8] = scaleM * a3 * (a1 + a3 * a2);
39583       switch (order) {
39584       case 0 : {
39585         const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
39586         for (int pass = 0; pass<2; ++pass) {
39587           if (!pass) {
39588             for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
39589           } else {
39590             // Apply Triggs boundary conditions
39591             const double
39592               uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3),
39593               unp  = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
39594             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
39595             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
39596             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
39597             *data = (T)val[0];
39598             data -= off;
39599             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39600           }
39601           for (int n = pass; n<N; ++n) {
39602             val[0] = (*data);
39603             if (pass) val[0] *= sum;
39604             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39605             *data = (T)val[0];
39606             if (!pass) data += off; else data -= off;
39607             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39608           }
39609           if (!pass) data -= off;
39610         }
39611       } break;
39612       case 1 : {
39613         double x[3]; // [front,center,back]
39614         for (int pass = 0; pass<2; ++pass) {
39615           if (!pass) {
39616             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39617             for (int k = 0; k<4; ++k) val[k] = 0;
39618           } else {
39619             // Apply Triggs boundary conditions
39620             const double
39621               unp  = val[1], unp1 = val[2], unp2 = val[3];
39622             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39623             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39624             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39625             *data = (T)val[0];
39626             data -= off;
39627             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39628           }
39629           for (int n = pass; n<N - 1; ++n) {
39630             if (!pass) {
39631               x[0] = *(data + off);
39632               val[0] = 0.5f * (x[0] - x[2]);
39633             } else val[0] = (*data) * sum;
39634             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39635             *data = (T)val[0];
39636             if (!pass) {
39637               data += off;
39638               for (int k = 2; k>0; --k) x[k] = x[k - 1];
39639             } else { data-=off;}
39640             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39641           }
39642           *data = (T)0;
39643         }
39644       } break;
39645       case 2: {
39646         double x[3]; // [front,center,back]
39647         for (int pass = 0; pass<2; ++pass) {
39648           if (!pass) {
39649             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39650             for (int k = 0; k<4; ++k) val[k] = 0;
39651           } else {
39652             // Apply Triggs boundary conditions
39653             const double
39654               unp  = val[1], unp1 = val[2], unp2 = val[3];
39655             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39656             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39657             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39658             *data = (T)val[0];
39659             data -= off;
39660             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39661           }
39662           for (int n = pass; n<N - 1; ++n) {
39663             if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
39664             else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
39665             for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
39666             *data = (T)val[0];
39667             if (!pass) data += off; else data -= off;
39668             for (int k = 2; k>0; --k) x[k] = x[k - 1];
39669             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39670           }
39671           *data = (T)0;
39672         }
39673       } break;
39674       case 3: {
39675         double x[3]; // [front,center,back]
39676         for (int pass = 0; pass<2; ++pass) {
39677           if (!pass) {
39678             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39679             for (int k = 0; k<4; ++k) val[k] = 0;
39680           } else {
39681             // Apply Triggs boundary conditions
39682             const double
39683               unp = val[1], unp1 = val[2], unp2 = val[3];
39684             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39685             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39686             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39687             *data = (T)val[0];
39688             data -= off;
39689             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39690           }
39691           for (int n = pass; n<N - 1; ++n) {
39692             if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
39693             else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
39694             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39695             *data = (T)val[0];
39696             if (!pass) data += off; else data -= off;
39697             for (int k = 2; k>0; --k) x[k] = x[k - 1];
39698             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39699           }
39700           *data = (T)0;
39701         }
39702       } break;
39703       }
39704     }
39705 
39706     //! Van Vliet recursive Gaussian filter.
39707     /**
39708        \param sigma standard deviation of the Gaussian filter
39709        \param order the order of the filter 0,1,2,3
39710        \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
39711        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39712        \note dirichlet boundary condition has a strange behavior
39713 
39714        I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
39715        IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
39716 
39717        (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
39718 
39719        Boundary conditions (only for order 0) using Triggs matrix, from
39720        B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
39721        recursive filtering. IEEE Trans. Signal Processing,
39722        vol. 54, pp. 2365-2367, 2006.
39723     **/
39724     CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
39725                       const bool boundary_conditions=true) {
39726       if (is_empty()) return *this;
39727       if (!cimg::type<T>::is_float())
39728         return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
39729       const char naxis = cimg::lowercase(axis);
39730       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
39731       if (is_empty() || (nsigma<0.5f && !order)) return *this;
39732       const double
39733         nnsigma = nsigma<0.5f?0.5f:nsigma,
39734         m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
39735         m1sq = m1 * m1, m2sq = m2 * m2,
39736         q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
39737         qsq = q * q,
39738         scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
39739         b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
39740         b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
39741         b3 = -qsq * q / scale,
39742         B = ( m0 * (m1sq + m2sq) ) / scale;
39743       double filter[4];
39744       filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
39745       switch (naxis) {
39746       case 'x' : {
39747         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39748                                                                    _height*_depth*_spectrum>=16))
39749         cimg_forYZC(*this,y,z,c)
39750           _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
39751       } break;
39752       case 'y' : {
39753         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39754                                                                    _height*_depth*_spectrum>=16))
39755         cimg_forXZC(*this,x,z,c)
39756           _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
39757       } break;
39758       case 'z' : {
39759         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39760                                                                    _height*_depth*_spectrum>=16))
39761         cimg_forXYC(*this,x,y,c)
39762           _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
39763                                 order,boundary_conditions);
39764       } break;
39765       default : {
39766         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39767                                                                    _height*_depth*_spectrum>=16))
39768         cimg_forXYZ(*this,x,y,z)
39769           _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
39770                                 order,boundary_conditions);
39771       }
39772       }
39773       return *this;
39774     }
39775 
39776     //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
39777     CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
39778                               const bool boundary_conditions=true) const {
39779       return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
39780     }
39781 
39782     //! Blur image.
39783     /**
39784        \param sigma_x Standard deviation of the blur, along the X-axis.
39785        \param sigma_y Standard deviation of the blur, along the Y-axis.
39786        \param sigma_z Standard deviation of the blur, along the Z-axis.
39787        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
39788        \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
39789        \note
39790        - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian).
39791        - This is a recursive algorithm, not depending on the values of the standard deviations.
39792        \see deriche(), vanvliet().
39793     **/
39794     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
39795                   const bool boundary_conditions=true, const bool is_gaussian=true) {
39796       if (is_empty()) return *this;
39797       if (is_gaussian) {
39798         if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
39799         if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
39800         if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
39801       } else {
39802         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
39803         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
39804         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
39805       }
39806       return *this;
39807     }
39808 
39809     //! Blur image \newinstance.
39810     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
39811                           const bool boundary_conditions=true, const bool is_gaussian=true) const {
39812       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
39813     }
39814 
39815     //! Blur image isotropically.
39816     /**
39817        \param sigma Standard deviation of the blur.
39818        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
39819        \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise.
39820        \see deriche(), vanvliet().
39821     **/
39822     CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) {
39823       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
39824       return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
39825     }
39826 
39827     //! Blur image isotropically \newinstance.
39828     CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const {
39829       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
39830     }
39831 
39832     //! Blur image anisotropically, directed by a field of diffusion tensors.
39833     /**
39834        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
39835        \param amplitude Amplitude of the smoothing.
39836        \param dl Spatial discretization.
39837        \param da Angular discretization.
39838        \param gauss_prec Precision of the diffusion process.
39839        \param interpolation_type Interpolation scheme.
39840          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
39841        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
39842     **/
39843     template<typename t>
39844     CImg<T>& blur_anisotropic(const CImg<t>& G,
39845                               const float amplitude=60, const float dl=0.8f, const float da=30,
39846                               const float gauss_prec=2, const unsigned int interpolation_type=0,
39847                               const bool is_fast_approx=1) {
39848 
39849       // Check arguments and init variables
39850       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
39851         throw CImgArgumentException(_cimg_instance
39852                                     "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
39853                                     cimg_instance,
39854                                     G._width,G._height,G._depth,G._spectrum,G._data);
39855       if (is_empty() || dl<0) return *this;
39856       const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
39857       unsigned int iamplitude = cimg::round(namplitude);
39858       const bool is_3d = (G._spectrum==6);
39859       T val_min, val_max = max_min(val_min);
39860       _cimg_abort_init_openmp;
39861       cimg_abort_init;
39862 
39863       if (da<=0) {  // Iterated oriented Laplacians
39864         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
39865         for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
39866           Tfloat *ptrd = velocity._data, veloc_max = 0;
39867           if (is_3d) // 3D version
39868             cimg_forC(*this,c) {
39869               cimg_abort_test;
39870               CImg_3x3x3(I,Tfloat);
39871               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
39872                 const Tfloat
39873                   ixx = Incc + Ipcc - 2*Iccc,
39874                   ixy = (Innc + Ippc - Inpc - Ipnc)/4,
39875                   ixz = (Incn + Ipcp - Incp - Ipcn)/4,
39876                   iyy = Icnc + Icpc - 2*Iccc,
39877                   iyz = (Icnn + Icpp - Icnp - Icpn)/4,
39878                   izz = Iccn + Iccp - 2*Iccc,
39879                   veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
39880                                    G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
39881                 *(ptrd++) = veloc;
39882                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
39883               }
39884             }
39885           else // 2D version
39886             cimg_forZC(*this,z,c) {
39887               cimg_abort_test;
39888               CImg_3x3(I,Tfloat);
39889               cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
39890                 const Tfloat
39891                   ixx = Inc + Ipc - 2*Icc,
39892                   ixy = (Inn + Ipp - Inp - Ipn)/4,
39893                   iyy = Icn + Icp - 2*Icc,
39894                   veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
39895                 *(ptrd++) = veloc;
39896                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
39897               }
39898             }
39899           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
39900         }
39901       } else { // LIC-based smoothing
39902         const ulongT whd = (ulongT)_width*_height*_depth;
39903         const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
39904         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
39905         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
39906         int N = 0;
39907         if (is_3d) { // 3D version
39908           for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
39909             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
39910               da2 = datmp<1?360.f:datmp;
39911             for (float theta = 0; theta<360; (theta+=da2),++N) {
39912               const float
39913                 thetar = (float)(theta*cimg::PI/180),
39914                 vx = (float)(std::cos(thetar)*std::cos(phir)),
39915                 vy = (float)(std::sin(thetar)*std::cos(phir)),
39916                 vz = (float)std::sin(phir);
39917               const t
39918                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
39919                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
39920               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);
39921               cimg_forXYZ(G,xg,yg,zg) {
39922                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
39923                 const float
39924                   u = (float)(a*vx + b*vy + c*vz),
39925                   v = (float)(b*vx + d*vy + e*vz),
39926                   w = (float)(c*vx + e*vy + f*vz),
39927                   n = 1e-5f + cimg::hypot(u,v,w),
39928                   dln = dl/n;
39929                 *(pd0++) = (Tfloat)(u*dln);
39930                 *(pd1++) = (Tfloat)(v*dln);
39931                 *(pd2++) = (Tfloat)(w*dln);
39932                 *(pd3++) = (Tfloat)n;
39933               }
39934 
39935               cimg_abort_test;
39936               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
39937                                  cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
39938                                  firstprivate(val))
39939               cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
39940                 cimg_abort_test2;
39941                 cimg_forX(*this,x) {
39942                   val.fill(0);
39943                   const float
39944                     n = (float)W(x,y,z,3),
39945                     fsigma = (float)(n*sqrt2amplitude),
39946                     fsigma2 = 2*fsigma*fsigma,
39947                     length = gauss_prec*fsigma;
39948                   float
39949                     S = 0,
39950                     X = (float)x,
39951                     Y = (float)y,
39952                     Z = (float)z;
39953                   switch (interpolation_type) {
39954                   case 0 : { // Nearest neighbor
39955                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39956                       const int
39957                         cx = (int)(X + 0.5f),
39958                         cy = (int)(Y + 0.5f),
39959                         cz = (int)(Z + 0.5f);
39960                       const float
39961                         u = (float)W(cx,cy,cz,0),
39962                         v = (float)W(cx,cy,cz,1),
39963                         w = (float)W(cx,cy,cz,2);
39964                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
39965                       else {
39966                         const float coef = (float)std::exp(-l*l/fsigma2);
39967                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
39968                         S+=coef;
39969                       }
39970                       X+=u; Y+=v; Z+=w;
39971                     }
39972                   } break;
39973                   case 1 : { // Linear interpolation
39974                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39975                       const float
39976                         u = (float)(W._linear_atXYZ(X,Y,Z,0)),
39977                         v = (float)(W._linear_atXYZ(X,Y,Z,1)),
39978                         w = (float)(W._linear_atXYZ(X,Y,Z,2));
39979                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
39980                       else {
39981                         const float coef = (float)std::exp(-l*l/fsigma2);
39982                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
39983                         S+=coef;
39984                       }
39985                       X+=u; Y+=v; Z+=w;
39986                     }
39987                   } break;
39988                   default : { // 2nd order Runge Kutta
39989                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39990                       const float
39991                         u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
39992                         v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
39993                         w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
39994                         u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
39995                         v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
39996                         w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
39997                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
39998                       else {
39999                         const float coef = (float)std::exp(-l*l/fsigma2);
40000                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
40001                         S+=coef;
40002                       }
40003                       X+=u; Y+=v; Z+=w;
40004                     }
40005                   } break;
40006                   }
40007                   Tfloat *ptrd = res.data(x,y,z);
40008                   if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
40009                   else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
40010                 }
40011               } _cimg_abort_catch_openmp2
40012             }
40013           }
40014         } else { // 2D LIC algorithm
40015           for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
40016             const float thetar = (float)(theta*cimg::PI/180),
40017               vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
40018             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
40019             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
40020             cimg_forXY(G,xg,yg) {
40021               const t a = *(pa++), b = *(pb++), c = *(pc++);
40022               const float
40023                 u = (float)(a*vx + b*vy),
40024                 v = (float)(b*vx + c*vy),
40025                 n = std::max(1e-5f,cimg::hypot(u,v)),
40026                 dln = dl/n;
40027               *(pd0++) = (Tfloat)(u*dln);
40028               *(pd1++) = (Tfloat)(v*dln);
40029               *(pd2++) = (Tfloat)n;
40030             }
40031 
40032             cimg_abort_test;
40033             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
40034                                firstprivate(val))
40035             cimg_forY(*this,y) _cimg_abort_try_openmp2 {
40036               cimg_abort_test2;
40037               cimg_forX(*this,x) {
40038                 val.fill(0);
40039                 const float
40040                   n = (float)W(x,y,0,2),
40041                   fsigma = (float)(n*sqrt2amplitude),
40042                   fsigma2 = 2*fsigma*fsigma,
40043                   length = gauss_prec*fsigma;
40044                 float
40045                   S = 0,
40046                   X = (float)x,
40047                   Y = (float)y;
40048                 switch (interpolation_type) {
40049                 case 0 : { // Nearest-neighbor
40050                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40051                     const int
40052                       cx = (int)(X + 0.5f),
40053                       cy = (int)(Y + 0.5f);
40054                     const float
40055                       u = (float)W(cx,cy,0,0),
40056                       v = (float)W(cx,cy,0,1);
40057                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
40058                     else {
40059                       const float coef = (float)std::exp(-l*l/fsigma2);
40060                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
40061                       S+=coef;
40062                     }
40063                     X+=u; Y+=v;
40064                   }
40065                 } break;
40066                 case 1 : { // Linear interpolation
40067                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40068                     const float
40069                       u = (float)(W._linear_atXY(X,Y,0,0)),
40070                       v = (float)(W._linear_atXY(X,Y,0,1));
40071                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
40072                     else {
40073                       const float coef = (float)std::exp(-l*l/fsigma2);
40074                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
40075                       S+=coef;
40076                     }
40077                     X+=u; Y+=v;
40078                   }
40079                 } break;
40080                 default : { // 2nd-order Runge-kutta interpolation
40081                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40082                     const float
40083                       u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
40084                       v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
40085                       u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
40086                       v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
40087                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
40088                     else {
40089                       const float coef = (float)std::exp(-l*l/fsigma2);
40090                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
40091                       S+=coef;
40092                     }
40093                     X+=u; Y+=v;
40094                   }
40095                 }
40096                 }
40097                 Tfloat *ptrd = res.data(x,y);
40098                 if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
40099                 else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
40100               }
40101             } _cimg_abort_catch_openmp2
40102           }
40103         }
40104         const Tfloat *ptrs = res._data;
40105         cimg_for(*this,ptrd,T) {
40106           const Tfloat _val = *(ptrs++)/N;
40107           *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
40108         }
40109       }
40110       cimg_abort_test;
40111       return *this;
40112     }
40113 
40114     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
40115     template<typename t>
40116     CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
40117                                       const float amplitude=60, const float dl=0.8f, const float da=30,
40118                                       const float gauss_prec=2, const unsigned int interpolation_type=0,
40119                                       const bool is_fast_approx=true) const {
40120       return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
40121     }
40122 
40123     //! Blur image anisotropically, in an edge-preserving way.
40124     /**
40125        \param amplitude Amplitude of the smoothing.
40126        \param sharpness Sharpness.
40127        \param anisotropy Anisotropy.
40128        \param alpha Standard deviation of the gradient blur.
40129        \param sigma Standard deviation of the structure tensor blur.
40130        \param dl Spatial discretization.
40131        \param da Angular discretization.
40132        \param gauss_prec Precision of the diffusion process.
40133        \param interpolation_type Interpolation scheme.
40134          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
40135        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
40136      **/
40137     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
40138                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
40139                               const float gauss_prec=2, const unsigned int interpolation_type=0,
40140                               const bool is_fast_approx=true) {
40141       const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
40142       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
40143       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
40144                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
40145     }
40146 
40147     //! Blur image anisotropically, in an edge-preserving way \newinstance.
40148     CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
40149                                       const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
40150                                       const float da=30, const float gauss_prec=2,
40151                                       const unsigned int interpolation_type=0,
40152                                       const bool is_fast_approx=true) const {
40153       return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
40154                                                         interpolation_type,is_fast_approx);
40155     }
40156 
40157     //! Blur image, with the joint bilateral filter.
40158     /**
40159        \param guide Image used to model the smoothing weights.
40160        \param sigma_x Amount of blur along the X-axis.
40161        \param sigma_y Amount of blur along the Y-axis.
40162        \param sigma_z Amount of blur along the Z-axis.
40163        \param sigma_r Amount of blur along the value axis.
40164        \param sampling_x Amount of downsampling along the X-axis used for the approximation.
40165          Defaults (0) to sigma_x.
40166        \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
40167          Defaults (0) to sigma_y.
40168        \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
40169          Defaults (0) to sigma_z.
40170        \param sampling_r Amount of downsampling along the value axis used for the approximation.
40171          Defaults (0) to sigma_r.
40172        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
40173        (extended for 3D volumetric images).
40174        It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
40175     **/
40176     template<typename t>
40177     CImg<T>& blur_bilateral(const CImg<t>& guide,
40178                             const float sigma_x, const float sigma_y,
40179                             const float sigma_z, const float sigma_r,
40180                             const float sampling_x, const float sampling_y,
40181                             const float sampling_z, const float sampling_r) {
40182       if (!is_sameXYZ(guide))
40183         throw CImgArgumentException(_cimg_instance
40184                                     "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
40185                                     cimg_instance,
40186                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
40187       if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
40188       T edge_min, edge_max = guide.max_min(edge_min);
40189       if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
40190       const float
40191         edge_delta = (float)(edge_max - edge_min),
40192         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
40193         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
40194         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
40195         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100,
40196         _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f),
40197         _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f),
40198         _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f),
40199         _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
40200         derived_sigma_x = _sigma_x / _sampling_x,
40201         derived_sigma_y = _sigma_y / _sampling_y,
40202         derived_sigma_z = _sigma_z / _sampling_z,
40203         derived_sigma_r = _sigma_r / _sampling_r;
40204       const int
40205         padding_x = (int)(2*derived_sigma_x) + 1,
40206         padding_y = (int)(2*derived_sigma_y) + 1,
40207         padding_z = (int)(2*derived_sigma_z) + 1,
40208         padding_r = (int)(2*derived_sigma_r) + 1;
40209       const unsigned int
40210         bx = (unsigned int)((_width  - 1)/_sampling_x + 1 + 2*padding_x),
40211         by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
40212         bz = (unsigned int)((_depth  - 1)/_sampling_z + 1 + 2*padding_z),
40213         br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
40214       if (bx>0 || by>0 || bz>0 || br>0) {
40215         const bool is_3d = (_depth>1);
40216         if (is_3d) { // 3D version of the algorithm
40217           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
40218           cimg_forC(*this,c) {
40219             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
40220             bgrid.fill(0); bgridw.fill(0);
40221             cimg_forXYZ(*this,x,y,z) {
40222               const T val = (*this)(x,y,z,c);
40223               const float edge = (float)_guide(x,y,z);
40224               const int
40225                 X = (int)cimg::round(x/_sampling_x) + padding_x,
40226                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
40227                 Z = (int)cimg::round(z/_sampling_z) + padding_z,
40228                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
40229               bgrid(X,Y,Z,R)+=(float)val;
40230               bgridw(X,Y,Z,R)+=1;
40231             }
40232             bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
40233             bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
40234 
40235             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096))
40236             cimg_forXYZ(*this,x,y,z) {
40237               const float edge = (float)_guide(x,y,z);
40238               const float
40239                 X = x/_sampling_x + padding_x,
40240                 Y = y/_sampling_y + padding_y,
40241                 Z = z/_sampling_z + padding_z,
40242                 R = (edge - edge_min)/_sampling_r + padding_r;
40243               const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
40244               (*this)(x,y,z,c) = (T)(bval0/bval1);
40245             }
40246           }
40247         } else { // 2D version of the algorithm
40248           CImg<floatT> bgrid(bx,by,br,2);
40249           cimg_forC(*this,c) {
40250             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
40251             bgrid.fill(0);
40252             cimg_forXY(*this,x,y) {
40253               const T val = (*this)(x,y,c);
40254               const float edge = (float)_guide(x,y);
40255               const int
40256                 X = (int)cimg::round(x/_sampling_x) + padding_x,
40257                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
40258                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
40259               bgrid(X,Y,R,0)+=(float)val;
40260               bgrid(X,Y,R,1)+=1;
40261             }
40262             bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
40263 
40264             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096))
40265             cimg_forXY(*this,x,y) {
40266               const float edge = (float)_guide(x,y);
40267               const float
40268                 X = x/_sampling_x + padding_x,
40269                 Y = y/_sampling_y + padding_y,
40270                 R = (edge - edge_min)/_sampling_r + padding_r;
40271               const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
40272               (*this)(x,y,c) = (T)(bval0/bval1);
40273             }
40274           }
40275         }
40276       }
40277       return *this;
40278     }
40279 
40280     //! Blur image, with the joint bilateral filter \newinstance.
40281     template<typename t>
40282     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
40283                                     const float sigma_x, const float sigma_y,
40284                                     const float sigma_z, const float sigma_r,
40285                                     const float sampling_x, const float sampling_y,
40286                                     const float sampling_z, const float sampling_r) const {
40287       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
40288                                                       sampling_x,sampling_y,sampling_z,sampling_r);
40289     }
40290 
40291     //! Blur image using the joint bilateral filter.
40292     /**
40293        \param guide Image used to model the smoothing weights.
40294        \param sigma_s Amount of blur along the XYZ-axes.
40295        \param sigma_r Amount of blur along the value axis.
40296        \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
40297        \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
40298     **/
40299     template<typename t>
40300     CImg<T>& blur_bilateral(const CImg<t>& guide,
40301                             const float sigma_s, const float sigma_r,
40302                             const float sampling_s=0, const float sampling_r=0) {
40303       const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
40304       return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
40305     }
40306 
40307     //! Blur image using the bilateral filter \newinstance.
40308     template<typename t>
40309     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
40310                                     const float sigma_s, const float sigma_r,
40311                                     const float sampling_s=0, const float sampling_r=0) const {
40312       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
40313     }
40314 
40315     // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
40316     /*
40317       \param ptr the pointer of the data
40318       \param N size of the data
40319       \param boxsize Size of the box filter (can be subpixel).
40320       \param off the offset between two data point
40321       \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative.
40322       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
40323     */
40324     static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
40325                                      const int order, const bool boundary_conditions,
40326                                      const unsigned int nb_iter) {
40327       // Smooth.
40328       if (boxsize>1 && nb_iter) {
40329         const int w2 = (int)(boxsize - 1)/2;
40330         const unsigned int winsize = 2*w2 + 1U;
40331         const double frac = (boxsize - winsize)/2.;
40332         CImg<T> win(winsize);
40333         for (unsigned int iter = 0; iter<nb_iter; ++iter) {
40334           Tdouble sum = 0; // window sum
40335           for (int x = -w2; x<=w2; ++x) {
40336             win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x);
40337             sum+=win[x + w2];
40338           }
40339           int ifirst = 0, ilast = 2*w2;
40340           T
40341             prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1),
40342             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1);
40343           for (int x = 0; x < N - 1; ++x) {
40344             const double sum2 = sum + frac * (prev + next);
40345             ptr[x*off] = (T)(sum2/boxsize);
40346             prev = win[ifirst];
40347             sum-=prev;
40348             ifirst = (int)((ifirst + 1)%winsize);
40349             ilast = (int)((ilast + 1)%winsize);
40350             win[ilast] = next;
40351             sum+=next;
40352             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2);
40353           }
40354           const double sum2 = sum + frac * (prev + next);
40355           ptr[(N - 1)*off] = (T)(sum2/boxsize);
40356         }
40357       }
40358 
40359       // Derive.
40360       switch (order) {
40361       case 0 :
40362         break;
40363       case 1 : {
40364         Tfloat
40365           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
40366           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
40367           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
40368         for (int x = 0; x<N - 1; ++x) {
40369           ptr[x*off] = (T)((n-p)/2.);
40370           p = c;
40371           c = n;
40372           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
40373         }
40374         ptr[(N - 1)*off] = (T)((n-p)/2.);
40375       } break;
40376       case 2: {
40377         Tfloat
40378           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
40379           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
40380           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
40381         for (int x = 0; x<N - 1; ++x) {
40382           ptr[x*off] = (T)(n - 2*c + p);
40383           p = c;
40384           c = n;
40385           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
40386         }
40387         ptr[(N - 1)*off] = (T)(n - 2*c + p);
40388       } break;
40389       }
40390     }
40391 
40392     static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
40393                                    const bool boundary_conditions, const int x) {
40394       if (x<0) return boundary_conditions?ptr[0]:T();
40395       if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T();
40396       return ptr[x*off];
40397     }
40398 
40399     // Apply box filter of order 0,1,2.
40400     /**
40401       \param boxsize Size of the box window (can be subpixel)
40402       \param order the order of the filter 0,1 or 2.
40403       \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
40404       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
40405       \param nb_iter Number of filter iterations.
40406     **/
40407     CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
40408                        const bool boundary_conditions=true,
40409                        const unsigned int nb_iter=1) {
40410       if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this;
40411       const char naxis = cimg::lowercase(axis);
40412       const float nboxsize = boxsize>=0?boxsize:-boxsize*
40413         (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
40414       switch (naxis) {
40415       case 'x' : {
40416         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40417                                                                    _height*_depth*_spectrum>=16))
40418         cimg_forYZC(*this,y,z,c)
40419           _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
40420       } break;
40421       case 'y' : {
40422         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40423                                                                    _height*_depth*_spectrum>=16))
40424         cimg_forXZC(*this,x,z,c)
40425           _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
40426       } break;
40427       case 'z' : {
40428         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40429                                                                    _height*_depth*_spectrum>=16))
40430         cimg_forXYC(*this,x,y,c)
40431           _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
40432       } break;
40433       default : {
40434         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40435                                                                    _height*_depth*_spectrum>=16))
40436         cimg_forXYZ(*this,x,y,z)
40437           _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
40438                                order,boundary_conditions,nb_iter);
40439       }
40440       }
40441       return *this;
40442     }
40443 
40444     // Apply box filter of order 0,1 or 2 \newinstance.
40445     CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
40446                                const bool boundary_conditions=true,
40447                                const unsigned int nb_iter=1) const {
40448       return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
40449     }
40450 
40451     //! Blur image with a box filter.
40452     /**
40453        \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
40454        \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
40455        \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
40456        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
40457        \param nb_iter Number of filter iterations.
40458        \note
40459        - This is a recursive algorithm, not depending on the values of the box kernel size.
40460        \see blur().
40461     **/
40462     CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
40463                       const bool boundary_conditions=true,
40464                       const unsigned int nb_iter=1) {
40465       if (is_empty()) return *this;
40466       if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
40467       if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
40468       if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
40469       return *this;
40470     }
40471 
40472     //! Blur image with a box filter \newinstance.
40473     CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
40474                               const bool boundary_conditions=true) const {
40475       return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
40476     }
40477 
40478     //! Blur image with a box filter.
40479     /**
40480        \param boxsize Size of the box window (can be subpixel).
40481        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
40482        \see deriche(), vanvliet().
40483     **/
40484     CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) {
40485       const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
40486       return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
40487     }
40488 
40489     //! Blur image with a box filter \newinstance.
40490     CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const {
40491       return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
40492     }
40493 
40494     //! Blur image, with the image guided filter.
40495     /**
40496        \param guide Image used to guide the smoothing process.
40497        \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
40498        \param regularization Regularization parameter.
40499                              If negative, it is expressed as a percentage of the guide value range.
40500        \note This method implements the filtering algorithm described in:
40501        He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
40502        IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
40503     **/
40504     template<typename t>
40505     CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
40506       return get_blur_guided(guide,radius,regularization).move_to(*this);
40507     }
40508 
40509     //! Blur image, with the image guided filter \newinstance.
40510     template<typename t>
40511     CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
40512       if (!is_sameXYZ(guide))
40513         throw CImgArgumentException(_cimg_instance
40514                                     "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
40515                                     cimg_instance,
40516                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
40517       if (is_empty() || !radius) return *this;
40518       const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
40519       float _regularization = regularization;
40520       if (regularization<0) {
40521         T edge_min, edge_max = guide.max_min(edge_min);
40522         if (edge_min==edge_max) return *this;
40523         _regularization = -regularization*(edge_max - edge_min)/100;
40524       }
40525       _regularization = std::max(_regularization,0.01f);
40526       const unsigned int psize = (unsigned int)(1 + 2*_radius);
40527       CImg<Tfloat>
40528         mean_p = get_blur_box(psize,true),
40529         mean_I = guide.get_blur_box(psize,true).resize(mean_p),
40530         cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
40531         var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
40532         &a = cov_Ip.div(var_I+=_regularization),
40533         &b = mean_p-=a.get_mul(mean_I);
40534       a.blur_box(psize,true);
40535       b.blur_box(psize,true);
40536       return a.mul(guide)+=b;
40537     }
40538 
40539     //! Blur image using patch-based space.
40540     /**
40541        \param guide Image used to model the smoothing weights.
40542        \param sigma_s Amount of blur along the XYZ-axes.
40543        \param sigma_r Amount of blur along the value axis.
40544        \param patch_size Size of the patches.
40545        \param lookup_size Size of the window to search similar patches.
40546        \param smoothness Smoothness for the patch comparison.
40547        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
40548     **/
40549     template<typename t>
40550     CImg<T>& blur_patch(const CImg<t>& guide,
40551                         const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40552                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
40553       if (is_empty() || !patch_size || !lookup_size) return *this;
40554       return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
40555     }
40556 
40557     //! Blur image using patch-based space \newinstance.
40558     template<typename t>
40559     CImg<Tfloat> get_blur_patch(const CImg<t>& guide,
40560                                 const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40561                                 const unsigned int lookup_size=4, const float smoothness=0,
40562                                 const bool is_fast_approx=true) const {
40563 
40564 #define _cimg_blur_patch3d_fast(N) { \
40565       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
40566                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
40567                          firstprivate(P,Q)) \
40568       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \
40569         cimg_abort_test; \
40570         cimg_def##N##x##N##x##N(res,x,y,z); \
40571         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
40572         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
40573                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
40574         tfloat sum_weights = 0; \
40575         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
40576           if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) { \
40577             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
40578             tfloat distance2 = 0; \
40579             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40580             distance2/=Pnorm; \
40581             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
40582               alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0:1; \
40583             sum_weights+=weight; \
40584             cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
40585           } \
40586         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
40587         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
40588     } _cimg_abort_catch_openmp }
40589 
40590 #define _cimg_blur_patch3d(N) { \
40591       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
40592                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
40593                          firstprivate(P,Q)) \
40594       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \
40595         cimg_abort_test; \
40596         cimg_def##N##x##N##x##N(res,x,y,z); \
40597         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
40598         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
40599                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
40600         tfloat sum_weights = 0, weight_max = 0; \
40601         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
40602           tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
40603           tfloat distance2 = 0; \
40604           pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40605           distance2/=Pnorm; \
40606           const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
40607             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \
40608           if (weight>weight_max) weight_max = weight; \
40609           sum_weights+=weight; \
40610           cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
40611         } \
40612         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \
40613         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
40614         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
40615       } _cimg_abort_catch_openmp }
40616 
40617 #define _cimg_blur_patch2d_fast(N) { \
40618         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
40619                            firstprivate(P,Q)) \
40620         cimg_forXY(res,x,y) _cimg_abort_try_openmp { \
40621           cimg_abort_test; \
40622           cimg_def##N##x##N(res,x,y); \
40623           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
40624           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
40625           tfloat sum_weights = 0; \
40626           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
40627             if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))<sigma_r3) { \
40628               tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
40629               tfloat distance2 = 0; \
40630               pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40631               distance2/=Pnorm; \
40632               const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
40633                 alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0:1; \
40634               sum_weights+=weight; \
40635               cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
40636             } \
40637           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
40638           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
40639         } _cimg_abort_catch_openmp }
40640 
40641 #define _cimg_blur_patch2d(N) { \
40642         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
40643                            firstprivate(P,Q)) \
40644         cimg_forXY(res,x,y) _cimg_abort_try_openmp { \
40645           cimg_abort_test; \
40646           cimg_def##N##x##N(res,x,y); \
40647           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
40648           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
40649           tfloat sum_weights = 0, weight_max = 0; \
40650           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
40651             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
40652             tfloat distance2 = 0; \
40653             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40654             distance2/=Pnorm; \
40655             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
40656               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \
40657             if (weight>weight_max) weight_max = weight; \
40658             sum_weights+=weight; \
40659             cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
40660           } \
40661           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \
40662           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
40663           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
40664     } _cimg_abort_catch_openmp }
40665 
40666       typedef _cimg_tfloat tfloat;
40667       if (!is_sameXYZ(guide))
40668         throw CImgArgumentException(_cimg_instance
40669                                     "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
40670                                     cimg_instance,
40671                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
40672       if (is_empty() || !patch_size || !lookup_size) return +*this;
40673       Tfloat val_min, val_max = (Tfloat)max_min(val_min);
40674       _cimg_abort_init_openmp;
40675       cimg_abort_init;
40676 
40677       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
40678       const CImg<tfloat>
40679         __guide = guide?CImg<tfloat>(guide,guide.pixel_type()==cimg::type<tfloat>::string()):
40680                         CImg<tfloat>(*this,pixel_type()==cimg::type<tfloat>::string()),
40681         _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared();
40682       CImg<tfloat> P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P);
40683 
40684       t guide_min = (t)0, guide_max = (t)0;
40685       if (sigma_r<0) guide_max = guide.max_min(guide_min);
40686       const float
40687         guide_delta = (float)(guide_max - guide_min),
40688         _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
40689         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100,
40690         sigma_s2 = _sigma_s*_sigma_s,
40691         sigma_r2 = _sigma_r*_sigma_r,
40692         sigma_r3 = 3*_sigma_r,
40693         Pnorm = P.size()*sigma_r2;
40694       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
40695       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
40696       cimg::unused(N2,N3);
40697       if (_depth>1) switch (patch_size) { // 3D
40698         case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
40699         case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
40700         default : {
40701           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
40702           if (is_fast_approx) {
40703             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40704                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
40705                                firstprivate(P,Q))
40706             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast
40707               cimg_abort_test;
40708               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
40709               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
40710                 x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
40711               tfloat sum_weights = 0;
40712               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
40713                 if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) {
40714                   (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
40715                   const tfloat
40716                     dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
40717                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
40718                     weight = distance2>3?0:1;
40719                   sum_weights+=weight;
40720                   cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
40721                 }
40722               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
40723               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
40724             } _cimg_abort_catch_openmp
40725           } else {
40726             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40727                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
40728                                firstprivate(P,Q))
40729             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact
40730               cimg_abort_test;
40731               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
40732               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
40733                         x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
40734               tfloat sum_weights = 0, weight_max = 0;
40735               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
40736                 (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
40737                 const tfloat
40738                   dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
40739                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
40740                   weight = std::exp(-distance2);
40741                 if (weight>weight_max) weight_max = weight;
40742                 sum_weights+=weight;
40743                 cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
40744               }
40745               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c);
40746               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
40747               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
40748             } _cimg_abort_catch_openmp
40749           }
40750         }
40751         } else switch (patch_size) { // 2D
40752         case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
40753         case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
40754         case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
40755         case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
40756         case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
40757         case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
40758         case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
40759         case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
40760         default : { // Fast
40761           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
40762           if (is_fast_approx) {
40763             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
40764                                firstprivate(P,Q))
40765             cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast
40766               cimg_abort_test;
40767               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
40768               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
40769               tfloat sum_weights = 0;
40770               cimg_for_inXY(res,x0,y0,x1,y1,p,q)
40771                 if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))<sigma_r3) {
40772                   (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
40773                   const tfloat
40774                     dx = (tfloat)x - p, dy = (tfloat)y - q,
40775                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
40776                     weight = distance2>3?0:1;
40777                   sum_weights+=weight;
40778                   cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
40779                 }
40780               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
40781               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
40782             } _cimg_abort_catch_openmp
40783           } else {
40784             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
40785                                firstprivate(P,Q))
40786             cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact
40787               cimg_abort_test;
40788               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
40789               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
40790               tfloat sum_weights = 0, weight_max = 0;
40791               cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
40792                 (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
40793                 const tfloat
40794                   dx = (tfloat)x - p, dy = (tfloat)y - q,
40795                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
40796                   weight = std::exp(-distance2);
40797                 if (weight>weight_max) weight_max = weight;
40798                 sum_weights+=weight;
40799                 cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
40800               }
40801               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c);
40802               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
40803               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
40804             } _cimg_abort_catch_openmp
40805           }
40806         }
40807         }
40808       return res.cut(val_min,val_max);
40809     }
40810 
40811     //! Blur image using patch-based space \simplification.
40812     CImg<T>& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40813                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
40814       return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
40815     }
40816 
40817     //! Blur image using patch-based space \simplification \newinstance.
40818     CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40819                                 const unsigned int lookup_size=4, const float smoothness=0,
40820                                 const bool is_fast_approx=true) const {
40821       return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
40822     }
40823 
40824     //! Blur image with the median filter.
40825     /**
40826        \param n Size of the median filter.
40827        \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
40828     **/
40829     CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
40830       if (!n) return *this;
40831       return get_blur_median(n,threshold).move_to(*this);
40832     }
40833 
40834     //! Blur image with the median filter \newinstance.
40835     CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
40836       if (is_empty() || n<=1) return +*this;
40837       CImg<T> res(_width,_height,_depth,_spectrum);
40838       T *ptrd = res._data;
40839       cimg::unused(ptrd);
40840       const int hr = (int)n/2, hl = n - hr - 1;
40841       if (res._depth!=1) { // 3D
40842         if (threshold>0)
40843           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40844                                                                      _height*_depth*_spectrum>=4))
40845           cimg_forXYZC(*this,x,y,z,c) { // With threshold
40846             const int
40847               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
40848               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
40849               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
40850             const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
40851             CImg<T> values(n*n*n);
40852             unsigned int nb_values = 0;
40853             T *_ptrd = values.data();
40854             cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
40855               if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; }
40856             res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
40857           }
40858         else
40859           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40860                                                                      _height*_depth*_spectrum>=4))
40861           cimg_forXYZC(*this,x,y,z,c) { // Without threshold
40862             const int
40863               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
40864               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
40865               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
40866             res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
40867           }
40868       } else {
40869         if (threshold>0)
40870           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40871                                                                      _height*_spectrum>=4))
40872           cimg_forXYC(*this,x,y,c) { // With threshold
40873             const int
40874               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
40875               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
40876                                         nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
40877             const Tfloat val0 = (Tfloat)(*this)(x,y,c);
40878             CImg<T> values(n*n);
40879             unsigned int nb_values = 0;
40880             T *_ptrd = values.data();
40881             cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
40882               if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; }
40883             res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
40884           }
40885         else {
40886           const int
40887             w1 = width() - 1, h1 = height() - 1,
40888             w2 = width() - 2, h2 = height() - 2,
40889             w3 = width() - 3, h3 = height() - 3,
40890             w4 = width() - 4, h4 = height() - 4;
40891           switch (n) { // Without threshold
40892           case 3 : {
40893             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40894             cimg_forC(*this,c) {
40895               CImg<T> I(9);
40896               cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
40897                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
40898               cimg_for_borderXY(*this,x,y,1)
40899                 res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
40900                                       std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
40901             }
40902           } break;
40903           case 5 : {
40904             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40905             cimg_forC(*this,c) {
40906               CImg<T> I(25);
40907               cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
40908                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
40909                                           I[5],I[6],I[7],I[8],I[9],
40910                                           I[10],I[11],I[12],I[13],I[14],
40911                                           I[15],I[16],I[17],I[18],I[19],
40912                                           I[20],I[21],I[22],I[23],I[24]);
40913               cimg_for_borderXY(*this,x,y,2)
40914                 res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
40915                                       std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
40916             }
40917           } break;
40918           case 7 : {
40919             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40920             cimg_forC(*this,c) {
40921               CImg<T> I(49);
40922               cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
40923                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
40924                                           I[7],I[8],I[9],I[10],I[11],I[12],I[13],
40925                                           I[14],I[15],I[16],I[17],I[18],I[19],I[20],
40926                                           I[21],I[22],I[23],I[24],I[25],I[26],I[27],
40927                                           I[28],I[29],I[30],I[31],I[32],I[33],I[34],
40928                                           I[35],I[36],I[37],I[38],I[39],I[40],I[41],
40929                                           I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
40930               cimg_for_borderXY(*this,x,y,3)
40931                 res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
40932                                       std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
40933             }
40934           } break;
40935           default : {
40936             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40937                                cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4))
40938             cimg_forXYC(*this,x,y,c) {
40939               const int
40940                 x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
40941                 nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
40942                                           nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
40943               res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
40944             }
40945           }
40946           }
40947         }
40948       }
40949       return res;
40950     }
40951 
40952     //! Sharpen image.
40953     /**
40954        \param amplitude Sharpening amplitude
40955        \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
40956        \param edge Edge threshold (shock filters only).
40957        \param alpha Gradient smoothness (shock filters only).
40958        \param sigma Tensor smoothness (shock filters only).
40959     **/
40960     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
40961                      const float alpha=0, const float sigma=0) {
40962       if (is_empty()) return *this;
40963       T val_min, val_max = max_min(val_min);
40964       const float nedge = edge/2;
40965       CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
40966 
40967       if (_depth>1) { // 3D
40968         if (sharpen_type) { // Shock filters
40969           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
40970           if (sigma>0) G.blur(sigma);
40971           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
40972                                                                      _height*_depth>=16))
40973           cimg_forYZ(G,y,z) {
40974             Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
40975               *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
40976             CImg<Tfloat> val, vec;
40977             cimg_forX(G,x) {
40978               G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
40979               if (val[0]<0) val[0] = 0;
40980               if (val[1]<0) val[1] = 0;
40981               if (val[2]<0) val[2] = 0;
40982               *(ptrG0++) = vec(0,0);
40983               *(ptrG1++) = vec(0,1);
40984               *(ptrG2++) = vec(0,2);
40985               *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
40986             }
40987           }
40988           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
40989                                                          _spectrum>=2))
40990           cimg_forC(*this,c) {
40991             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
40992             CImg_3x3x3(I,Tfloat);
40993             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40994               const Tfloat
40995                 u = G(x,y,z,0),
40996                 v = G(x,y,z,1),
40997                 w = G(x,y,z,2),
40998                 amp = G(x,y,z,3),
40999                 ixx = Incc + Ipcc - 2*Iccc,
41000                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
41001                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
41002                 iyy = Icnc + Icpc - 2*Iccc,
41003                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
41004                 izz = Iccn + Iccp - 2*Iccc,
41005                 ixf = Incc - Iccc,
41006                 ixb = Iccc - Ipcc,
41007                 iyf = Icnc - Iccc,
41008                 iyb = Iccc - Icpc,
41009                 izf = Iccn - Iccc,
41010                 izb = Iccc - Iccp,
41011                 itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
41012                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
41013                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
41014               *(ptrd++) = veloc;
41015               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41016             }
41017             _veloc_max[c] = veloc_max;
41018           }
41019         } else  // Inverse diffusion
41020           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
41021                                                          _spectrum>=2))
41022           cimg_forC(*this,c) {
41023             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41024             CImg_3x3x3(I,Tfloat);
41025             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41026               const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
41027               *(ptrd++) = veloc;
41028               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41029             }
41030             _veloc_max[c] = veloc_max;
41031           }
41032       } else { // 2D
41033         if (sharpen_type) { // Shock filters
41034           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
41035           if (sigma>0) G.blur(sigma);
41036           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
41037                                                          _height>=(cimg_openmp_sizefactor)*16))
41038           cimg_forY(G,y) {
41039             CImg<Tfloat> val, vec;
41040             Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
41041             cimg_forX(G,x) {
41042               G.get_tensor_at(x,y).symmetric_eigen(val,vec);
41043               if (val[0]<0) val[0] = 0;
41044               if (val[1]<0) val[1] = 0;
41045               *(ptrG0++) = vec(0,0);
41046               *(ptrG1++) = vec(0,1);
41047               *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
41048             }
41049           }
41050           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
41051                                                          _spectrum>=2))
41052           cimg_forC(*this,c) {
41053             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41054             CImg_3x3(I,Tfloat);
41055             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41056               const Tfloat
41057                 u = G(x,y,0),
41058                 v = G(x,y,1),
41059                 amp = G(x,y,2),
41060                 ixx = Inc + Ipc - 2*Icc,
41061                 ixy = (Inn + Ipp - Inp - Ipn)/4,
41062                 iyy = Icn + Icp - 2*Icc,
41063                 ixf = Inc - Icc,
41064                 ixb = Icc - Ipc,
41065                 iyf = Icn - Icc,
41066                 iyb = Icc - Icp,
41067                 itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
41068                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
41069                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
41070               *(ptrd++) = veloc;
41071               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41072             }
41073             _veloc_max[c] = veloc_max;
41074           }
41075         } else // Inverse diffusion
41076           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
41077                                                          _spectrum>=2))
41078           cimg_forC(*this,c) {
41079             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41080             CImg_3x3(I,Tfloat);
41081             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41082               const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
41083               *(ptrd++) = veloc;
41084               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41085             }
41086             _veloc_max[c] = veloc_max;
41087           }
41088       }
41089       const Tfloat veloc_max = _veloc_max.max();
41090       if (veloc_max<=0) return *this;
41091       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
41092     }
41093 
41094     //! Sharpen image \newinstance.
41095     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
41096                         const float alpha=0, const float sigma=0) const {
41097       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
41098     }
41099 
41100     //! Return image gradient.
41101     /**
41102        \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
41103        \param scheme = Numerical scheme used for the gradient computation:
41104        - -1 = Backward finite differences
41105        - 0 = Centered finite differences (default)
41106        - 1 = Forward finite differences
41107        - 2 = Using Sobel kernels
41108        - 3 = Using rotation invariant kernels
41109        - 4 = Using Deriche recursive filter.
41110        - 5 = Using Van Vliet recursive filter.
41111     **/
41112     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=0) const {
41113       CImgList<Tfloat> res;
41114       char __axes[4] = { 0 };
41115       const char *_axes = axes?axes:__axes;
41116       if (!axes) {
41117         unsigned int k = 0;
41118         if (_width>1) __axes[k++] = 'x';
41119         if (_height>1) __axes[k++] = 'y';
41120         if (_depth>1) __axes[k++] = 'z';
41121       }
41122 
41123       CImg<Tfloat> grad;
41124       while (*_axes) {
41125         const char axis = cimg::lowercase(*(_axes++));
41126         if (axis!='x' && axis!='y' && axis!='z')
41127           throw CImgArgumentException(_cimg_instance
41128                                       "get_gradient(): Invalid specified axes '%s'.",
41129                                       cimg_instance,
41130                                       axes);
41131         const longT off = axis=='x'?1:axis=='y'?_width:_width*_height;
41132         if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) {
41133           grad.assign(_width,_height,_depth,_spectrum,0).move_to(res);
41134           continue;
41135         }
41136 
41137         const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme;
41138         switch (_scheme) {
41139         case -1 : { // Backward finite differences
41140           grad.assign(_width,_height,_depth,_spectrum);
41141           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41142           cimg_forXYZC(*this,x,y,z,c) {
41143             const ulongT pos = offset(x,y,z,c);
41144             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
41145               grad[pos] = 0;
41146             else
41147               grad[pos] = (Tfloat)_data[pos] - _data[pos - off];
41148           }
41149           grad.move_to(res);
41150         } break;
41151         case 1 : { // Forward finite differences
41152           grad.assign(_width,_height,_depth,_spectrum);
41153           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41154           cimg_forXYZC(*this,x,y,z,c) {
41155             const ulongT pos = offset(x,y,z,c);
41156             if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
41157               grad[pos] = 0;
41158             else
41159               grad[pos] = (Tfloat)_data[pos + off] - _data[pos];
41160           }
41161           grad.move_to(res);
41162         } break;
41163         case 2 : { // Sobel scheme
41164           grad.assign(_width,_height,_depth,_spectrum);
41165           if (axis=='x') // X-axis
41166             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41167                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41168                                               _depth*_spectrum>=2))
41169             cimg_forZC(*this,z,c) {
41170               CImg_3x3(I,Tfloat);
41171               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn;
41172             }
41173           else // Y-axis
41174             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41175                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41176                                               _depth*_spectrum>=2))
41177             cimg_forZC(*this,z,c) {
41178               CImg_3x3(I,Tfloat);
41179               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
41180             }
41181           grad.move_to(res);
41182         } break;
41183         case 3 : { // Rotation invariant scheme
41184           const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1));
41185           grad.assign(_width,_height,_depth,_spectrum);
41186           if (axis=='x') // X-axis
41187             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41188                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41189                                               _depth*_spectrum>=2))
41190             cimg_forZC(*this,z,c) {
41191               CImg_3x3(I,Tfloat);
41192               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
41193             }
41194           else // Y-axis
41195             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41196                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41197                                               _depth*_spectrum>=2))
41198             cimg_forZC(*this,z,c) {
41199               CImg_3x3(I,Tfloat);
41200               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
41201             }
41202           grad.move_to(res);
41203         } break;
41204         case 4 : // Deriche filter
41205           get_deriche(0,1,axis).move_to(res);
41206           break;
41207         case 5 : // Van Vliet filter
41208           get_vanvliet(0,1,axis).move_to(res);
41209           break;
41210         default : { // Central finite differences
41211           grad.assign(_width,_height,_depth,_spectrum);
41212           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41213           cimg_forXYZC(*this,x,y,z,c) {
41214             const ulongT pos = offset(x,y,z,c);
41215             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
41216               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2;
41217             else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
41218               grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2;
41219             else
41220               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2;
41221           }
41222           grad.move_to(res);
41223         } break;
41224         }
41225       }
41226       return res;
41227     }
41228 
41229     //! Return image hessian.
41230     /**
41231        \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
41232     **/
41233     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
41234       CImgList<Tfloat> res;
41235       char __axes[12] = { 0 };
41236       const char *_axes = axes?axes:__axes;
41237       if (!axes) {
41238         unsigned int k = 0;
41239         if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; }
41240         if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; }
41241         if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; }
41242         if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; }
41243         if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; }
41244         if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; }
41245       }
41246       const unsigned int len = (unsigned int)std::strlen(_axes);
41247       if (len%2)
41248         throw CImgArgumentException(_cimg_instance
41249                                     "get_hessian(): Invalid specified axes '%s'.",
41250                                     cimg_instance,
41251                                     axes);
41252       CImg<Tfloat> hess;
41253       for (unsigned int k = 0; k<len; k+=2) {
41254         const char
41255           _axis1 = cimg::lowercase(_axes[k]),
41256           _axis2 = cimg::lowercase(_axes[k + 1]),
41257           axis1 = std::min(_axis1,_axis2),
41258           axis2 = std::max(_axis2,_axis2);
41259         if (axis1!='x' && axis1!='y' && axis1!='z' &&
41260             axis2!='x' && axis2!='y' && axis2!='z')
41261           throw CImgArgumentException(_cimg_instance
41262                                       "get_hessian(): Invalid specified axes '%s'.",
41263                                       cimg_instance,
41264                                       axes);
41265         const longT off = axis1=='x'?1:axis1=='y'?_width:_width*_height;
41266 
41267         hess.assign(_width,_height,_depth,_spectrum);
41268 
41269         if (((axis1=='x' || axis2=='x') && _width==1) ||
41270             ((axis1=='y' || axis2=='y') && _height==1) ||
41271             ((axis1=='z' || axis2=='z') && _depth==1)) {
41272           hess.fill(0).move_to(res);
41273           continue;
41274         }
41275 
41276         if (axis1==axis2) // Ixx, Iyy, Izz
41277           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41278           cimg_forXYZC(*this,x,y,z,c) {
41279             const ulongT pos = offset(x,y,z,c);
41280             if ((axis1=='x' && !x) || (axis1=='y' && !y) || (axis1=='z' && !z))
41281               hess[pos] = (Tfloat)_data[pos + off] - _data[pos];
41282             else if ((axis1=='x' && x==width() - 1) ||
41283                      (axis1=='y' && y==height() - 1) ||
41284                      (axis1=='z' && z==depth() - 1))
41285               hess[pos] = (Tfloat)_data[pos - off] - _data[pos];
41286             else
41287               hess[pos] = (Tfloat)_data[pos + off] + _data[pos - off] - 2*_data[pos];
41288           }
41289         else if (axis1=='x' && axis2=='y') // Ixy
41290           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41291                              cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41292                                             _depth*_spectrum>=2))
41293           cimg_forZC(*this,z,c) {
41294             CImg_3x3(I,Tfloat);
41295             cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4;
41296           }
41297         else if (axis1=='x' && axis2=='z') // Ixz
41298           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
41299                                                          _spectrum>=2))
41300           cimg_forC(*this,c) {
41301             CImg_3x3x3(I,Tfloat);
41302             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4;
41303           }
41304         else // Iyz
41305           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
41306                                                          _spectrum>=2))
41307           cimg_forC(*this,c) {
41308             CImg_3x3x3(I,Tfloat);
41309             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4;
41310           }
41311         hess.move_to(res);
41312       }
41313       return res;
41314     }
41315 
41316     //! Compute image Laplacian.
41317     CImg<T>& laplacian() {
41318       return get_laplacian().move_to(*this);
41319     }
41320 
41321     //! Compute image Laplacian \newinstance.
41322     CImg<Tfloat> get_laplacian() const {
41323       if (is_empty()) return CImg<Tfloat>();
41324       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
41325       if (_depth>1) { // 3D
41326         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41327                                                        _spectrum>=2))
41328         cimg_forC(*this,c) {
41329           Tfloat *ptrd = res.data(0,0,0,c);
41330           CImg_3x3x3(I,Tfloat);
41331           cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
41332         }
41333       } else if (_height>1) { // 2D
41334         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41335                                                        _depth*_spectrum>=2))
41336         cimg_forC(*this,c) {
41337           Tfloat *ptrd = res.data(0,0,0,c);
41338           CImg_3x3(I,Tfloat);
41339           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
41340         }
41341       } else { // 1D
41342         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 &&
41343                                                        _height*_depth*_spectrum>=2))
41344         cimg_forC(*this,c) {
41345           Tfloat *ptrd = res.data(0,0,0,c);
41346           CImg_3x3(I,Tfloat);
41347           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
41348         }
41349       }
41350       return res;
41351     }
41352 
41353     //! Compute the structure tensor field of an image.
41354     /**
41355        \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
41356     **/
41357     CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
41358       return get_structure_tensors(is_fwbw_scheme).move_to(*this);
41359     }
41360 
41361     //! Compute the structure tensor field of an image \newinstance.
41362     CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
41363       if (is_empty()) return *this;
41364       CImg<Tfloat> res;
41365       if (_depth>1) { // 3D
41366         res.assign(_width,_height,_depth,6,0);
41367         if (!is_fwbw_scheme) { // Classical central finite differences
41368           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41369                                                          _spectrum>=2))
41370           cimg_forC(*this,c) {
41371             Tfloat
41372               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
41373               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
41374             CImg_3x3x3(I,Tfloat);
41375             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41376               const Tfloat
41377                 ix = (Incc - Ipcc)/2,
41378                 iy = (Icnc - Icpc)/2,
41379                 iz = (Iccn - Iccp)/2;
41380               *(ptrd0++)+=ix*ix;
41381               *(ptrd1++)+=ix*iy;
41382               *(ptrd2++)+=ix*iz;
41383               *(ptrd3++)+=iy*iy;
41384               *(ptrd4++)+=iy*iz;
41385               *(ptrd5++)+=iz*iz;
41386             }
41387           }
41388         } else { // Forward/backward finite differences
41389           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41390                                                          _spectrum>=2))
41391           cimg_forC(*this,c) {
41392             Tfloat
41393               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
41394               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
41395             CImg_3x3x3(I,Tfloat);
41396             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41397               const Tfloat
41398                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
41399                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
41400                 izf = Iccn - Iccc, izb = Iccc - Iccp;
41401               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
41402               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
41403               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
41404               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
41405               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
41406               *(ptrd5++)+=(izf*izf + izb*izb)/2;
41407             }
41408           }
41409         }
41410       } else { // 2D
41411         res.assign(_width,_height,_depth,3,0);
41412         if (!is_fwbw_scheme) { // Classical central finite differences
41413           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41414                                                          _depth*_spectrum>=2))
41415           cimg_forC(*this,c) {
41416             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
41417             CImg_3x3(I,Tfloat);
41418             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41419               const Tfloat
41420                 ix = (Inc - Ipc)/2,
41421                 iy = (Icn - Icp)/2;
41422               *(ptrd0++)+=ix*ix;
41423               *(ptrd1++)+=ix*iy;
41424               *(ptrd2++)+=iy*iy;
41425             }
41426           }
41427         } else { // Forward/backward finite differences (version 2)
41428           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41429                                                          _depth*_spectrum>=2))
41430           cimg_forC(*this,c) {
41431             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
41432             CImg_3x3(I,Tfloat);
41433             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41434               const Tfloat
41435                 ixf = Inc - Icc, ixb = Icc - Ipc,
41436                 iyf = Icn - Icc, iyb = Icc - Icp;
41437               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
41438               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
41439               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
41440             }
41441           }
41442         }
41443       }
41444       return res;
41445     }
41446 
41447     //! Compute field of diffusion tensors for edge-preserving smoothing.
41448     /**
41449        \param sharpness Sharpness
41450        \param anisotropy Anisotropy
41451        \param alpha Standard deviation of the gradient blur.
41452        \param sigma Standard deviation of the structure tensor blur.
41453        \param is_sqrt Tells if the square root of the tensor field is computed instead.
41454     **/
41455     CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
41456                                const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
41457       CImg<Tfloat> res;
41458       const float
41459         nsharpness = std::max(sharpness,1e-5f),
41460         power1 = (is_sqrt?0.5f:1)*nsharpness,
41461         power2 = power1/(1e-7f + 1 - anisotropy);
41462       blur(alpha).normalize(0,(T)255);
41463 
41464       if (_depth>1) { // 3D
41465         get_structure_tensors().move_to(res).blur(sigma);
41466         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
41467                                                                    _height*_depth>=(cimg_openmp_sizefactor)*256))
41468         cimg_forYZ(*this,y,z) {
41469           Tfloat
41470             *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
41471             *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
41472           CImg<floatT> val(3), vec(3,3);
41473           cimg_forX(*this,x) {
41474             res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
41475             const float
41476               _l1 = val[2], _l2 = val[1], _l3 = val[0],
41477               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
41478               ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
41479               vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
41480               wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
41481               n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
41482               n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
41483             *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
41484             *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
41485             *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
41486             *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
41487             *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
41488             *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
41489           }
41490         }
41491       } else { // for 2D images
41492         get_structure_tensors().move_to(res).blur(sigma);
41493         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
41494                                                        _height>=(cimg_openmp_sizefactor)*256))
41495         cimg_forY(*this,y) {
41496           Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
41497           CImg<floatT> val(2), vec(2,2);
41498           cimg_forX(*this,x) {
41499             res.get_tensor_at(x,y).symmetric_eigen(val,vec);
41500             const float
41501               _l1 = val[1], _l2 = val[0],
41502               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
41503               ux = vec(1,0), uy = vec(1,1),
41504               vx = vec(0,0), vy = vec(0,1),
41505               n1 = (float)std::pow(1 + l1 + l2,-power1),
41506               n2 = (float)std::pow(1 + l1 + l2,-power2);
41507             *(ptrd0++) = n1*ux*ux + n2*vx*vx;
41508             *(ptrd1++) = n1*ux*uy + n2*vx*vy;
41509             *(ptrd2++) = n1*uy*uy + n2*vy*vy;
41510           }
41511         }
41512       }
41513       return res.move_to(*this);
41514     }
41515 
41516     //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
41517     CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
41518                                        const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
41519       return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
41520     }
41521 
41522     //! Estimate displacement field between two images.
41523     /**
41524        \param source Reference image.
41525        \param smoothness Smoothness of estimated displacement field.
41526        \param precision Precision required for algorithm convergence.
41527        \param nb_scales Number of scales used to estimate the displacement field.
41528        \param iteration_max Maximum number of iterations allowed for one scale.
41529        \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
41530        \param guide Image used as the initial correspondence estimate for the algorithm.
41531        'guide' may have a last channel with boolean values (0=false | other=true) that
41532        tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
41533     **/
41534     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f,
41535                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
41536                           const bool is_backward=false,
41537                           const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
41538       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
41539         move_to(*this);
41540     }
41541 
41542     //! Estimate displacement field between two images \newinstance.
41543     CImg<floatT> get_displacement(const CImg<T>& source,
41544                                   const float smoothness=0.1f, const float precision=5.f,
41545                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
41546                                   const bool is_backward=false,
41547                                   const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
41548       if (is_empty() || !source) return +*this;
41549       if (!is_sameXYZC(source))
41550         throw CImgArgumentException(_cimg_instance
41551                                     "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
41552                                     "different dimensions.",
41553                                     cimg_instance,
41554                                     source._width,source._height,source._depth,source._spectrum,source._data);
41555       if (precision<0)
41556         throw CImgArgumentException(_cimg_instance
41557                                     "displacement(): Invalid specified precision %g "
41558                                     "(should be >=0)",
41559                                     cimg_instance,
41560                                     precision);
41561 
41562       const bool is_3d = source._depth>1;
41563       const unsigned int constraint = is_3d?3:2;
41564 
41565       if (guide &&
41566           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
41567         throw CImgArgumentException(_cimg_instance
41568                                     "displacement(): Specified guide (%u,%u,%u,%u,%p) "
41569                                     "has invalid dimensions.",
41570                                     cimg_instance,
41571                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
41572 
41573       const unsigned int
41574         mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
41575         _nb_scales = nb_scales>0?nb_scales:
41576         (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1);
41577 
41578       const float _precision = (float)std::pow(10.,-(double)precision);
41579       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
41580       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
41581 
41582       CImg<floatT> U, V;
41583       floatT bound = 0;
41584       for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
41585         const float factor = (float)std::pow(1.5,(double)scale);
41586         const unsigned int
41587           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
41588           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
41589           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
41590         if (sw<5 && sh<5 && (!is_3d || sd<5)) continue;  // Skip too small scales
41591         const CImg<Tfloat>
41592           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
41593           I2 = (get_resize(I1,2)-=tm)/=tdelta;
41594         if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
41595         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
41596         else {
41597           if (guide)
41598             guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
41599           else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
41600         }
41601 
41602         float dt = 2, energy = cimg::type<float>::max();
41603         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
41604         cimg_abort_init;
41605 
41606         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
41607           cimg_abort_test;
41608           float _energy = 0;
41609 
41610           if (is_3d) { // 3D version
41611             if (smoothness>=0) // Isotropic regularization
41612               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41613                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
41614                                                 _width>=(cimg_openmp_sizefactor)*16)
41615                                  reduction(+:_energy))
41616               cimg_forYZ(U,y,z) {
41617                 const int
41618                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
41619                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
41620                 cimg_for3X(U,x) {
41621                   const float
41622                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
41623                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
41624                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
41625                   float delta_I = 0, _energy_regul = 0;
41626                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
41627                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
41628                   cimg_forC(U,c) {
41629                     const float
41630                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
41631                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
41632                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
41633                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
41634                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
41635                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
41636                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
41637                                                           smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
41638                     _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
41639                   }
41640                   if (is_backward) { // Constraint displacement vectors to stay in image
41641                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
41642                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
41643                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
41644                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
41645                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
41646                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
41647                   } else {
41648                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
41649                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
41650                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
41651                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
41652                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
41653                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
41654                   }
41655                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
41656                 }
41657                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
41658                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
41659                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
41660                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
41661                   }
41662               } else { // Anisotropic regularization
41663               const float nsmoothness = -smoothness;
41664               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41665                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
41666                                                 _width>=(cimg_openmp_sizefactor)*16)
41667                                  reduction(+:_energy))
41668               cimg_forYZ(U,y,z) {
41669                 const int
41670                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
41671                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
41672                 cimg_for3X(U,x) {
41673                   const float
41674                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
41675                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
41676                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
41677                   float delta_I = 0, _energy_regul = 0;
41678                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
41679                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
41680                   cimg_forC(U,c) {
41681                     const float
41682                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
41683                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
41684                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
41685                       N2 = Ux*Ux + Uy*Uy + Uz*Uz,
41686                       N = std::sqrt(N2),
41687                       N3 = 1e-5f + N2*N,
41688                       coef_a = (1 - Ux*Ux/N2)/N,
41689                       coef_b = -2*Ux*Uy/N3,
41690                       coef_c = -2*Ux*Uz/N3,
41691                       coef_d = (1 - Uy*Uy/N2)/N,
41692                       coef_e = -2*Uy*Uz/N3,
41693                       coef_f = (1 - Uz*Uz/N2)/N,
41694                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
41695                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
41696                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
41697                       Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
41698                       Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
41699                       Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
41700                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
41701                                                           nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
41702                                                                          coef_c*Uxz + coef_d*Uyy +
41703                                                                          coef_e*Uyz + coef_f*Uzz ))
41704                                          )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
41705                     _energy_regul+=N;
41706                   }
41707                   if (is_backward) { // Constraint displacement vectors to stay in image
41708                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
41709                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
41710                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
41711                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
41712                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
41713                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
41714                   } else {
41715                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
41716                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
41717                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
41718                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
41719                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
41720                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
41721                   }
41722                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
41723                 }
41724                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
41725                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
41726                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
41727                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
41728                   }
41729               }
41730             }
41731           } else { // 2D version
41732             if (smoothness>=0) // Isotropic regularization
41733               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
41734                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
41735               cimg_forY(U,y) {
41736                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
41737                 cimg_for3X(U,x) {
41738                   const float
41739                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
41740                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
41741                   float delta_I = 0, _energy_regul = 0;
41742                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
41743                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
41744                   cimg_forC(U,c) {
41745                     const float
41746                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
41747                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
41748                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
41749                       Uyy = U(x,_n1y,c) + U(x,_p1y,c);
41750                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
41751                                                       smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
41752                     _energy_regul+=Ux*Ux + Uy*Uy;
41753                   }
41754                   if (is_backward) { // Constraint displacement vectors to stay in image
41755                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
41756                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
41757                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
41758                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
41759                   } else {
41760                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
41761                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
41762                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
41763                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
41764                   }
41765                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
41766                 }
41767                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
41768                     U(_x,_y,0) = V(_x,_y,0)/factor;
41769                     U(_x,_y,1) = V(_x,_y,1)/factor;
41770                   }
41771               } else { // Anisotropic regularization
41772               const float nsmoothness = -smoothness;
41773               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
41774                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
41775               cimg_forY(U,y) {
41776                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
41777                 cimg_for3X(U,x) {
41778                   const float
41779                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
41780                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
41781                   float delta_I = 0, _energy_regul = 0;
41782                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
41783                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
41784                   cimg_forC(U,c) {
41785                     const float
41786                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
41787                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
41788                       N2 = Ux*Ux + Uy*Uy,
41789                       N = std::sqrt(N2),
41790                       N3 = 1e-5f + N2*N,
41791                       coef_a = Uy*Uy/N3,
41792                       coef_b = -2*Ux*Uy/N3,
41793                       coef_c = Ux*Ux/N3,
41794                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
41795                       Uyy = U(x,_n1y,c) + U(x,_p1y,c),
41796                       Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
41797                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
41798                                                       nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
41799                       (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
41800                     _energy_regul+=N;
41801                   }
41802                   if (is_backward) { // Constraint displacement vectors to stay in image
41803                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
41804                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
41805                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
41806                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
41807                   } else {
41808                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
41809                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
41810                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
41811                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
41812                   }
41813                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
41814                 }
41815                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
41816                     U(_x,_y,0) = V(_x,_y,0)/factor;
41817                     U(_x,_y,1) = V(_x,_y,1)/factor;
41818                   }
41819               }
41820             }
41821           }
41822           const float d_energy = (_energy - energy)/(sw*sh*sd);
41823           if (d_energy<=0 && -d_energy<_precision) break;
41824           if (d_energy>0) dt*=0.5f;
41825           energy = _energy;
41826         }
41827       }
41828       return U;
41829     }
41830 
41831     //! Compute correspondence map between two images, using a patch-matching algorithm.
41832     /**
41833         \param patch_image The image containing the reference patches to match with the instance image.
41834         \param patch_width Width of the patch used for matching.
41835         \param patch_height Height of the patch used for matching.
41836         \param patch_depth Depth of the patch used for matching.
41837         \param nb_iterations Number of patch-match iterations.
41838         \param nb_randoms Number of randomization attempts (per pixel).
41839         \param patch_penalization Penalization factor in score related patch occurrences.
41840                if negative, also tells that identity result is not avoided.
41841         \param guide Image used as the initial correspondence estimate for the algorithm.
41842           'guide' may have a last channel with boolean values (0=false | other=true) that
41843           tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
41844         \param[out] matching_score Returned as the image of matching scores.
41845     **/
41846     template<typename t1, typename t2>
41847     CImg<T>& matchpatch(const CImg<T>& patch_image,
41848                         const unsigned int patch_width,
41849                         const unsigned int patch_height,
41850                         const unsigned int patch_depth,
41851                         const unsigned int nb_iterations,
41852                         const unsigned int nb_randoms,
41853                         const float patch_penalization,
41854                         const CImg<t1> &guide,
41855                         CImg<t2> &matching_score) {
41856       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
41857                             nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
41858     }
41859 
41860     //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
41861     template<typename t1, typename t2>
41862     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
41863                               const unsigned int patch_width,
41864                               const unsigned int patch_height,
41865                               const unsigned int patch_depth,
41866                               const unsigned int nb_iterations,
41867                               const unsigned int nb_randoms,
41868                               const float patch_penalization,
41869                               const CImg<t1> &guide,
41870                               CImg<t2> &matching_score) const {
41871       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
41872                          nb_iterations,nb_randoms,patch_penalization,
41873                          guide,true,matching_score);
41874     }
41875 
41876     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
41877     template<typename t>
41878     CImg<T>& matchpatch(const CImg<T>& patch_image,
41879                         const unsigned int patch_width,
41880                         const unsigned int patch_height,
41881                         const unsigned int patch_depth,
41882                         const unsigned int nb_iterations=5,
41883                         const unsigned int nb_randoms=5,
41884                         const float patch_penalization=0,
41885                         const CImg<t> &guide=CImg<t>::const_empty()) {
41886       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
41887                             nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this);
41888     }
41889 
41890     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
41891     template<typename t>
41892     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
41893                               const unsigned int patch_width,
41894                               const unsigned int patch_height,
41895                               const unsigned int patch_depth,
41896                               const unsigned int nb_iterations=5,
41897                               const unsigned int nb_randoms=5,
41898                               const float patch_penalization=0,
41899                               const CImg<t> &guide=CImg<t>::const_empty()) const {
41900       CImg<T> matching_score;
41901       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
41902                          nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score);
41903     }
41904 
41905     template<typename t1, typename t2>
41906     CImg<intT> _matchpatch(const CImg<T>& patch_image,
41907                            const unsigned int patch_width,
41908                            const unsigned int patch_height,
41909                            const unsigned int patch_depth,
41910                            const unsigned int nb_iterations,
41911                            const unsigned int nb_randoms,
41912                            const float patch_penalization,
41913                            const CImg<t1> &guide,
41914                            const bool is_matching_score,
41915                            CImg<t2> &matching_score) const {
41916       if (is_empty()) return CImg<intT>::const_empty();
41917       if (patch_image._spectrum!=_spectrum)
41918         throw CImgArgumentException(_cimg_instance
41919                                     "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
41920                                     "have different spectrums.",
41921                                     cimg_instance,
41922                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41923                                     patch_image._data);
41924       if (patch_width>_width || patch_height>_height || patch_depth>_depth)
41925         throw CImgArgumentException(_cimg_instance
41926                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
41927                                     "of the instance image.",
41928                                     cimg_instance,patch_width,patch_height,patch_depth);
41929       if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
41930         throw CImgArgumentException(_cimg_instance
41931                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
41932                                     "of the patch image image (%u,%u,%u,%u,%p).",
41933                                     cimg_instance,patch_width,patch_height,patch_depth,
41934                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41935                                     patch_image._data);
41936       const unsigned int
41937         _constraint = patch_image._depth>1?3:2,
41938         constraint = guide._spectrum>_constraint?_constraint:0;
41939 
41940       if (guide &&
41941           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
41942         throw CImgArgumentException(_cimg_instance
41943                                     "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
41944                                     "considering instance and patch image (%u,%u,%u,%u,%p).",
41945                                     cimg_instance,
41946                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
41947                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41948                                     patch_image._data);
41949 
41950       CImg<intT> a_map(_width,_height,_depth,patch_image._depth>1?3:2);
41951       CImg<ucharT> is_updated(_width,_height,_depth,1,3);
41952       CImg<floatT> score(_width,_height,_depth);
41953       CImg<uintT> occ;
41954       const float _patch_penalization = cimg::abs(patch_penalization);
41955       const bool allow_identity = patch_penalization>=0;
41956       if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0);
41957       const int
41958         psizew = (int)patch_width,  psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
41959         psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
41960         psized = (int)patch_depth,  psized1 = psized/2, psized2 = psized - psized1 - 1;
41961 
41962       // Interleave image buffers to speed up patch comparison (cache-friendly).
41963       CImg<T> in_this = get_permute_axes("cxyz");
41964       in_this._width = _width*_spectrum;
41965       in_this._height = _height;
41966       in_this._depth = _depth;
41967       in_this._spectrum = 1;
41968       CImg<T> in_patch = patch_image.get_permute_axes("cxyz");
41969       in_patch._width = patch_image._width*patch_image._spectrum;
41970       in_patch._height = patch_image._height;
41971       in_patch._depth = patch_image._depth;
41972       in_patch._spectrum = 1;
41973 
41974       if (_depth>1 || patch_image._depth>1) { // 3D version
41975 
41976         // Initialize correspondence map.
41977         if (guide)
41978           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64))
41979             cimg_forXYZ(*this,x,y,z) { // User-defined initialization
41980             const int
41981               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41982               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41983               cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
41984               u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2),
41985               v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2),
41986               w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2);
41987             a_map(x,y,z,0) = u;
41988             a_map(x,y,z,1) = v;
41989             a_map(x,y,z,2) = w;
41990             score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41991                                        x - cx1,y - cy1,z - cz1,
41992                                        u - cx1,v - cy1,w - cz1,
41993                                        u,v,w,0,allow_identity,cimg::type<float>::inf());
41994           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
41995             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
41996 #if cimg_use_openmp!=0
41997             rng+=omp_get_thread_num();
41998 #endif
41999             cimg_pragma_openmp(for cimg_openmp_collapse(2))
42000               cimg_forXYZ(*this,x,y,z) { // Random initialization
42001               const int
42002                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42003                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42004                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
42005                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
42006                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)),
42007                 w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng));
42008               a_map(x,y,z,0) = u;
42009               a_map(x,y,z,1) = v;
42010               a_map(x,y,z,2) = w;
42011               score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42012                                          x - cx1,y - cy1,z - cz1,
42013                                          u - cx1,v - cy1,w - cz1,
42014                                          u,v,w,0,allow_identity,cimg::type<float>::inf());
42015             }
42016             cimg::srand(rng);
42017           }
42018 
42019         // Start iteration loop.
42020         cimg_abort_init;
42021         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
42022           cimg_abort_test;
42023           const bool is_backward = iter&1;
42024           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
42025 
42026           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
42027                                                      iter<nb_iterations-2)) {
42028             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42029 
42030 #if cimg_use_openmp!=0
42031             rng+=omp_get_thread_num();
42032 #endif
42033             cimg_pragma_openmp(for cimg_openmp_collapse(2))
42034               cimg_forXYZ(*this,X,Y,Z) {
42035               const int
42036                 x = is_backward?width() - 1 - X:X,
42037                 y = is_backward?height() - 1 - Y:Y,
42038                 z = is_backward?depth() - 1 - Z:Z;
42039               if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
42040               const int
42041                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42042                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42043                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
42044                 xp = x - cx1,
42045                 yp = y - cy1,
42046                 zp = z - cz1;
42047 
42048               int best_u = a_map(x,y,z,0), best_v = a_map(x,y,z,1), best_w = a_map(x,y,z,2), u, v, w;
42049               const float best_score0 = score(x,y,z);
42050               float best_score = best_score0, s;
42051 
42052               if (x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor
42053                 u = a_map(x - 1,y,z,0);
42054                 v = a_map(x - 1,y,z,1);
42055                 w = a_map(x - 1,y,z,2);
42056                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
42057                     v>=cy1 && v<patch_image.height() - cy2 &&
42058                     w>=cz1 && w<patch_image.depth() - cz2) {
42059                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42060                                   xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,
42061                                   u,v,w,_patch_penalization,allow_identity,best_score);
42062                   if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; }
42063                 }
42064               }
42065               if (y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor
42066                 u = a_map(x,y - 1,z,0);
42067                 v = a_map(x,y - 1,z,1);
42068                 w = a_map(x,y - 1,z,2);
42069                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42070                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
42071                     w>=cz1 && w<patch_image.depth() - cz2) {
42072                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42073                                   xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,
42074                                   u,v,w,_patch_penalization,allow_identity,best_score);
42075                   if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; }
42076                 }
42077               }
42078               if (z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor
42079                 u = a_map(x,y,z - 1,0);
42080                 v = a_map(x,y,z - 1,1);
42081                 w = a_map(x,y,z - 1,2);
42082                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42083                     v>=cy1 && v<patch_image.height() - cy2 &&
42084                     w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
42085                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42086                                   xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,
42087                                   u,v,w,_patch_penalization,allow_identity,best_score);
42088                   if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; }
42089                 }
42090               }
42091               if (x<width() - 1 && (is_updated(x + 1,y,z)&cmask)) { // Compare with right neighbor
42092                 u = a_map(x + 1,y,z,0);
42093                 v = a_map(x + 1,y,z,1);
42094                 w = a_map(x + 1,y,z,2);
42095                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
42096                     v>=cy1 && v<patch_image.height() - cy2 &&
42097                     w>=cz1 && w<patch_image.depth() - cz2) {
42098                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42099                                   xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,
42100                                   u,v,w,_patch_penalization,allow_identity,best_score);
42101                   if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; }
42102                 }
42103               }
42104               if (y<height() - 1 && (is_updated(x,y + 1,z)&cmask)) { // Compare with bottom neighbor
42105                 u = a_map(x,y + 1,z,0);
42106                 v = a_map(x,y + 1,z,1);
42107                 w = a_map(x,y + 1,z,2);
42108                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42109                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
42110                     w>=cz1 && w<patch_image.depth() - cz2) {
42111                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42112                                   xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,
42113                                   u,v,w,_patch_penalization,allow_identity,best_score);
42114                   if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; }
42115                 }
42116               }
42117               if (z<depth() - 1 && (is_updated(x,y,z + 1)&cmask)) { // Compare with forward neighbor
42118                 u = a_map(x,y,z + 1,0);
42119                 v = a_map(x,y,z + 1,1);
42120                 w = a_map(x,y,z + 1,2);
42121                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42122                     v>=cy1 && v<patch_image.height() - cy2 &&
42123                     w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
42124                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42125                                   xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,
42126                                   u,v,w,_patch_penalization,allow_identity,best_score);
42127                   if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; }
42128                 }
42129               }
42130 
42131               float
42132                 dw = (float)patch_image.width(),
42133                 dh = (float)patch_image.height(),
42134                 dd = (float)patch_image.depth();
42135               for (unsigned int i = 0; i<nb_randoms; ++i) {
42136                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
42137                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
42138                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
42139                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
42140                 w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd),
42141                                                 std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng));
42142                 if (u!=best_u || v!=best_v || w!=best_w) {
42143                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
42144                                   xp,yp,zp,u - cx1,v - cy1,w - cz1,
42145                                   u,v,w,_patch_penalization,allow_identity,best_score);
42146                   if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; }
42147                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f);
42148                 }
42149               }
42150 
42151               if (best_score<best_score0) {
42152                 if (_patch_penalization!=0) {
42153                   uintT &n_occ = occ(a_map(x,y,z,0),a_map(x,y,z,1),a_map(x,y,z,2));
42154                   if (n_occ) cimg_pragma_openmp(atomic) --n_occ;
42155                 }
42156                 a_map(x,y,z,0) = best_u;
42157                 a_map(x,y,z,1) = best_v;
42158                 a_map(x,y,z,2) = best_w;
42159                 score(x,y,z) = best_score;
42160                 is_updated(x,y,z) = 3;
42161               } else is_updated(x,y,z)&=~nmask;
42162               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v,best_w);
42163             }
42164             cimg::srand(rng);
42165           }
42166         }
42167 
42168       } else { // 2D version
42169 
42170         // Initialize correspondence map.
42171         if (guide)
42172           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,64))
42173             cimg_forXY(*this,x,y) { // User-defined initialization
42174             const int
42175               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42176               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42177               u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2),
42178               v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2);
42179             a_map(x,y,0) = u;
42180             a_map(x,y,1) = v;
42181             score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42182                                      x - cx1,y - cy1,u - cx1,v - cy1,
42183                                      u,v,0,allow_identity,cimg::type<float>::inf());
42184           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
42185             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42186 
42187 #if cimg_use_openmp!=0
42188             rng+=omp_get_thread_num();
42189 #endif
42190             cimg_pragma_openmp(for)
42191               cimg_forXY(*this,x,y) { // Random initialization
42192               const int
42193                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42194                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42195                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
42196                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng));
42197               a_map(x,y,0) = u;
42198               a_map(x,y,1) = v;
42199               score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42200                                        x - cx1,y - cy1,u - cx1,v - cy1,
42201                                        u,v,0,allow_identity,cimg::type<float>::inf());
42202             }
42203             cimg::srand(rng);
42204           }
42205 
42206         // Start iteration loop.
42207         cimg_abort_init;
42208         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
42209           cimg_abort_test;
42210           const bool is_backward = iter&1;
42211           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
42212 
42213           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
42214                                                      iter<nb_iterations-2)) {
42215             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42216 
42217 #if cimg_use_openmp!=0
42218             rng+=omp_get_thread_num();
42219 #endif
42220             cimg_pragma_openmp(for)
42221               cimg_forXY(*this,X,Y) {
42222               const int
42223                 x = is_backward?width() - 1 - X:X,
42224                 y = is_backward?height() - 1 - Y:Y;
42225               if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
42226               const int
42227                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42228                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42229                 xp = x - cx1,
42230                 yp = y - cy1;
42231 
42232               int best_u = a_map(x,y,0), best_v = a_map(x,y,1), u, v;
42233               const float best_score0 = score(x,y);
42234               float best_score = best_score0, s;
42235 
42236               if (x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor
42237                 u = a_map(x - 1,y,0);
42238                 v = a_map(x - 1,y,1);
42239                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
42240                     v>=cy1 && v<patch_image.height() - cy2) {
42241                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42242                                   xp,yp,u + 1 - cx1,v - cy1,
42243                                   u,v,_patch_penalization,allow_identity,best_score);
42244                   if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; }
42245                 }
42246               }
42247               if (y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor
42248                 u = a_map(x,y - 1,0);
42249                 v = a_map(x,y - 1,1);
42250                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42251                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
42252                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42253                                   xp,yp,u - cx1,v + 1 - cy1,
42254                                   u,v,_patch_penalization,allow_identity,best_score);
42255                   if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; }
42256                 }
42257               }
42258               if (x<width() - 1 && (is_updated(x + 1,y)&cmask)) { // Compare with right neighbor
42259                 u = a_map(x + 1,y,0);
42260                 v = a_map(x + 1,y,1);
42261                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
42262                     v>=cy1 && v<patch_image.height() - cy2) {
42263                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42264                                   xp,yp,u - 1 - cx1,v - cy1,
42265                                   u,v,_patch_penalization,allow_identity,best_score);
42266                   if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; }
42267                 }
42268               }
42269               if (y<height() - 1 && (is_updated(x,y + 1)&cmask)) { // Compare with bottom neighbor
42270                 u = a_map(x,y + 1,0);
42271                 v = a_map(x,y + 1,1);
42272                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42273                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
42274                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42275                                   xp,yp,u - cx1,v - 1 - cy1,
42276                                   u,v,_patch_penalization,allow_identity,best_score);
42277                   if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; }
42278                 }
42279               }
42280 
42281               float
42282                 dw = (float)patch_image.width(),
42283                 dh = (float)patch_image.height();
42284               for (unsigned int i = 0; i<nb_randoms; ++i) {
42285                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
42286                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
42287                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
42288                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
42289                 if (u!=best_u || v!=best_v) {
42290                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
42291                                   xp,yp,u - cx1,v - cy1,
42292                                   u,v,_patch_penalization,allow_identity,best_score);
42293                   if (s<best_score) { best_u = u; best_v = v; best_score = s; }
42294                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f);
42295                 }
42296               }
42297 
42298               if (best_score<best_score0) {
42299                 if (_patch_penalization!=0) {
42300                   uintT &n_occ = occ(a_map(x,y,0),a_map(x,y,1));
42301                   if (n_occ) cimg_pragma_openmp(atomic) --n_occ;
42302                 }
42303                 a_map(x,y,0) = best_u;
42304                 a_map(x,y,1) = best_v;
42305                 score(x,y) = best_score;
42306                 is_updated(x,y) = 3;
42307               } else is_updated(x,y)&=~nmask;
42308               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v);
42309             }
42310             cimg::srand(rng);
42311           }
42312         }
42313       }
42314 
42315       if (is_matching_score) score.move_to(matching_score);
42316       return a_map;
42317     }
42318 
42319     // Compute SSD between two patches in different images.
42320     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ,
42321                              const unsigned int psizew, const unsigned int psizeh,
42322                              const unsigned int psized, const unsigned int psizec,
42323                              const int x1, const int y1, const int z1,
42324                              const int x2, const int y2, const int z2,
42325                              const int xc, const int yc, const int zc,
42326                              const float patch_penalization,
42327                              const bool allow_identity,
42328                              const float max_score) { // 3D version
42329       if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)<patch_penalization)
42330         return cimg::type<float>::inf();
42331       const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2);
42332       const unsigned int psizewc = psizew*psizec;
42333       const ulongT
42334         offx1 = (ulongT)img1._width - psizewc,
42335         offx2 = (ulongT)img2._width - psizewc,
42336         offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width,
42337         offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width;
42338       float ssd = 0;
42339       for (unsigned int k = 0; k<psized; ++k) {
42340         for (unsigned int j = 0; j<psizeh; ++j) {
42341           for (unsigned int i = 0; i<psizewc; ++i)
42342             ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
42343           if (ssd>max_score) return max_score;
42344           p1+=offx1; p2+=offx2;
42345         }
42346         p1+=offy1; p2+=offy2;
42347       }
42348       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
42349                                                patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100);
42350     }
42351 
42352     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ,
42353                              const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec,
42354                              const int x1, const int y1,
42355                              const int x2, const int y2,
42356                              const int xc, const int yc,
42357                              const float patch_penalization,
42358                              const bool allow_identity,
42359                              const float max_score) { // 2D version
42360       if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)<patch_penalization)
42361         return cimg::type<float>::inf();
42362       const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2);
42363       const unsigned int psizewc = psizew*psizec;
42364       const ulongT
42365         offx1 = (ulongT)img1._width - psizewc,
42366         offx2 = (ulongT)img2._width - psizewc;
42367       float ssd = 0;
42368       for (unsigned int j = 0; j<psizeh; ++j) {
42369         for (unsigned int i = 0; i<psizewc; ++i)
42370           ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
42371         if (ssd>max_score) return max_score;
42372         p1+=offx1; p2+=offx2;
42373       }
42374       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
42375                                                patch_penalization*psizewc*psizeh*occ(xc,yc)/100);
42376     }
42377 
42378     //! Compute Euclidean distance function to a specified value.
42379     /**
42380         \param value Reference value.
42381         \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
42382         \note
42383         The distance transform implementation has been submitted by A. Meijster, and implements
42384         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
42385                      "A general algorithm for computing distance transforms in linear time.",
42386                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
42387                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
42388          The submitted code has then been modified to fit CImg coding style and constraints.
42389     **/
42390     CImg<T>& distance(const T& value, const unsigned int metric=2) {
42391       if (is_empty()) return *this;
42392       if (cimg::type<Tint>::string()!=pixel_type()) // For datatype < int
42393         return CImg<Tint>(*this,false).distance((Tint)value,metric).
42394           cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
42395       bool is_value = false;
42396       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
42397       if (!is_value) return fill(cimg::type<T>::max());
42398       switch (metric) {
42399       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev
42400       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan
42401       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Squared Euclidean
42402       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Euclidean
42403       }
42404       return *this;
42405     }
42406 
42407     //! Compute distance to a specified value \newinstance.
42408     CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
42409       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
42410     }
42411 
42412     static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
42413       return (u*u - i*i + g[u] - g[i])/(2*(u - i));
42414     }
42415 
42416     static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
42417       return (x - i)*(x - i) + g[i];
42418     }
42419 
42420     static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
42421       return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
42422     }
42423 
42424     static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
42425       return (x<i?i - x:x - i) + g[i];
42426     }
42427 
42428     static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
42429       const longT h = (i + u)/2;
42430       if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
42431       return h<u - g[i]?h:u - g[i];
42432     }
42433 
42434     static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
42435       const longT d = x<i?i - x:x - i;
42436       return d<g[i]?g[i]:d;
42437     }
42438 
42439     static void _distance_scan(const unsigned int len,
42440                                const longT *const g,
42441                                longT (*const sep)(const longT, const longT, const longT *const),
42442                                longT (*const f)(const longT, const longT, const longT *const),
42443                                longT *const s,
42444                                longT *const t,
42445                                longT *const dt) {
42446       longT q = s[0] = t[0] = 0;
42447       for (int u = 1; u<(int)len; ++u) { // Forward scan
42448         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
42449         if (q<0) { q = 0; s[0] = u; }
42450         else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
42451       }
42452       for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan
42453     }
42454 
42455     CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
42456                             longT (*const f)(const longT, const longT, const longT *const)) {
42457  // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
42458 #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
42459 
42460       const ulongT wh = (ulongT)_width*_height;
42461 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42462       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
42463 #endif
42464       cimg_forC(*this,c) {
42465         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
42466         CImg<T> img = get_shared_channel(c);
42467 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42468         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
42469                                                                    _height*_depth>=16)
42470                            firstprivate(g,dt,s,t))
42471 #endif
42472         cimg_forYZ(*this,y,z) { // Over X-direction
42473           cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
42474           _distance_scan(_width,g,sep,f,s,t,dt);
42475           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
42476         }
42477         if (_height>1) {
42478           g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
42479 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42480           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
42481                              cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16)
42482                              firstprivate(g,dt,s,t))
42483 #endif
42484           cimg_forXZ(*this,x,z) { // Over Y-direction
42485             cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
42486             _distance_scan(_height,g,sep,f,s,t,dt);
42487             cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
42488           }
42489         }
42490         if (_depth>1) {
42491           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
42492 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42493           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
42494                              cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16)
42495                              firstprivate(g,dt,s,t))
42496 #endif
42497           cimg_forXY(*this,x,y) { // Over Z-direction
42498             cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
42499             _distance_scan(_depth,g,sep,f,s,t,dt);
42500             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
42501           }
42502         }
42503       }
42504       return *this;
42505     }
42506 
42507     //! Compute chamfer distance to a specified value, with a custom metric.
42508     /**
42509        \param value Reference value.
42510        \param metric_mask Metric mask.
42511        \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
42512     **/
42513     template<typename t>
42514     CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
42515       if (is_empty()) return *this;
42516       bool is_value = false;
42517       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
42518       if (!is_value) return fill(cimg::type<T>::max());
42519       const ulongT wh = (ulongT)_width*_height;
42520       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
42521       cimg_forC(*this,c) {
42522         CImg<T> img = get_shared_channel(c);
42523         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
42524                            cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024))
42525         cimg_forXYZ(metric_mask,dx,dy,dz) {
42526           const t weight = metric_mask(dx,dy,dz);
42527           if (weight) {
42528             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan
42529               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
42530                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
42531                   const T dd = img(nx,ny,nz,0,wh) + weight;
42532                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
42533                 }
42534               }
42535             }
42536             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan
42537               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
42538                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
42539                   const T dd = img(nx,ny,nz,0,wh) + weight;
42540                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
42541                 }
42542               }
42543             }
42544           }
42545         }
42546       }
42547       return *this;
42548     }
42549 
42550     //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
42551     template<typename t>
42552     CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
42553       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
42554     }
42555 
42556     //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
42557     /**
42558        \param value Reference value.
42559        \param metric Field of distance potentials.
42560        \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
42561        \param[out] return_path An image containing the nodes of the minimal path.
42562      **/
42563     template<typename t, typename to>
42564     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
42565                                CImg<to>& return_path) {
42566       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
42567     }
42568 
42569     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
42570     template<typename t, typename to>
42571     CImg<typename cimg::superset<t,long>::type>
42572     get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
42573                           CImg<to>& return_path) const {
42574       if (is_empty()) return return_path.assign();
42575       if (!is_sameXYZ(metric))
42576         throw CImgArgumentException(_cimg_instance
42577                                     "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
42578                                     "have incompatible dimensions.",
42579                                     cimg_instance,
42580                                     metric._width,metric._height,metric._depth,metric._spectrum);
42581       typedef typename cimg::superset<t,long>::type td;  // Type used for computing cumulative distances
42582       CImg<td> result(_width,_height,_depth,_spectrum), Q;
42583       CImg<boolT> is_queued(_width,_height,_depth,1);
42584       if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
42585 
42586       cimg_forC(*this,c) {
42587         const CImg<T> img = get_shared_channel(c);
42588         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
42589         CImg<td> res = result.get_shared_channel(c);
42590         CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
42591         unsigned int sizeQ = 0;
42592 
42593         // Detect initial seeds.
42594         is_queued.fill(0);
42595         cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
42596           Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
42597           res(x,y,z) = 0;
42598           if (path) path(x,y,z) = (to)0;
42599         }
42600 
42601         // Start distance propagation.
42602         while (sizeQ) {
42603 
42604           // Get and remove point with minimal potential from the queue.
42605           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
42606           const td P = (td)-Q(0,0);
42607           Q._priority_queue_remove(sizeQ);
42608 
42609           // Update neighbors.
42610           td npot = 0;
42611           if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
42612             res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
42613           }
42614           if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
42615             res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
42616           }
42617           if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
42618             res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
42619           }
42620           if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
42621             res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
42622           }
42623           if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
42624             res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
42625           }
42626           if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
42627             res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
42628           }
42629 
42630           if (is_high_connectivity) {
42631             const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f);
42632 
42633             // Diagonal neighbors on slice z.
42634             if (x - 1>=0 && y - 1>=0 &&
42635                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
42636               res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
42637             }
42638             if (x + 1<width() && y - 1>=0 &&
42639                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
42640               res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
42641             }
42642             if (x - 1>=0 && y + 1<height() &&
42643                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
42644               res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
42645             }
42646             if (x + 1<width() && y + 1<height() &&
42647                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
42648               res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
42649             }
42650 
42651             if (z - 1>=0) { // Diagonal neighbors on slice z - 1
42652               if (x - 1>=0 &&
42653                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
42654                 res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
42655               }
42656               if (x + 1<width() &&
42657                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
42658                 res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
42659               }
42660               if (y - 1>=0 &&
42661                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
42662                 res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
42663               }
42664               if (y + 1<height() &&
42665                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
42666                 res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
42667               }
42668               if (x - 1>=0 && y - 1>=0 &&
42669                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
42670                                            x - 1,y - 1,z - 1)) {
42671                 res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
42672               }
42673               if (x + 1<width() && y - 1>=0 &&
42674                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
42675                                            x + 1,y - 1,z - 1)) {
42676                 res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
42677               }
42678               if (x - 1>=0 && y + 1<height() &&
42679                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
42680                                            x - 1,y + 1,z - 1)) {
42681                 res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
42682               }
42683               if (x + 1<width() && y + 1<height() &&
42684                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
42685                                            x + 1,y + 1,z - 1)) {
42686                 res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
42687               }
42688             }
42689 
42690             if (z + 1<depth()) { // Diagonal neighbors on slice z + 1
42691               if (x - 1>=0 &&
42692                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
42693                 res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
42694               }
42695               if (x + 1<width() &&
42696                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
42697                 res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
42698               }
42699               if (y - 1>=0 &&
42700                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
42701                 res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
42702               }
42703               if (y + 1<height() &&
42704                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
42705                 res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
42706               }
42707               if (x - 1>=0 && y - 1>=0 &&
42708                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
42709                                            x - 1,y - 1,z + 1)) {
42710                 res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
42711               }
42712               if (x + 1<width() && y - 1>=0 &&
42713                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
42714                                            x + 1,y - 1,z + 1)) {
42715                 res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
42716               }
42717               if (x - 1>=0 && y + 1<height() &&
42718                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
42719                                            x - 1,y + 1,z + 1)) {
42720                 res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
42721               }
42722               if (x + 1<width() && y + 1<height() &&
42723                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
42724                                            x + 1,y + 1,z + 1)) {
42725                 res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
42726               }
42727             }
42728           }
42729         }
42730       }
42731       return result;
42732     }
42733 
42734     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
42735     template<typename t>
42736     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
42737                                const bool is_high_connectivity=false) {
42738       return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
42739     }
42740 
42741     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
42742     template<typename t>
42743     CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
42744                                        const bool is_high_connectivity=false) const {
42745       CImg<T> return_path;
42746       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
42747     }
42748 
42749     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
42750     /**
42751        \param value Reference value.
42752        \param metric Field of distance potentials.
42753      **/
42754     template<typename t>
42755     CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
42756       return get_distance_eikonal(value,metric).move_to(*this);
42757     }
42758 
42759     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
42760     template<typename t>
42761     CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
42762       if (is_empty()) return *this;
42763       if (!is_sameXYZ(metric))
42764         throw CImgArgumentException(_cimg_instance
42765                                     "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
42766                                     "incompatible dimensions.",
42767                                     cimg_instance,
42768                                     metric._width,metric._height,metric._depth,metric._spectrum);
42769       CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
42770       CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen
42771 
42772       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
42773       cimg_forC(*this,c) {
42774         const CImg<T> img = get_shared_channel(c);
42775         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
42776         CImg<Tfloat> res = result.get_shared_channel(c);
42777         unsigned int sizeQ = 0;
42778         state.fill(-1);
42779 
42780         // Detect initial seeds.
42781         Tfloat *ptr1 = res._data; char *ptr2 = state._data;
42782         cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
42783 
42784         // Initialize seeds neighbors.
42785         ptr2 = state._data;
42786         cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
42787           if (x - 1>=0 && state(x - 1,y,z)==-1) {
42788             const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
42789             Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
42790           }
42791           if (x + 1<width() && state(x + 1,y,z)==-1) {
42792             const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
42793             Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
42794           }
42795           if (y - 1>=0 && state(x,y - 1,z)==-1) {
42796             const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
42797             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
42798           }
42799           if (y + 1<height() && state(x,y + 1,z)==-1) {
42800             const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
42801             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
42802           }
42803           if (z - 1>=0 && state(x,y,z - 1)==-1) {
42804             const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
42805             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
42806           }
42807           if (z + 1<depth() && state(x,y,z + 1)==-1) {
42808             const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
42809             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
42810           }
42811         }
42812 
42813         // Propagate front.
42814         while (sizeQ) {
42815           int x = -1, y = -1, z = -1;
42816           while (sizeQ && x<0) {
42817             x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
42818             Q._priority_queue_remove(sizeQ);
42819             if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
42820           }
42821           if (x>=0) {
42822             if (x - 1>=0 && state(x - 1,y,z)!=1) {
42823               const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
42824               if (dist<res(x - 1,y,z)) {
42825                 res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
42826               }
42827             }
42828             if (x + 1<width() && state(x + 1,y,z)!=1) {
42829               const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
42830               if (dist<res(x + 1,y,z)) {
42831                 res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
42832               }
42833             }
42834             if (y - 1>=0 && state(x,y - 1,z)!=1) {
42835               const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
42836               if (dist<res(x,y - 1,z)) {
42837                 res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
42838               }
42839             }
42840             if (y + 1<height() && state(x,y + 1,z)!=1) {
42841               const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
42842               if (dist<res(x,y + 1,z)) {
42843                 res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
42844               }
42845             }
42846             if (z - 1>=0 && state(x,y,z - 1)!=1) {
42847               const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
42848               if (dist<res(x,y,z - 1)) {
42849                 res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
42850               }
42851             }
42852             if (z + 1<depth() && state(x,y,z + 1)!=1) {
42853               const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
42854               if (dist<res(x,y,z + 1)) {
42855                 res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
42856               }
42857             }
42858           }
42859         }
42860       }
42861       return result;
42862     }
42863 
42864     // Locally solve eikonal equation.
42865     Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
42866                               const int x=0, const int y=0, const int z=0) const {
42867       const Tfloat M = (Tfloat)cimg::type<T>::max();
42868       T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
42869       Tfloat root = 0;
42870       if (_depth>1) { // 3D
42871         T
42872           T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
42873           T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
42874         if (T1>T2) cimg::swap(T1,T2);
42875         if (T2>T3) cimg::swap(T2,T3);
42876         if (T1>T2) cimg::swap(T1,T2);
42877         if (P<=0) return (Tfloat)T1;
42878         if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
42879           return std::max((Tfloat)T3,root);
42880         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
42881           return std::max((Tfloat)T2,root);
42882         return P + T1;
42883       } else if (_height>1) { // 2D
42884         T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
42885         if (T1>T2) cimg::swap(T1,T2);
42886         if (P<=0) return (Tfloat)T1;
42887         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
42888           return std::max((Tfloat)T2,root);
42889         return P + T1;
42890       } else { // 1D
42891         if (P<=0) return (Tfloat)T1;
42892         return P + T1;
42893       }
42894       return 0;
42895     }
42896 
42897     // Find max root of a 2nd-order polynomial.
42898     static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
42899       const Tfloat delta = b*b - 4*a*c;
42900       if (delta<0) return false;
42901       root = 0.5f*(-b + std::sqrt(delta))/a;
42902       return true;
42903     }
42904 
42905     // Insert new point in heap.
42906     template<typename t>
42907     void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
42908                                     const unsigned int x, const unsigned int y, const unsigned int z) {
42909       if (state(x,y,z)>0) return;
42910       state(x,y,z) = 0;
42911       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
42912       (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
42913       for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
42914         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
42915         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
42916       }
42917     }
42918 
42919     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
42920     /**
42921        \param nb_iterations Number of PDE iterations.
42922        \param band_size Size of the narrow band.
42923        \param time_step Time step of the PDE iterations.
42924     **/
42925     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
42926       if (is_empty()) return *this;
42927       CImg<Tfloat> velocity(*this,false);
42928       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
42929         Tfloat *ptrd = velocity._data, veloc_max = 0;
42930         if (_depth>1) { // 3D
42931           CImg_3x3x3(I,Tfloat);
42932           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
42933             const Tfloat
42934               gx = (Incc - Ipcc)/2,
42935               gy = (Icnc - Icpc)/2,
42936               gz = (Iccn - Iccp)/2,
42937               sgn = -cimg::sign(Iccc),
42938               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
42939               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
42940               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
42941               ng = 1e-5f + cimg::hypot(gx,gy,gz),
42942               ngx = gx/ng,
42943               ngy = gy/ng,
42944               ngz = gz/ng,
42945               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
42946             *(ptrd++) = veloc;
42947             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
42948           } else *(ptrd++) = 0;
42949         } else { // 2D version
42950           CImg_3x3(I,Tfloat);
42951           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
42952             const Tfloat
42953               gx = (Inc - Ipc)/2,
42954               gy = (Icn - Icp)/2,
42955               sgn = -cimg::sign(Icc),
42956               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
42957               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
42958               ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
42959               ngx = gx/ng,
42960               ngy = gy/ng,
42961               veloc = sgn*(ngx*ix + ngy*iy - 1);
42962             *(ptrd++) = veloc;
42963             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
42964           } else *(ptrd++) = 0;
42965         }
42966         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
42967       }
42968       return *this;
42969     }
42970 
42971     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
42972     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
42973                                       const float time_step=0.5f) const {
42974       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
42975     }
42976 
42977     //! Compute Haar multiscale wavelet transform.
42978     /**
42979        \param axis Axis considered for the transform.
42980        \param invert Set inverse of direct transform.
42981        \param nb_scales Number of scales used for the transform.
42982     **/
42983     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
42984       return get_haar(axis,invert,nb_scales).move_to(*this);
42985     }
42986 
42987     //! Compute Haar multiscale wavelet transform \newinstance.
42988     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
42989       if (is_empty() || !nb_scales) return +*this;
42990       CImg<Tfloat> res;
42991       const Tfloat sqrt2 = std::sqrt(2.f);
42992       if (nb_scales==1) {
42993         switch (cimg::lowercase(axis)) { // Single scale transform
42994         case 'x' : {
42995           const unsigned int w = _width/2;
42996           if (w) {
42997             if ((w%2) && w!=1)
42998               throw CImgInstanceException(_cimg_instance
42999                                           "haar(): Sub-image width %u is not even.",
43000                                           cimg_instance,
43001                                           w);
43002 
43003             res.assign(_width,_height,_depth,_spectrum);
43004             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
43005               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
43006                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
43007                 res(x2++,y,z,c) = (val0 - val1)/sqrt2;
43008                 res(x2++,y,z,c) = (val0 + val1)/sqrt2;
43009               }
43010             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
43011               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
43012                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
43013                 res(x,y,z,c) = (val0 + val1)/sqrt2;
43014                 res(xw,y,z,c) = (val1 - val0)/sqrt2;
43015               }
43016             }
43017           } else return *this;
43018         } break;
43019         case 'y' : {
43020           const unsigned int h = _height/2;
43021           if (h) {
43022             if ((h%2) && h!=1)
43023               throw CImgInstanceException(_cimg_instance
43024                                           "haar(): Sub-image height %u is not even.",
43025                                           cimg_instance,
43026                                           h);
43027 
43028             res.assign(_width,_height,_depth,_spectrum);
43029             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
43030               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
43031                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
43032                 res(x,y2++,z,c) = (val0 - val1)/sqrt2;
43033                 res(x,y2++,z,c) = (val0 + val1)/sqrt2;
43034               }
43035             } else cimg_forXZC(*this,x,z,c) {
43036               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
43037                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
43038                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
43039                 res(x,yh,z,c) = (val1 - val0)/sqrt2;
43040               }
43041             }
43042           } else return *this;
43043         } break;
43044         case 'z' : {
43045           const unsigned int d = _depth/2;
43046           if (d) {
43047             if ((d%2) && d!=1)
43048               throw CImgInstanceException(_cimg_instance
43049                                           "haar(): Sub-image depth %u is not even.",
43050                                           cimg_instance,
43051                                           d);
43052 
43053             res.assign(_width,_height,_depth,_spectrum);
43054             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
43055               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
43056                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
43057                 res(x,y,z2++,c) = (val0 - val1)/sqrt2;
43058                 res(x,y,z2++,c) = (val0 + val1)/sqrt2;
43059               }
43060             } else cimg_forXYC(*this,x,y,c) {
43061               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
43062                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
43063                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
43064                 res(x,y,zd,c) = (val1 - val0)/sqrt2;
43065               }
43066             }
43067           } else return *this;
43068         } break;
43069         default :
43070           throw CImgArgumentException(_cimg_instance
43071                                       "haar(): Invalid specified axis '%c' "
43072                                       "(should be { x | y | z }).",
43073                                       cimg_instance,
43074                                       axis);
43075         }
43076       } else { // Multi-scale version
43077         if (invert) {
43078           res.assign(*this,false);
43079           switch (cimg::lowercase(axis)) {
43080           case 'x' : {
43081             unsigned int w = _width;
43082             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
43083             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
43084           } break;
43085           case 'y' : {
43086             unsigned int h = _width;
43087             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
43088             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));
43089           } break;
43090           case 'z' : {
43091             unsigned int d = _depth;
43092             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
43093             for (d = d?d:1; d<=_depth; d*=2)
43094               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
43095           } break;
43096           default :
43097             throw CImgArgumentException(_cimg_instance
43098                                         "haar(): Invalid specified axis '%c' "
43099                                         "(should be { x | y | z }).",
43100                                         cimg_instance,
43101                                         axis);
43102           }
43103         } else { // Direct transform
43104           res = get_haar(axis,false,1);
43105           switch (cimg::lowercase(axis)) {
43106           case 'x' : {
43107             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
43108               res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
43109           } break;
43110           case 'y' : {
43111             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
43112               res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
43113           } break;
43114           case 'z' : {
43115             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
43116               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
43117           } break;
43118           default :
43119             throw CImgArgumentException(_cimg_instance
43120                                         "haar(): Invalid specified axis '%c' "
43121                                         "(should be { x | y | z }).",
43122                                         cimg_instance,
43123                                         axis);
43124           }
43125         }
43126       }
43127       return res;
43128     }
43129 
43130     //! Compute Haar multiscale wavelet transform \overloading.
43131     /**
43132        \param invert Set inverse of direct transform.
43133        \param nb_scales Number of scales used for the transform.
43134     **/
43135     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
43136       return get_haar(invert,nb_scales).move_to(*this);
43137     }
43138 
43139     //! Compute Haar multiscale wavelet transform \newinstance.
43140     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
43141       CImg<Tfloat> res;
43142       if (nb_scales==1) { // Single scale transform
43143         if (_width>1) get_haar('x',invert,1).move_to(res);
43144         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
43145         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
43146         if (res) return res;
43147       } else { // Multi-scale transform
43148         if (invert) { // Inverse transform
43149           res.assign(*this,false);
43150           if (_width>1) {
43151             if (_height>1) {
43152               if (_depth>1) {
43153                 unsigned int w = _width, h = _height, d = _depth;
43154                 for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
43155                 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)
43156                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
43157               } else {
43158                 unsigned int w = _width, h = _height;
43159                 for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
43160                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
43161                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
43162               }
43163             } else {
43164               if (_depth>1) {
43165                 unsigned int w = _width, d = _depth;
43166                 for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
43167                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
43168                   res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
43169               } else {
43170                 unsigned int w = _width;
43171                 for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
43172                 for (w = w?w:1; w<=_width; w*=2)
43173                   res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
43174               }
43175             }
43176           } else {
43177             if (_height>1) {
43178               if (_depth>1) {
43179                 unsigned int h = _height, d = _depth;
43180                 for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
43181                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
43182                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
43183               } else {
43184                 unsigned int h = _height;
43185                 for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
43186                 for (h = h?h:1; h<=_height; h*=2)
43187                   res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
43188               }
43189             } else {
43190               if (_depth>1) {
43191                 unsigned int d = _depth;
43192                 for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
43193                 for (d = d?d:1; d<=_depth; d*=2)
43194                   res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
43195               } else return *this;
43196             }
43197           }
43198         } else { // Direct transform
43199           res = get_haar(false,1);
43200           if (_width>1) {
43201             if (_height>1) {
43202               if (_depth>1)
43203                 for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
43204                      ++s, w/=2, h/=2, d/=2)
43205                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
43206               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
43207                      res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
43208             } else {
43209               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
43210                               res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
43211               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
43212                      res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
43213             }
43214           } else {
43215             if (_height>1) {
43216               if (_depth>1)
43217                 for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
43218                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
43219               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
43220                      res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
43221             } else {
43222               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
43223                               res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
43224               else return *this;
43225             }
43226           }
43227         }
43228         return res;
43229       }
43230       return *this;
43231     }
43232 
43233     //! Compute 1D Fast Fourier Transform, along a specified axis.
43234     /**
43235        \param axis Axis along which the FFT is computed.
43236        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43237     **/
43238     CImgList<Tfloat> get_FFT(const char axis, const bool is_inverse=false) const {
43239       CImgList<Tfloat> res(*this,CImg<Tfloat>());
43240       CImg<Tfloat>::FFT(res[0],res[1],axis,is_inverse);
43241       return res;
43242     }
43243 
43244     //! Compute n-D Fast Fourier Transform.
43245     /*
43246       \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43247     **/
43248     CImgList<Tfloat> get_FFT(const bool is_inverse=false) const {
43249       CImgList<Tfloat> res(*this,CImg<Tfloat>());
43250       CImg<Tfloat>::FFT(res[0],res[1],is_inverse);
43251       return res;
43252     }
43253 
43254     //! Compute 1D Fast Fourier Transform, along a specified axis.
43255     /**
43256        \param[in,out] real Real part of the pixel values.
43257        \param[in,out] imag Imaginary part of the pixel values.
43258        \param axis Axis along which the FFT is computed.
43259        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43260     **/
43261     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_inverse=false,
43262                     const unsigned int nb_threads=0) {
43263       if (!real)
43264         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
43265                                     pixel_type());
43266       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
43267       if (!real.is_sameXYZC(imag))
43268         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
43269                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
43270                                     pixel_type(),
43271                                     real._width,real._height,real._depth,real._spectrum,real._data,
43272                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
43273       const char _axis = cimg::lowercase(axis);
43274       if (_axis!='x' && _axis!='y' && _axis!='z')
43275         throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
43276                                     "(%u,%u,%u,%u) "
43277                                     "(should be { x | y | z }).",
43278                                     pixel_type(),axis,
43279                                     real._width,real._height,real._depth,real._spectrum);
43280       cimg::unused(nb_threads);
43281 #ifdef cimg_use_fftw3
43282       cimg::mutex(12);
43283 #ifndef cimg_use_fftw3_singlethread
43284       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
43285 #endif
43286       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
43287       if (!data_in)
43288         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
43289                                     "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
43290                                     pixel_type(),
43291                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth),
43292                                     real._width,real._height,real._depth,real._spectrum);
43293       double *const ptrf = (double*)data_in;
43294       fftw_plan data_plan =
43295         _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(),
43296                                       data_in,0,1,real.width(),
43297                                       data_in,0,1,real.width(),
43298                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43299         _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(),
43300                                       data_in,0,1,real.height(),
43301                                       data_in,0,1,real.height(),
43302                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43303         fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(),
43304                            data_in,0,1,real.depth(),
43305                            data_in,0,1,real.depth(),
43306                            is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
43307       cimg_forC(real,c) {
43308         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
43309         switch (_axis) {
43310         case 'x' :
43311           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43312             cimg_forXYZ(realc,x,y,z) {
43313             const ulongT
43314               i = realc.offset(x,y,z),
43315               j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height);
43316             ptrf[j] = (double)realc[i];
43317             ptrf[j + 1] = (double)imagc[i];
43318           }
43319           break;
43320         case 'y' :
43321           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43322             cimg_forXYZ(realc,x,y,z) {
43323             const ulongT
43324               i = realc.offset(x,y,z),
43325               j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height);
43326             ptrf[j] = (double)realc[i];
43327             ptrf[j + 1] = (double)imagc[i];
43328           }
43329           break;
43330         default :
43331           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43332             cimg_forXYZ(realc,x,y,z) {
43333             const ulongT
43334               i = realc.offset(x,y,z),
43335               j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth);
43336             ptrf[j] = (double)realc[i];
43337             ptrf[j + 1] = (double)imagc[i];
43338           }
43339         }
43340 
43341         fftw_execute(data_plan);
43342 
43343         const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0;
43344         switch (_axis) {
43345         case 'x' :
43346           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43347             cimg_forXYZ(realc,x,y,z) {
43348             const ulongT
43349               i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height),
43350               j = realc.offset(x,y,z);
43351             realc[j] = (T)(a*ptrf[i]);
43352             imagc[j] = (T)(a*ptrf[i + 1]);
43353           }
43354           break;
43355         case 'y' :
43356           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43357             cimg_forXYZ(realc,x,y,z) {
43358             const ulongT
43359               i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height),
43360               j = realc.offset(x,y,z);
43361             realc[j] = (T)(a*ptrf[i]);
43362             imagc[j] = (T)(a*ptrf[i + 1]);
43363           }
43364           break;
43365         default :
43366           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43367             cimg_forXYZ(realc,x,y,z) {
43368             const ulongT
43369               i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth),
43370               j = realc.offset(x,y,z);
43371             realc[j] = (T)(a*ptrf[i]);
43372             imagc[j] = (T)(a*ptrf[i + 1]);
43373           }
43374         }
43375       }
43376 
43377       fftw_destroy_plan(data_plan);
43378       fftw_free(data_in);
43379 #ifndef cimg_use_fftw3_singlethread
43380       fftw_cleanup_threads();
43381 #endif
43382       cimg::mutex(12,0);
43383 #else
43384       switch (_axis) {
43385       case 'x' : { // Fourier along X, using built-in functions
43386         const unsigned int N = real._width, N2 = N>>1;
43387         if (((N - 1)&N) && N!=1)
43388           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
43389                                       "have non 2^N dimension along the X-axis.",
43390                                       pixel_type(),
43391                                       real._width,real._height,real._depth,real._spectrum);
43392 
43393         for (unsigned int i = 0, j = 0; i<N2; ++i) {
43394           if (j>i) cimg_forYZC(real,y,z,c) {
43395               cimg::swap(real(i,y,z,c),real(j,y,z,c));
43396               cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
43397               if (j<N2) {
43398                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
43399                 cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
43400                 cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
43401               }
43402             }
43403           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
43404         }
43405         for (unsigned int delta = 2; delta<=N; delta<<=1) {
43406           const unsigned int delta2 = delta>>1;
43407           for (unsigned int i = 0; i<N; i+=delta) {
43408             float wr = 1, wi = 0;
43409             const float
43410               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
43411               ca = (float)std::cos(angle),
43412               sa = (float)std::sin(angle);
43413             for (unsigned int k = 0; k<delta2; ++k) {
43414               const unsigned int j = i + k, nj = j + delta2;
43415               cimg_forYZC(real,y,z,c) {
43416                 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);
43417                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
43418                 nir = (T)(ir - tmpr);
43419                 nii = (T)(ii - tmpi);
43420                 ir+=(T)tmpr;
43421                 ii+=(T)tmpi;
43422               }
43423               const float nwr = wr*ca-wi*sa;
43424               wi = wi*ca + wr*sa;
43425               wr = nwr;
43426             }
43427           }
43428         }
43429         if (is_inverse) { real/=N; imag/=N; }
43430       } break;
43431       case 'y' : { // Fourier along Y, using built-in functions
43432         const unsigned int N = real._height, N2 = N>>1;
43433         if (((N - 1)&N) && N!=1)
43434           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
43435                                       "have non 2^N dimension along the Y-axis.",
43436                                       pixel_type(),
43437                                       real._width,real._height,real._depth,real._spectrum);
43438 
43439         for (unsigned int i = 0, j = 0; i<N2; ++i) {
43440           if (j>i) cimg_forXZC(real,x,z,c) {
43441               cimg::swap(real(x,i,z,c),real(x,j,z,c));
43442               cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
43443               if (j<N2) {
43444                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
43445                 cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
43446                 cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
43447               }
43448             }
43449           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
43450         }
43451         for (unsigned int delta = 2; delta<=N; delta<<=1) {
43452           const unsigned int delta2 = (delta>>1);
43453           for (unsigned int i = 0; i<N; i+=delta) {
43454             float wr = 1, wi = 0;
43455             const float
43456               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
43457               ca = (float)std::cos(angle),
43458               sa = (float)std::sin(angle);
43459             for (unsigned int k = 0; k<delta2; ++k) {
43460               const unsigned int j = i + k, nj = j + delta2;
43461               cimg_forXZC(real,x,z,c) {
43462                 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);
43463                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
43464                 nir = (T)(ir - tmpr);
43465                 nii = (T)(ii - tmpi);
43466                 ir+=(T)tmpr;
43467                 ii+=(T)tmpi;
43468               }
43469               const float nwr = wr*ca-wi*sa;
43470               wi = wi*ca + wr*sa;
43471               wr = nwr;
43472             }
43473           }
43474         }
43475         if (is_inverse) { real/=N; imag/=N; }
43476       } break;
43477       default : { // Fourier along Z, using built-in functions
43478         const unsigned int N = real._depth, N2 = N>>1;
43479         if (((N - 1)&N) && N!=1)
43480           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
43481                                       "have non 2^N dimension along the Z-axis.",
43482                                       pixel_type(),
43483                                       real._width,real._height,real._depth,real._spectrum);
43484 
43485         for (unsigned int i = 0, j = 0; i<N2; ++i) {
43486           if (j>i) cimg_forXYC(real,x,y,c) {
43487               cimg::swap(real(x,y,i,c),real(x,y,j,c));
43488               cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
43489               if (j<N2) {
43490                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
43491                 cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
43492                 cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
43493               }
43494             }
43495           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
43496         }
43497         for (unsigned int delta = 2; delta<=N; delta<<=1) {
43498           const unsigned int delta2 = (delta>>1);
43499           for (unsigned int i = 0; i<N; i+=delta) {
43500             float wr = 1, wi = 0;
43501             const float
43502               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
43503               ca = (float)std::cos(angle),
43504               sa = (float)std::sin(angle);
43505             for (unsigned int k = 0; k<delta2; ++k) {
43506               const unsigned int j = i + k, nj = j + delta2;
43507               cimg_forXYC(real,x,y,c) {
43508                 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);
43509                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
43510                 nir = (T)(ir - tmpr);
43511                 nii = (T)(ii - tmpi);
43512                 ir+=(T)tmpr;
43513                 ii+=(T)tmpi;
43514               }
43515               const float nwr = wr*ca-wi*sa;
43516               wi = wi*ca + wr*sa;
43517               wr = nwr;
43518             }
43519           }
43520         }
43521         if (is_inverse) { real/=N; imag/=N; }
43522       } break;
43523       }
43524 #endif
43525     }
43526 
43527     //! Compute n-D Fast Fourier Transform.
43528     /**
43529        \param[in,out] real Real part of the pixel values.
43530        \param[in,out] imag Imaginary part of the pixel values.
43531        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43532        \param nb_threads Number of parallel threads used for the computation.
43533          Use \c 0 to set this to the number of available cpus.
43534     **/
43535     static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_inverse=false,
43536                     const unsigned int nb_threads=0) {
43537       if (!real)
43538         throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
43539                                     pixel_type());
43540       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
43541       if (!real.is_sameXYZC(imag))
43542         throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
43543                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
43544                                     pixel_type(),
43545                                     real._width,real._height,real._depth,real._spectrum,real._data,
43546                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
43547       cimg::unused(nb_threads);
43548 #ifdef cimg_use_fftw3
43549       cimg::mutex(12);
43550 #ifndef cimg_use_fftw3_singlethread
43551       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
43552 #endif
43553       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
43554       if (!data_in)
43555         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
43556                                     "for computing FFT of image (%u,%u,%u,%u).",
43557                                     pixel_type(),
43558                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*
43559                                                         real._height*real._depth*real._spectrum),
43560                                     real._width,real._height,real._depth,real._spectrum);
43561       double *const ptrf = (double*)data_in;
43562       fftw_plan data_plan =
43563         real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in,
43564                                        is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43565         real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in,
43566                                         is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43567         fftw_plan_dft_1d(real._width,data_in,data_in,
43568                          is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
43569       cimg_forC(real,c) {
43570         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
43571         cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43572           cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; }
43573         fftw_execute(data_plan);
43574         if (is_inverse) {
43575           const double a = 1.0/(real.width()*real.height()*real.depth());
43576           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43577             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); }
43578         } else
43579           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43580             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; }
43581       }
43582       fftw_destroy_plan(data_plan);
43583       fftw_free(data_in);
43584 #ifndef cimg_use_fftw3_singlethread
43585       fftw_cleanup_threads();
43586 #endif
43587       cimg::mutex(12,0);
43588 #else
43589       if (real._depth>1) FFT(real,imag,'z',is_inverse);
43590       if (real._height>1) FFT(real,imag,'y',is_inverse);
43591       if (real._width>1) FFT(real,imag,'x',is_inverse);
43592 #endif
43593     }
43594 
43595     //@}
43596     //-------------------------------------
43597     //
43598     //! \name 3D Objects Management
43599     //@{
43600     //-------------------------------------
43601 
43602     //! Rotate 3D object's vertices.
43603     /**
43604        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
43605        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
43606        \param z Z-coordinate of the rotation axis, or second quaternion coordinate.
43607        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
43608        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
43609     **/
43610     CImg<T>& rotate_object3d(const float x, const float y, const float z, const float w,
43611                              const bool is_quaternion=false) {
43612       return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this);
43613     }
43614 
43615     CImg<Tfloat> get_rotate_object3d(const float x, const float y, const float z, const float w,
43616                                      const bool is_quaternion=false) const {
43617       if (_height!=3 || _depth>1 || _spectrum>1)
43618         throw CImgInstanceException(_cimg_instance
43619                                     "rotate_object3d(): Instance is not a set of 3D vertices.",
43620                                     cimg_instance);
43621       return CImg<Tfloat>::rotation_matrix(x,y,z,w,is_quaternion)**this;
43622     }
43623 
43624     //! Shift 3D object's vertices.
43625     /**
43626        \param tx X-coordinate of the 3D displacement vector.
43627        \param ty Y-coordinate of the 3D displacement vector.
43628        \param tz Z-coordinate of the 3D displacement vector.
43629     **/
43630     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
43631       if (_height!=3 || _depth>1 || _spectrum>1)
43632         throw CImgInstanceException(_cimg_instance
43633                                     "shift_object3d(): Instance is not a set of 3D vertices.",
43634                                     cimg_instance);
43635 
43636       get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
43637       return *this;
43638     }
43639 
43640     //! Shift 3D object's vertices \newinstance.
43641     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
43642       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
43643     }
43644 
43645     //! Shift 3D object's vertices, so that it becomes centered.
43646     /**
43647        \note The object center is computed as its barycenter.
43648     **/
43649     CImg<T>& shift_object3d() {
43650       if (_height!=3 || _depth>1 || _spectrum>1)
43651         throw CImgInstanceException(_cimg_instance
43652                                     "shift_object3d(): Instance is not a set of 3D vertices.",
43653                                     cimg_instance);
43654 
43655       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43656       float
43657         xm, xM = (float)xcoords.max_min(xm),
43658         ym, yM = (float)ycoords.max_min(ym),
43659         zm, zM = (float)zcoords.max_min(zm);
43660       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
43661       return *this;
43662     }
43663 
43664     //! Shift 3D object's vertices, so that it becomes centered \newinstance.
43665     CImg<Tfloat> get_shift_object3d() const {
43666       return CImg<Tfloat>(*this,false).shift_object3d();
43667     }
43668 
43669     //! Resize 3D object.
43670     /**
43671        \param sx Width of the 3D object's bounding box.
43672        \param sy Height of the 3D object's bounding box.
43673        \param sz Depth of the 3D object's bounding box.
43674     **/
43675     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
43676       if (_height!=3 || _depth>1 || _spectrum>1)
43677         throw CImgInstanceException(_cimg_instance
43678                                     "resize_object3d(): Instance is not a set of 3D vertices.",
43679                                     cimg_instance);
43680 
43681       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43682       float
43683         xm, xM = (float)xcoords.max_min(xm),
43684         ym, yM = (float)ycoords.max_min(ym),
43685         zm, zM = (float)zcoords.max_min(zm);
43686       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
43687       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
43688       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
43689       return *this;
43690     }
43691 
43692     //! Resize 3D object \newinstance.
43693     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
43694       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
43695     }
43696 
43697     //! Resize 3D object to unit size.
43698     CImg<T> resize_object3d() {
43699       if (_height!=3 || _depth>1 || _spectrum>1)
43700         throw CImgInstanceException(_cimg_instance
43701                                     "resize_object3d(): Instance is not a set of 3D vertices.",
43702                                     cimg_instance);
43703 
43704       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43705       float
43706         xm, xM = (float)xcoords.max_min(xm),
43707         ym, yM = (float)ycoords.max_min(ym),
43708         zm, zM = (float)zcoords.max_min(zm);
43709       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
43710       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
43711       return *this;
43712     }
43713 
43714     //! Resize 3D object to unit size \newinstance.
43715     CImg<Tfloat> get_resize_object3d() const {
43716       return CImg<Tfloat>(*this,false).resize_object3d();
43717     }
43718 
43719     //! Merge two 3D objects together.
43720     /**
43721        \param[in,out] primitives Primitives data of the current 3D object.
43722        \param obj_vertices Vertices data of the additional 3D object.
43723        \param obj_primitives Primitives data of the additional 3D object.
43724     **/
43725     template<typename tf, typename tp, typename tff>
43726     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
43727                              const CImgList<tff>& obj_primitives) {
43728       if (!obj_vertices || !obj_primitives) return *this;
43729       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
43730         throw CImgInstanceException(_cimg_instance
43731                                     "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
43732                                     "set of 3D vertices.",
43733                                     cimg_instance,
43734                                     obj_vertices._width,obj_vertices._height,
43735                                     obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
43736 
43737       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
43738       if (_height!=3 || _depth>1 || _spectrum>1)
43739         throw CImgInstanceException(_cimg_instance
43740                                     "append_object3d(): Instance is not a set of 3D vertices.",
43741                                     cimg_instance);
43742 
43743       const unsigned int P = _width;
43744       append(obj_vertices,'x');
43745       const unsigned int N = primitives._width;
43746       primitives.insert(obj_primitives);
43747       for (unsigned int i = N; i<primitives._width; ++i) {
43748         CImg<tf> &p = primitives[i];
43749         switch (p.size()) {
43750         case 1 : p[0]+=P; break; // Point
43751         case 5 : p[0]+=P; p[1]+=P; break; // Sphere
43752         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment
43753         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle
43754         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle
43755         }
43756       }
43757       return *this;
43758     }
43759 
43760     //! Texturize primitives of a 3D object.
43761     /**
43762        \param[in,out] primitives Primitives data of the 3D object.
43763        \param[in,out] colors Colors data of the 3D object.
43764        \param texture Texture image to map to 3D object.
43765        \param coords Texture-mapping coordinates.
43766     **/
43767     template<typename tp, typename tc, typename tt, typename tx>
43768     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
43769                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
43770       if (is_empty()) return *this;
43771       if (_height!=3)
43772         throw CImgInstanceException(_cimg_instance
43773                                     "texturize_object3d(): image instance is not a set of 3D points.",
43774                                     cimg_instance);
43775       if (coords && (coords._width!=_width || coords._height!=2))
43776         throw CImgArgumentException(_cimg_instance
43777                                     "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
43778                                     cimg_instance,
43779                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
43780       CImg<intT> _coords;
43781       if (!coords) { // If no texture coordinates specified, do a default XY-projection
43782         _coords.assign(_width,2);
43783         float
43784           xmin, xmax = (float)get_shared_row(0).max_min(xmin),
43785           ymin, ymax = (float)get_shared_row(1).max_min(ymin),
43786           dx = xmax>xmin?xmax-xmin:1,
43787           dy = ymax>ymin?ymax-ymin:1;
43788         cimg_forX(*this,p) {
43789           _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
43790           _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
43791         }
43792       } else _coords = coords;
43793 
43794       int texture_ind = -1;
43795       cimglist_for(primitives,l) {
43796         CImg<tp> &p = primitives[l];
43797         const unsigned int siz = p.size();
43798         switch (siz) {
43799         case 1 : { // Point
43800           const unsigned int i0 = (unsigned int)p[0];
43801           const int x0 = _coords(i0,0), y0 = _coords(i0,1);
43802           texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
43803                                 y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
43804         } break;
43805         case 2 : case 6 : { // Line
43806           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
43807           const int
43808             x0 = _coords(i0,0), y0 = _coords(i0,1),
43809             x1 = _coords(i1,0), y1 = _coords(i1,1);
43810           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43811           else colors[l].assign(colors[texture_ind],true);
43812           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
43813         } break;
43814         case 3 : case 9 : { // Triangle
43815           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
43816           const int
43817             x0 = _coords(i0,0), y0 = _coords(i0,1),
43818             x1 = _coords(i1,0), y1 = _coords(i1,1),
43819             x2 = _coords(i2,0), y2 = _coords(i2,1);
43820           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43821           else colors[l].assign(colors[texture_ind],true);
43822           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
43823         } break;
43824         case 4 : case 12 : { // Quadrangle
43825           const unsigned int
43826             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
43827           const int
43828             x0 = _coords(i0,0), y0 = _coords(i0,1),
43829             x1 = _coords(i1,0), y1 = _coords(i1,1),
43830             x2 = _coords(i2,0), y2 = _coords(i2,1),
43831             x3 = _coords(i3,0), y3 = _coords(i3,1);
43832           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43833           else colors[l].assign(colors[texture_ind],true);
43834           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
43835         } break;
43836         }
43837       }
43838       return *this;
43839     }
43840 
43841     //! Generate a 3D elevation of the image instance.
43842     /**
43843        \param[out] primitives The returned list of the 3D object primitives
43844                               (template type \e tf should be at least \e unsigned \e int).
43845        \param[out] colors The returned list of the 3D object colors.
43846        \param elevation The input elevation map.
43847        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
43848        \par Example
43849        \code
43850        const CImg<float> img("reference.jpg");
43851        CImgList<unsigned int> faces3d;
43852        CImgList<unsigned char> colors3d;
43853        const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
43854        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
43855        \endcode
43856        \image html ref_elevation3d.jpg
43857     **/
43858     template<typename tf, typename tc, typename te>
43859     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
43860       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
43861         throw CImgArgumentException(_cimg_instance
43862                                     "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
43863                                     "have incompatible dimensions.",
43864                                     cimg_instance,
43865                                     elevation._width,elevation._height,elevation._depth,
43866                                     elevation._spectrum,elevation._data);
43867       if (is_empty()) return *this;
43868       float m, M = (float)max_min(m);
43869       if (M==m) ++M;
43870       colors.assign();
43871       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
43872       for (unsigned int y = 0; y<size_y1; ++y)
43873         for (unsigned int x = 0; x<size_x1; ++x) {
43874           const unsigned char
43875             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
43876             g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
43877             b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
43878           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
43879         }
43880       const typename CImg<te>::_functor2d_int func(elevation);
43881       return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height);
43882     }
43883 
43884     //! Generate the 3D projection planes of the image instance.
43885     /**
43886        \param[out] primitives Primitives data of the returned 3D object.
43887        \param[out] colors Colors data of the returned 3D object.
43888        \param x0 X-coordinate of the projection point.
43889        \param y0 Y-coordinate of the projection point.
43890        \param z0 Z-coordinate of the projection point.
43891        \param normalize_colors Tells if the created textures have normalized colors.
43892     **/
43893     template<typename tf, typename tc>
43894     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
43895                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
43896                                    const bool normalize_colors=false) const {
43897       float m = 0, M = 0, delta = 1;
43898       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
43899       const unsigned int
43900         _x0 = (x0>=_width)?_width - 1:x0,
43901         _y0 = (y0>=_height)?_height - 1:y0,
43902         _z0 = (z0>=_depth)?_depth - 1:z0;
43903       CImg<tc> img_xy, img_xz, img_yz;
43904       if (normalize_colors) {
43905         ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
43906         ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
43907           move_to(img_xz);
43908         ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
43909           move_to(img_yz);
43910       } else {
43911         get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
43912         get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
43913         get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
43914       }
43915       CImg<floatT> points(12,3,1,1,
43916                           0,_width - 1,_width - 1,0,   0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
43917                           0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0,       0,_height - 1,_height - 1,0,
43918                           _z0,_z0,_z0,_z0,         0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
43919       primitives.assign();
43920       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).
43921         move_to(primitives);
43922       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).
43923         move_to(primitives);
43924       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).
43925         move_to(primitives);
43926       colors.assign();
43927       img_xy.move_to(colors);
43928       img_xz.move_to(colors);
43929       img_yz.move_to(colors);
43930       return points;
43931     }
43932 
43933     //! Generate a isoline of the image instance as a 3D object.
43934     /**
43935        \param[out] primitives The returned list of the 3D object primitives
43936                               (template type \e tf should be at least \e unsigned \e int).
43937        \param isovalue The returned list of the 3D object colors.
43938        \param size_x The number of subdivisions along the X-axis.
43939        \param size_y The number of subdisivions along the Y-axis.
43940        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
43941        \par Example
43942        \code
43943        const CImg<float> img("reference.jpg");
43944        CImgList<unsigned int> faces3d;
43945        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
43946        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
43947        \endcode
43948        \image html ref_isoline3d.jpg
43949     **/
43950     template<typename tf>
43951     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
43952                                const int size_x=-100, const int size_y=-100) const {
43953       if (_spectrum>1)
43954         throw CImgInstanceException(_cimg_instance
43955                                     "get_isoline3d(): Instance is not a scalar image.",
43956                                     cimg_instance);
43957       if (_depth>1)
43958         throw CImgInstanceException(_cimg_instance
43959                                     "get_isoline3d(): Instance is not a 2D image.",
43960                                     cimg_instance);
43961       primitives.assign();
43962       if (is_empty()) return *this;
43963       CImg<floatT> vertices;
43964       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
43965         const _functor2d_int func(*this);
43966         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height());
43967       } else {
43968         const _functor2d_float func(*this);
43969         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y);
43970       }
43971       return vertices;
43972     }
43973 
43974     //! Compute isolines of a function, as a 3D object.
43975     /**
43976        \param[out] primitives Primitives data of the resulting 3D object.
43977        \param func Elevation functor. Must have <tt>operator()(x,y)</tt> defined.
43978        \param isovalue Isovalue to extract from function.
43979        \param x0 X-coordinate of the starting point.
43980        \param y0 Y-coordinate of the starting point.
43981        \param x1 X-coordinate of the ending point.
43982        \param y1 Y-coordinate of the ending point.
43983        \param size_x Resolution of the function along the X-axis.
43984        \param size_y Resolution of the function along the Y-axis.
43985        \note Use the marching squares algorithm for extracting the isolines.
43986      **/
43987     template<typename tf, typename tfunc>
43988     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
43989                                   const float x0, const float y0, const float x1, const float y1,
43990                                   const int size_x=256, const int size_y=256) {
43991       CImgList<floatT> vertices;
43992       primitives.assign();
43993       typename CImg<floatT>::_functor_isoline3d add_vertex(vertices);
43994       typename CImg<tf>::_functor_isoline3d add_segment(primitives);
43995       isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y);
43996       return vertices>'x';
43997     }
43998 
43999     //! Compute isolines of a function, as a 3D object.
44000     /**
44001        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
44002        \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment.
44003        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
44004        \param isovalue Isovalue to extract from function.
44005        \param x0 X-coordinate of the starting point.
44006        \param y0 Y-coordinate of the starting point.
44007        \param x1 X-coordinate of the ending point.
44008        \param y1 Y-coordinate of the ending point.
44009        \param size_x Resolution of the function along the X-axis.
44010        \param size_y Resolution of the function along the Y-axis.
44011        \note Use the marching squares algorithm for extracting the isolines.
44012      **/
44013     template<typename tv, typename tf, typename tfunc>
44014     static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue,
44015                           const float x0, const float y0, const float x1, const float y1,
44016                           const int size_x, const int size_y) {
44017       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
44018                                               0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
44019       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
44020                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
44021                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
44022                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
44023       const unsigned int
44024         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
44025         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
44026         nx = _nx?_nx:1,
44027         ny = _ny?_ny:1,
44028         nxm1 = nx - 1,
44029         nym1 = ny - 1;
44030 
44031       if (!nxm1 || !nym1) return;
44032       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
44033       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
44034       CImg<floatT> values1(nx), values2(nx);
44035       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
44036       int nb_vertices = 0;
44037 
44038       // Fill first line with values
44039       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
44040 
44041       // Run the marching squares algorithm
44042       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
44043         X = x0; nX = X + dx;
44044         indices2.fill(-1);
44045         values2(0) = (float)func(X,nY);
44046         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
44047 
44048           // Determine square configuration
44049           const float
44050             val0 = values1(xi),
44051             val1 = values1(nxi),
44052             val2 = values2(nxi) = (float)func(nX,nY),
44053             val3 = values2(xi);
44054           const unsigned int
44055             configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
44056             (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
44057             edge = edges[configuration];
44058 
44059           // Compute intersection vertices
44060           if (edge) {
44061             if ((edge&1) && indices1(xi,0)<0) {
44062               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
44063               indices1(xi,0) = nb_vertices++;
44064               add_vertex(Xi,Y,0.0f);
44065             }
44066             if ((edge&2) && indices1(nxi,1)<0) {
44067               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
44068               indices1(nxi,1) = nb_vertices++;
44069               add_vertex(nX,Yi,0.0f);
44070             }
44071             if ((edge&4) && indices2(xi,0)<0) {
44072               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
44073               indices2(xi,0) = nb_vertices++;
44074               add_vertex(Xi,nY,0.0f);
44075             }
44076             if ((edge&8) && indices1(xi,1)<0) {
44077               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
44078               indices1(xi,1) = nb_vertices++;
44079               add_vertex(X,Yi,0.0f);
44080             }
44081 
44082             // Create segments
44083             for (const int *segment = segments[configuration]; *segment!=-1; ) {
44084               const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
44085               const int
44086                 i0 = _isoline3d_index(p0,indices1,indices2,xi,nxi),
44087                 i1 = _isoline3d_index(p1,indices1,indices2,xi,nxi);
44088               add_segment(i0,i1);
44089             }
44090           }
44091         }
44092         values1.swap(values2);
44093         indices1.swap(indices2);
44094       }
44095     }
44096 
44097     //! Compute isolines of a function, as a 3D object \overloading.
44098     template<typename tf>
44099     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
44100                                   const float x0, const float y0, const float x1, const float y1,
44101                                   const int size_x=256, const int size_y=256) {
44102       const _functor2d_expr func(expression);
44103       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
44104     }
44105 
44106     template<typename t>
44107     static int _isoline3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
44108                                  const unsigned int x, const unsigned int nx) {
44109       switch (edge) {
44110       case 0 : return (int)indices1(x,0);
44111       case 1 : return (int)indices1(nx,1);
44112       case 2 : return (int)indices2(x,0);
44113       case 3 : return (int)indices1(x,1);
44114       }
44115       return 0;
44116     }
44117 
44118     //! Generate an isosurface of the image instance as a 3D object.
44119     /**
44120        \param[out] primitives The returned list of the 3D object primitives
44121                               (template type \e tf should be at least \e unsigned \e int).
44122        \param isovalue The returned list of the 3D object colors.
44123        \param size_x Number of subdivisions along the X-axis.
44124        \param size_y Number of subdisivions along the Y-axis.
44125        \param size_z Number of subdisivions along the Z-axis.
44126        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44127        \par Example
44128        \code
44129        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
44130        CImgList<unsigned int> faces3d;
44131        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
44132        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
44133        \endcode
44134        \image html ref_isosurface3d.jpg
44135     **/
44136     template<typename tf>
44137     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
44138                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
44139       if (_spectrum>1)
44140         throw CImgInstanceException(_cimg_instance
44141                                     "get_isosurface3d(): Instance is not a scalar image.",
44142                                     cimg_instance);
44143       primitives.assign();
44144       if (is_empty()) return *this;
44145       CImg<floatT> vertices;
44146       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
44147         const _functor3d_int func(*this);
44148         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
44149                                 width(),height(),depth());
44150       } else {
44151         const _functor3d_float func(*this);
44152         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
44153                                 size_x,size_y,size_z);
44154       }
44155       return vertices;
44156     }
44157 
44158     //! Compute isosurface of a function, as a 3D object.
44159     /**
44160        \param[out] primitives Primitives data of the resulting 3D object.
44161        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
44162        \param isovalue Isovalue to extract.
44163        \param x0 X-coordinate of the starting point.
44164        \param y0 Y-coordinate of the starting point.
44165        \param z0 Z-coordinate of the starting point.
44166        \param x1 X-coordinate of the ending point.
44167        \param y1 Y-coordinate of the ending point.
44168        \param z1 Z-coordinate of the ending point.
44169        \param size_x Resolution of the elevation function along the X-axis.
44170        \param size_y Resolution of the elevation function along the Y-axis.
44171        \param size_z Resolution of the elevation function along the Z-axis.
44172        \note Use the marching cubes algorithm for extracting the isosurface.
44173      **/
44174     template<typename tf, typename tfunc>
44175     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
44176                                      const float x0, const float y0, const float z0,
44177                                      const float x1, const float y1, const float z1,
44178                                      const int size_x=32, const int size_y=32, const int size_z=32) {
44179       CImgList<floatT> vertices;
44180       primitives.assign();
44181       typename CImg<floatT>::_functor_isosurface3d add_vertex(vertices);
44182       typename CImg<tf>::_functor_isosurface3d add_triangle(primitives);
44183       isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z);
44184       return vertices>'x';
44185     }
44186 
44187     //! Compute isosurface of a function, as a 3D object.
44188     /**
44189        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
44190        \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment.
44191        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
44192        \param isovalue Isovalue to extract.
44193        \param x0 X-coordinate of the starting point.
44194        \param y0 Y-coordinate of the starting point.
44195        \param z0 Z-coordinate of the starting point.
44196        \param x1 X-coordinate of the ending point.
44197        \param y1 Y-coordinate of the ending point.
44198        \param z1 Z-coordinate of the ending point.
44199        \param size_x Resolution of the elevation function along the X-axis.
44200        \param size_y Resolution of the elevation function along the Y-axis.
44201        \param size_z Resolution of the elevation function along the Z-axis.
44202        \note Use the marching cubes algorithm for extracting the isosurface.
44203      **/
44204     template<typename tv, typename tf, typename tfunc>
44205     static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue,
44206                              const float x0, const float y0, const float z0,
44207                              const float x1, const float y1, const float z1,
44208                              const int size_x, const int size_y, const int size_z) {
44209       static const unsigned int edges[256] = {
44210         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
44211         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
44212         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
44213         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
44214         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
44215         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
44216         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
44217         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
44218         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
44219         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
44220         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
44221         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
44222         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
44223         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
44224         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
44225         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
44226       };
44227 
44228       static const int triangles[256][16] = {
44229         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44230         { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44231         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44232         { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44233         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44234         { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44235         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44236         { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
44237         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44238         { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44239         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44240         { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
44241         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44242         { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
44243         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
44244         { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44245         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44246         { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44247         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44248         { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
44249         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44250         { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
44251         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
44252         { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
44253         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44254         { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
44255         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
44256         { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
44257         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
44258         { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
44259         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
44260         { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
44261         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44262         { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44263         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44264         { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
44265         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44266         { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
44267         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
44268         { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
44269         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44270         { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
44271         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
44272         { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
44273         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
44274         { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
44275         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
44276         { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
44277         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44278         { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
44279         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
44280         { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44281         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
44282         { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
44283         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
44284         { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
44285         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
44286         { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
44287         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
44288         { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
44289         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
44290         { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
44291         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
44292         { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44293         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44294         { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44295         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44296         { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
44297         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44298         { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
44299         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
44300         { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
44301         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44302         { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
44303         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
44304         { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
44305         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
44306         { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
44307         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
44308         { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
44309         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44310         { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
44311         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
44312         { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
44313         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
44314         { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
44315         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
44316         { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
44317         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
44318         { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
44319         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
44320         { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
44321         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
44322         { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
44323         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
44324         { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
44325         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44326         { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
44327         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
44328         { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
44329         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
44330         { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
44331         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44332         { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
44333         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
44334         { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
44335         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
44336         { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
44337         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
44338         { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
44339         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
44340         { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44341         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
44342         { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
44343         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
44344         { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
44345         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
44346         { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
44347         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
44348         { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44349         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
44350         { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
44351         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
44352         { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
44353         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
44354         { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44355         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
44356         { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44357         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44358         { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44359         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44360         { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
44361         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44362         { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
44363         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
44364         { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
44365         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44366         { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
44367         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
44368         { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
44369         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
44370         { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
44371         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
44372         { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
44373         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44374         { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
44375         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
44376         { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
44377         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
44378         { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
44379         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
44380         { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
44381         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
44382         { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44383         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
44384         { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
44385         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
44386         { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
44387         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
44388         { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44389         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44390         { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
44391         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
44392         { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
44393         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
44394         { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
44395         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
44396         { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
44397         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
44398         { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
44399         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
44400         { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
44401         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
44402         { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
44403         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
44404         { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
44405         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
44406         { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
44407         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
44408         { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
44409         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
44410         { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
44411         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
44412         { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
44413         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
44414         { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
44415         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
44416         { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44417         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
44418         { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
44419         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44420         { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44421         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44422         { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
44423         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
44424         { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
44425         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
44426         { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
44427         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
44428         { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
44429         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
44430         { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
44431         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
44432         { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
44433         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44434         { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
44435         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
44436         { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44437         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
44438         { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
44439         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
44440         { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
44441         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
44442         { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
44443         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
44444         { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44445         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
44446         { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
44447         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
44448         { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
44449         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
44450         { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44451         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
44452         { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44453         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
44454         { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
44455         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
44456         { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
44457         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
44458         { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
44459         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
44460         { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
44461         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
44462         { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
44463         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
44464         { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44465         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
44466         { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
44467         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44468         { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44469         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44470         { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
44471         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
44472         { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44473         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
44474         { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
44475         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44476         { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44477         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
44478         { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44479         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
44480         { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44481         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44482         { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44483         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44484         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
44485       };
44486 
44487       const unsigned int
44488         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
44489         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
44490         _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
44491         nx = _nx?_nx:1,
44492         ny = _ny?_ny:1,
44493         nz = _nz?_nz:1,
44494         nxm1 = nx - 1,
44495         nym1 = ny - 1,
44496         nzm1 = nz - 1;
44497       if (!nxm1 || !nym1 || !nzm1) return;
44498       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
44499       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
44500       CImg<floatT> values1(nx,ny), values2(nx,ny);
44501       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
44502       int nb_vertices = 0;
44503 
44504       // Fill the first plane with function values
44505       Y = y0;
44506       cimg_forY(values1,y) {
44507         X = x0;
44508         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
44509         Y+=dy;
44510       }
44511 
44512       // Run Marching Cubes algorithm
44513       Z = z0; nZ = Z + dz;
44514       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
44515         Y = y0; nY = Y + dy;
44516         indices2.fill(-1);
44517         X = x0; for (unsigned int xi = 0; xi<nx; ++xi) { values2(xi,0) = (float)func(X,Y,nZ); X += dx; }
44518 
44519         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
44520           X = x0; nX = X + dx;
44521           values2(0,nyi) = (float)func(X,nY,nZ);
44522 
44523           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
44524 
44525             // Determine cube configuration
44526             const float
44527               val0 = values1(xi,yi),
44528               val1 = values1(nxi,yi),
44529               val2 = values1(nxi,nyi),
44530               val3 = values1(xi,nyi),
44531               val4 = values2(xi,yi),
44532               val5 = values2(nxi,yi),
44533               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
44534               val7 = values2(xi,nyi);
44535 
44536             const unsigned int configuration =
44537               (val0<isovalue?1U:0U)  | (val1<isovalue?2U:0U)  | (val2<isovalue?4U:0U)  | (val3<isovalue?8U:0U) |
44538               (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
44539               edge = edges[configuration];
44540 
44541             // Compute intersection vertices
44542             if (edge) {
44543               if ((edge&1) && indices1(xi,yi,0)<0) {
44544                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
44545                 indices1(xi,yi,0) = nb_vertices++;
44546                 add_vertex(Xi,Y,Z);
44547               }
44548               if ((edge&2) && indices1(nxi,yi,1)<0) {
44549                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
44550                 indices1(nxi,yi,1) = nb_vertices++;
44551                 add_vertex(nX,Yi,Z);
44552               }
44553               if ((edge&4) && indices1(xi,nyi,0)<0) {
44554                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
44555                 indices1(xi,nyi,0) = nb_vertices++;
44556                 add_vertex(Xi,nY,Z);
44557               }
44558               if ((edge&8) && indices1(xi,yi,1)<0) {
44559                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
44560                 indices1(xi,yi,1) = nb_vertices++;
44561                 add_vertex(X,Yi,Z);
44562               }
44563               if ((edge&16) && indices2(xi,yi,0)<0) {
44564                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
44565                 indices2(xi,yi,0) = nb_vertices++;
44566                 add_vertex(Xi,Y,nZ);
44567               }
44568               if ((edge&32) && indices2(nxi,yi,1)<0) {
44569                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
44570                 indices2(nxi,yi,1) = nb_vertices++;
44571                 add_vertex(nX,Yi,nZ);
44572               }
44573               if ((edge&64) && indices2(xi,nyi,0)<0) {
44574                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
44575                 indices2(xi,nyi,0) = nb_vertices++;
44576                 add_vertex(Xi,nY,nZ);
44577               }
44578               if ((edge&128) && indices2(xi,yi,1)<0)  {
44579                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
44580                 indices2(xi,yi,1) = nb_vertices++;
44581                 add_vertex(X,Yi,nZ);
44582               }
44583               if ((edge&256) && indices1(xi,yi,2)<0) {
44584                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
44585                 indices1(xi,yi,2) = nb_vertices++;
44586                 add_vertex(X,Y,Zi);
44587               }
44588               if ((edge&512) && indices1(nxi,yi,2)<0)  {
44589                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
44590                 indices1(nxi,yi,2) = nb_vertices++;
44591                 add_vertex(nX,Y,Zi);
44592               }
44593               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
44594                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
44595                 indices1(nxi,nyi,2) = nb_vertices++;
44596                 add_vertex(nX,nY,Zi);
44597               }
44598               if ((edge&2048) && indices1(xi,nyi,2)<0) {
44599                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
44600                 indices1(xi,nyi,2) = nb_vertices++;
44601                 add_vertex(X,nY,Zi);
44602               }
44603 
44604               // Create triangles
44605               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
44606                 const unsigned int
44607                   p0 = (unsigned int)*(triangle++),
44608                   p1 = (unsigned int)*(triangle++),
44609                   p2 = (unsigned int)*(triangle++);
44610                 const int
44611                   i0 = _isosurface3d_index(p0,indices1,indices2,xi,yi,nxi,nyi),
44612                   i1 = _isosurface3d_index(p1,indices1,indices2,xi,yi,nxi,nyi),
44613                   i2 = _isosurface3d_index(p2,indices1,indices2,xi,yi,nxi,nyi);
44614                 add_triangle(i0,i2,i1);
44615               }
44616             }
44617           }
44618         }
44619         cimg::swap(values1,values2);
44620         cimg::swap(indices1,indices2);
44621       }
44622     }
44623 
44624     //! Compute isosurface of a function, as a 3D object \overloading.
44625     template<typename tf>
44626     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
44627                                      const float x0, const float y0, const float z0,
44628                                      const float x1, const float y1, const float z1,
44629                                      const int dx=32, const int dy=32, const int dz=32) {
44630       const _functor3d_expr func(expression);
44631       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
44632     }
44633 
44634     template<typename t>
44635     static int _isosurface3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
44636                                     const unsigned int x, const unsigned int y,
44637                                     const unsigned int nx, const unsigned int ny) {
44638       switch (edge) {
44639       case 0 : return indices1(x,y,0);
44640       case 1 : return indices1(nx,y,1);
44641       case 2 : return indices1(x,ny,0);
44642       case 3 : return indices1(x,y,1);
44643       case 4 : return indices2(x,y,0);
44644       case 5 : return indices2(nx,y,1);
44645       case 6 : return indices2(x,ny,0);
44646       case 7 : return indices2(x,y,1);
44647       case 8 : return indices1(x,y,2);
44648       case 9 : return indices1(nx,y,2);
44649       case 10 : return indices1(nx,ny,2);
44650       case 11 : return indices1(x,ny,2);
44651       }
44652       return 0;
44653     }
44654 
44655     // Define functors for accessing image values (used in previous functions).
44656     struct _functor2d_int {
44657       const CImg<T>& ref;
44658       _functor2d_int(const CImg<T>& pref):ref(pref) {}
44659       float operator()(const float x, const float y) const {
44660         return (float)ref((int)x,(int)y);
44661       }
44662     };
44663 
44664     struct _functor2d_float {
44665       const CImg<T>& ref;
44666       _functor2d_float(const CImg<T>& pref):ref(pref) {}
44667       float operator()(const float x, const float y) const {
44668         return (float)ref._linear_atXY(x,y);
44669       }
44670     };
44671 
44672     struct _functor2d_expr {
44673       _cimg_math_parser *mp;
44674       ~_functor2d_expr() { mp->end(); delete mp; }
44675       _functor2d_expr(const char *const expr):mp(0) {
44676         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
44677       }
44678       float operator()(const float x, const float y) const {
44679         return (float)(*mp)(x,y,0,0);
44680       }
44681     };
44682 
44683     struct _functor3d_int {
44684       const CImg<T>& ref;
44685       _functor3d_int(const CImg<T>& pref):ref(pref) {}
44686       float operator()(const float x, const float y, const float z) const {
44687         return (float)ref((int)x,(int)y,(int)z);
44688       }
44689     };
44690 
44691     struct _functor3d_float {
44692       const CImg<T>& ref;
44693       _functor3d_float(const CImg<T>& pref):ref(pref) {}
44694       float operator()(const float x, const float y, const float z) const {
44695         return (float)ref._linear_atXYZ(x,y,z);
44696       }
44697     };
44698 
44699     struct _functor3d_expr {
44700       _cimg_math_parser *mp;
44701       ~_functor3d_expr() { mp->end(); delete mp; }
44702       _functor3d_expr(const char *const expr):mp(0) {
44703         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
44704       }
44705       float operator()(const float x, const float y, const float z) const {
44706         return (float)(*mp)(x,y,z,0);
44707       }
44708     };
44709 
44710     struct _functor4d_int {
44711       const CImg<T>& ref;
44712       _functor4d_int(const CImg<T>& pref):ref(pref) {}
44713       float operator()(const float x, const float y, const float z, const unsigned int c) const {
44714         return (float)ref((int)x,(int)y,(int)z,c);
44715       }
44716     };
44717 
44718     struct _functor_isoline3d {
44719       CImgList<T>& list;
44720       _functor_isoline3d(CImgList<T>& _list):list(_list) {}
44721       template<typename t>
44722       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
44723       template<typename t>
44724       void operator()(const t i, const t j) { CImg<T>::vector((T)i,(T)j).move_to(list); }
44725     };
44726 
44727     struct _functor_isosurface3d {
44728       CImgList<T>& list;
44729       _functor_isosurface3d(CImgList<T>& _list):list(_list) {}
44730       template<typename t>
44731       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
44732     };
44733 
44734     //! Compute 3D elevation of a function as a 3D object.
44735     /**
44736        \param[out] primitives Primitives data of the resulting 3D object.
44737        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
44738        \param x0 X-coordinate of the starting point.
44739        \param y0 Y-coordinate of the starting point.
44740        \param x1 X-coordinate of the ending point.
44741        \param y1 Y-coordinate of the ending point.
44742        \param size_x Resolution of the function along the X-axis.
44743        \param size_y Resolution of the function along the Y-axis.
44744     **/
44745     template<typename tf, typename tfunc>
44746     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
44747                                     const float x0, const float y0, const float x1, const float y1,
44748                                     const int size_x=256, const int size_y=256) {
44749       const float
44750         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
44751         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
44752       const unsigned int
44753         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
44754         nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
44755         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
44756         nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
44757       if (nsize_x<2 || nsize_y<2)
44758         throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
44759                                     pixel_type(),
44760                                     nsize_x,nsize_y);
44761 
44762       CImg<floatT> vertices(nsize_x*nsize_y,3);
44763       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
44764       for (unsigned int y = 0; y<nsize_y; ++y) {
44765         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
44766         for (unsigned int x = 0; x<nsize_x; ++x) {
44767           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
44768           *(ptr_x++) = (float)x;
44769           *(ptr_y++) = (float)y;
44770           *(ptr_z++) = (float)func(X,Y);
44771         }
44772       }
44773       primitives.assign(nsize_x1*nsize_y1,1,4);
44774       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
44775         const unsigned int yw = y*nsize_x;
44776         for (unsigned int x = 0; x<nsize_x1; ++x) {
44777           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
44778           primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
44779         }
44780       }
44781       return vertices;
44782     }
44783 
44784     //! Compute 3D elevation of a function, as a 3D object \overloading.
44785     template<typename tf>
44786     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
44787                                     const float x0, const float y0, const float x1, const float y1,
44788                                     const int size_x=256, const int size_y=256) {
44789       const _functor2d_expr func(expression);
44790       return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
44791     }
44792 
44793     //! Generate a 3D box object.
44794     /**
44795        \param[out] primitives The returned list of the 3D object primitives
44796                               (template type \e tf should be at least \e unsigned \e int).
44797        \param size_x The width of the box (dimension along the X-axis).
44798        \param size_y The height of the box (dimension along the Y-axis).
44799        \param size_z The depth of the box (dimension along the Z-axis).
44800        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44801        \par Example
44802        \code
44803        CImgList<unsigned int> faces3d;
44804        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
44805        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
44806        \endcode
44807        \image html ref_box3d.jpg
44808     **/
44809     template<typename tf>
44810     static CImg<floatT> box3d(CImgList<tf>& primitives,
44811                               const float size_x=200, const float size_y=100, const float size_z=100) {
44812       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);
44813       return CImg<floatT>(8,3,1,1,
44814                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
44815                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
44816                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
44817     }
44818 
44819     //! Generate a 3D cone.
44820     /**
44821        \param[out] primitives The returned list of the 3D object primitives
44822                               (template type \e tf should be at least \e unsigned \e int).
44823        \param radius The radius of the cone basis.
44824        \param size_z The cone's height.
44825        \param subdivisions The number of basis angular subdivisions.
44826        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44827        \par Example
44828        \code
44829        CImgList<unsigned int> faces3d;
44830        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
44831        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
44832        \endcode
44833        \image html ref_cone3d.jpg
44834     **/
44835     template<typename tf>
44836     static CImg<floatT> cone3d(CImgList<tf>& primitives,
44837                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
44838       primitives.assign();
44839       if (!subdivisions) return CImg<floatT>();
44840       CImgList<floatT> vertices(2,1,3,1,1,
44841                                 0.,0.,size_z,
44842                                 0.,0.,0.);
44843       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
44844         const float a = (float)(angle*cimg::PI/180);
44845         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
44846       }
44847       const unsigned int nbr = vertices._width - 2;
44848       for (unsigned int p = 0; p<nbr; ++p) {
44849         const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
44850         CImg<tf>::vector(1,next,curr).move_to(primitives);
44851         CImg<tf>::vector(0,curr,next).move_to(primitives);
44852       }
44853       return vertices>'x';
44854     }
44855 
44856     //! Generate a 3D cylinder.
44857     /**
44858        \param[out] primitives The returned list of the 3D object primitives
44859                               (template type \e tf should be at least \e unsigned \e int).
44860        \param radius The radius of the cylinder basis.
44861        \param size_z The cylinder's height.
44862        \param subdivisions The number of basis angular subdivisions.
44863        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44864        \par Example
44865        \code
44866        CImgList<unsigned int> faces3d;
44867        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
44868        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
44869        \endcode
44870        \image html ref_cylinder3d.jpg
44871     **/
44872     template<typename tf>
44873     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
44874                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
44875       primitives.assign();
44876       if (!subdivisions) return CImg<floatT>();
44877       CImgList<floatT> vertices(2,1,3,1,1,
44878                                 0.,0.,0.,
44879                                 0.,0.,size_z);
44880       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
44881         const float a = (float)(angle*cimg::PI/180);
44882         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices);
44883         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
44884       }
44885       const unsigned int nbr = (vertices._width - 2)/2;
44886       for (unsigned int p = 0; p<nbr; ++p) {
44887         const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
44888         CImg<tf>::vector(0,next,curr).move_to(primitives);
44889         CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
44890         CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
44891       }
44892       return vertices>'x';
44893     }
44894 
44895     //! Generate a 3D torus.
44896     /**
44897        \param[out] primitives The returned list of the 3D object primitives
44898                               (template type \e tf should be at least \e unsigned \e int).
44899        \param radius1 The large radius.
44900        \param radius2 The small radius.
44901        \param subdivisions1 The number of angular subdivisions for the large radius.
44902        \param subdivisions2 The number of angular subdivisions for the small radius.
44903        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44904        \par Example
44905        \code
44906        CImgList<unsigned int> faces3d;
44907        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
44908        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
44909        \endcode
44910        \image html ref_torus3d.jpg
44911     **/
44912     template<typename tf>
44913     static CImg<floatT> torus3d(CImgList<tf>& primitives,
44914                                 const float radius1=100, const float radius2=30,
44915                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
44916       primitives.assign();
44917       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
44918       CImgList<floatT> vertices;
44919       for (unsigned int v = 0; v<subdivisions1; ++v) {
44920         const float
44921           beta = (float)(v*2*cimg::PI/subdivisions1),
44922           xc = radius1*(float)std::cos(beta),
44923           yc = radius1*(float)std::sin(beta);
44924         for (unsigned int u = 0; u<subdivisions2; ++u) {
44925           const float
44926             alpha = (float)(u*2*cimg::PI/subdivisions2),
44927             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
44928             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
44929             z = radius2*(float)std::sin(alpha);
44930           CImg<floatT>::vector(x,y,z).move_to(vertices);
44931         }
44932       }
44933       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
44934         const unsigned int nv = (vv + 1)%subdivisions1;
44935         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
44936           const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
44937           CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
44938         }
44939       }
44940       return vertices>'x';
44941     }
44942 
44943     //! Generate a 3D XY-plane.
44944     /**
44945        \param[out] primitives The returned list of the 3D object primitives
44946                               (template type \e tf should be at least \e unsigned \e int).
44947        \param size_x The width of the plane (dimension along the X-axis).
44948        \param size_y The height of the plane (dimensions along the Y-axis).
44949        \param subdivisions_x The number of planar subdivisions along the X-axis.
44950        \param subdivisions_y The number of planar subdivisions along the Y-axis.
44951        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44952        \par Example
44953        \code
44954        CImgList<unsigned int> faces3d;
44955        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
44956        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
44957        \endcode
44958        \image html ref_plane3d.jpg
44959     **/
44960     template<typename tf>
44961     static CImg<floatT> plane3d(CImgList<tf>& primitives,
44962                                 const float size_x=100, const float size_y=100,
44963                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
44964       primitives.assign();
44965       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
44966       CImgList<floatT> vertices;
44967       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
44968       const float fx = (float)size_x/w, fy = (float)size_y/h;
44969       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
44970         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
44971       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
44972         const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
44973         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
44974       }
44975       return vertices>'x';
44976     }
44977 
44978     //! Generate a 3D sphere.
44979     /**
44980        \param[out] primitives The returned list of the 3D object primitives
44981                               (template type \e tf should be at least \e unsigned \e int).
44982        \param radius The radius of the sphere (dimension along the X-axis).
44983        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
44984        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44985        \par Example
44986        \code
44987        CImgList<unsigned int> faces3d;
44988        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
44989        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
44990        \endcode
44991        \image html ref_sphere3d.jpg
44992     **/
44993     template<typename tf>
44994     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
44995                                  const float radius=50, const unsigned int subdivisions=3) {
44996 
44997       // Create initial icosahedron
44998       primitives.assign();
44999       const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a;
45000       CImgList<floatT> vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b,
45001                                 -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a);
45002       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,
45003                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
45004                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
45005       // edge - length/2
45006       float he = (float)a;
45007 
45008       // Recurse subdivisions
45009       for (unsigned int i = 0; i<subdivisions; ++i) {
45010         const unsigned int L = primitives._width;
45011         he/=2;
45012         const float he2 = he*he;
45013         for (unsigned int l = 0; l<L; ++l) {
45014           const unsigned int
45015             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
45016           const float
45017             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
45018             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
45019             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
45020             tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
45021             nn0 = cimg::hypot(tnx0,tny0,tnz0),
45022             tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
45023             nn1 = cimg::hypot(tnx1,tny1,tnz1),
45024             tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
45025             nn2 = cimg::hypot(tnx2,tny2,tnz2),
45026             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
45027             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
45028             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
45029           int i0 = -1, i1 = -1, i2 = -1;
45030           cimglist_for(vertices,p) {
45031             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
45032             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
45033             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
45034             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
45035           }
45036           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
45037           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
45038           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
45039           primitives.remove(0);
45040           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
45041           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
45042           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
45043           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
45044         }
45045       }
45046       return (vertices>'x')*=radius;
45047     }
45048 
45049     //! Generate a 3D ellipsoid.
45050     /**
45051        \param[out] primitives The returned list of the 3D object primitives
45052                               (template type \e tf should be at least \e unsigned \e int).
45053        \param tensor The tensor which gives the shape and size of the ellipsoid.
45054        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
45055        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45056        \par Example
45057        \code
45058        CImgList<unsigned int> faces3d;
45059        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
45060                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
45061        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
45062        \endcode
45063        \image html ref_ellipsoid3d.jpg
45064     **/
45065     template<typename tf, typename t>
45066     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
45067                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
45068       primitives.assign();
45069       if (!subdivisions) return CImg<floatT>();
45070       CImg<floatT> S, V;
45071       tensor.symmetric_eigen(S,V);
45072       const float orient =
45073         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
45074         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
45075         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
45076       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
45077       const float l0 = S[0], l1 = S[1], l2 = S[2];
45078       CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions);
45079       vertices.get_shared_row(0)*=l0;
45080       vertices.get_shared_row(1)*=l1;
45081       vertices.get_shared_row(2)*=l2;
45082       return V*vertices;
45083     }
45084 
45085     //! Convert 3D object into a CImg3d representation.
45086     /**
45087        \param primitives Primitives data of the 3D object.
45088        \param colors Colors data of the 3D object.
45089        \param opacities Opacities data of the 3D object.
45090        \param full_check Tells if full checking of the 3D object must be performed.
45091     **/
45092     template<typename tp, typename tc, typename to>
45093     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45094                               const CImgList<tc>& colors,
45095                               const to& opacities,
45096                               const bool full_check=true) {
45097       return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
45098     }
45099 
45100     //! Convert 3D object into a CImg3d representation \overloading.
45101     template<typename tp, typename tc>
45102     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45103                               const CImgList<tc>& colors,
45104                               const bool full_check=true) {
45105       return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
45106     }
45107 
45108     //! Convert 3D object into a CImg3d representation \overloading.
45109     template<typename tp>
45110     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45111                               const bool full_check=true) {
45112       return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
45113     }
45114 
45115     //! Convert 3D object into a CImg3d representation \overloading.
45116     CImg<T>& object3dtoCImg3d(const bool full_check=true) {
45117       return get_object3dtoCImg3d(full_check).move_to(*this);
45118     }
45119 
45120     //! Convert 3D object into a CImg3d representation \newinstance.
45121     template<typename tp, typename tc, typename to>
45122     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45123                                       const CImgList<tc>& colors,
45124                                       const to& opacities,
45125                                       const bool full_check=true) const {
45126       CImg<charT> error_message(1024);
45127       if (!is_object3d(primitives,colors,opacities,full_check,error_message))
45128         throw CImgInstanceException(_cimg_instance
45129                                     "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).",
45130                                     cimg_instance,_width,primitives._width,error_message.data());
45131       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
45132       float *ptrd = res._data;
45133 
45134       // Put magick number.
45135       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
45136       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
45137 
45138       // Put number of vertices and primitives.
45139       *(ptrd++) = cimg::uint2float(_width);
45140       *(ptrd++) = cimg::uint2float(primitives._width);
45141 
45142       // Put vertex data.
45143       if (is_empty() || !primitives) return res;
45144       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
45145       cimg_forX(*this,p) {
45146         *(ptrd++) = (float)*(ptrx++);
45147         *(ptrd++) = (float)*(ptry++);
45148         *(ptrd++) = (float)*(ptrz++);
45149       }
45150 
45151       // Put primitive data.
45152       cimglist_for(primitives,p) {
45153         *(ptrd++) = (float)primitives[p].size();
45154         const tp *ptrp = primitives[p]._data;
45155         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
45156       }
45157 
45158       // Put color/texture data.
45159       const unsigned int csiz = std::min(colors._width,primitives._width);
45160       for (int c = 0; c<(int)csiz; ++c) {
45161         const CImg<tc>& color = colors[c];
45162         const tc *ptrc = color._data;
45163         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
45164         else {
45165           *(ptrd++) = -128.f;
45166           int shared_ind = -1;
45167           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
45168           if (shared_ind<0) {
45169             *(ptrd++) = (float)color._width;
45170             *(ptrd++) = (float)color._height;
45171             *(ptrd++) = (float)color._spectrum;
45172             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
45173           } else {
45174             *(ptrd++) = (float)shared_ind;
45175             *(ptrd++) = 0;
45176             *(ptrd++) = 0;
45177           }
45178         }
45179       }
45180       const int csiz2 = primitives.width() - colors.width();
45181       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; }
45182 
45183       // Put opacity data.
45184       ptrd = _object3dtoCImg3d(opacities,ptrd);
45185       const float *ptre = res.end();
45186       while (ptrd<ptre) *(ptrd++) = 1.f;
45187       return res;
45188     }
45189 
45190     template<typename to>
45191     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
45192       cimglist_for(opacities,o) {
45193         const CImg<to>& opacity = opacities[o];
45194         const to *ptro = opacity._data;
45195         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
45196         else {
45197           *(ptrd++) = -128.f;
45198           int shared_ind = -1;
45199           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
45200           if (shared_ind<0) {
45201             *(ptrd++) = (float)opacity._width;
45202             *(ptrd++) = (float)opacity._height;
45203             *(ptrd++) = (float)opacity._spectrum;
45204             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
45205           } else {
45206             *(ptrd++) = (float)shared_ind;
45207             *(ptrd++) = 0;
45208             *(ptrd++) = 0;
45209           }
45210         }
45211       }
45212       return ptrd;
45213     }
45214 
45215     template<typename to>
45216     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
45217       const to *ptro = opacities._data;
45218       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
45219       return ptrd;
45220     }
45221 
45222     template<typename tp, typename tc, typename to>
45223     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
45224                                         const CImgList<tc>& colors,
45225                                         const CImgList<to>& opacities) const {
45226       unsigned int siz = 8U + 3*_width;
45227       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
45228       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
45229         if (colors[c].is_shared()) siz+=4;
45230         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
45231       }
45232       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
45233       cimglist_for(opacities,o) {
45234         if (opacities[o].is_shared()) siz+=4;
45235         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
45236       }
45237       siz+=primitives._width - opacities._width;
45238       return siz;
45239     }
45240 
45241     template<typename tp, typename tc, typename to>
45242     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
45243                                         const CImgList<tc>& colors,
45244                                         const CImg<to>& opacities) const {
45245       unsigned int siz = 8U + 3*_width;
45246       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
45247       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
45248         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
45249       }
45250       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
45251       siz+=primitives.size();
45252       cimg::unused(opacities);
45253       return siz;
45254     }
45255 
45256     //! Convert 3D object into a CImg3d representation \overloading.
45257     template<typename tp, typename tc>
45258     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45259                                       const CImgList<tc>& colors,
45260                                       const bool full_check=true) const {
45261       CImgList<T> opacities;
45262       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45263     }
45264 
45265     //! Convert 3D object into a CImg3d representation \overloading.
45266     template<typename tp>
45267     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45268                                       const bool full_check=true) const {
45269       CImgList<T> colors, opacities;
45270       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45271     }
45272 
45273     //! Convert 3D object into a CImg3d representation \overloading.
45274     CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
45275       CImgList<T> opacities, colors;
45276       CImgList<uintT> primitives(width(),1,1,1,1);
45277       cimglist_for(primitives,p) primitives(p,0) = p;
45278       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45279     }
45280 
45281     //! Convert CImg3d representation into a 3D object.
45282     /**
45283        \param[out] primitives Primitives data of the 3D object.
45284        \param[out] colors Colors data of the 3D object.
45285        \param[out] opacities Opacities data of the 3D object.
45286        \param full_check Tells if full checking of the 3D object must be performed.
45287     **/
45288     template<typename tp, typename tc, typename to>
45289     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
45290                               CImgList<tc>& colors,
45291                               CImgList<to>& opacities,
45292                               const bool full_check=true) {
45293       return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
45294     }
45295 
45296     //! Convert CImg3d representation into a 3D object \newinstance.
45297     template<typename tp, typename tc, typename to>
45298     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
45299                                  CImgList<tc>& colors,
45300                                  CImgList<to>& opacities,
45301                                  const bool full_check=true) const {
45302       CImg<charT> error_message(1024);
45303       if (!is_CImg3d(full_check,error_message))
45304         throw CImgInstanceException(_cimg_instance
45305                                     "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
45306                                     cimg_instance,error_message.data());
45307       const T *ptrs = _data + 6;
45308       const unsigned int
45309         nb_points = cimg::float2uint((float)*(ptrs++)),
45310         nb_primitives = cimg::float2uint((float)*(ptrs++));
45311       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
45312       ptrs+=3*nb_points;
45313       primitives.assign(nb_primitives);
45314       cimglist_for(primitives,p) {
45315         const unsigned int nb_inds = (unsigned int)*(ptrs++);
45316         primitives[p].assign(1,nb_inds);
45317         tp *ptrp = primitives[p]._data;
45318         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
45319       }
45320       colors.assign(nb_primitives);
45321       cimglist_for(colors,c) {
45322         if (*ptrs==(T)-128) {
45323           ++ptrs;
45324           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
45325           if (!h && !s) colors[c].assign(colors[w],true);
45326           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
45327         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
45328       }
45329       opacities.assign(nb_primitives);
45330       cimglist_for(opacities,o) {
45331         if (*ptrs==(T)-128) {
45332           ++ptrs;
45333           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
45334           if (!h && !s) opacities[o].assign(opacities[w],true);
45335           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
45336         } else opacities[o].assign(1,1,1,1,*(ptrs++));
45337       }
45338       return points;
45339     }
45340 
45341     //@}
45342     //---------------------------
45343     //
45344     //! \name Drawing Functions
45345     //@{
45346     //---------------------------
45347 
45348 #define cimg_init_scanline(opacity) \
45349     static const T _sc_maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); \
45350     const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \
45351     const ulongT _sc_whd = (ulongT)_width*_height*_depth; \
45352     cimg::unused(_sc_maxval);
45353 
45354 #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
45355     _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval)
45356 
45357     // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
45358     // used only for internal purpose.
45359     // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
45360     template<typename tc>
45361     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
45362                             const tc *const color, const float opacity,
45363                             const float brightness,
45364                             const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) {
45365       const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
45366       if (dx>=0) {
45367         const tc *col = color;
45368         const ulongT off = whd - dx - 1;
45369         T *ptrd = data(nx0,y);
45370         if (opacity>=1) { // ** Opaque drawing **
45371           if (brightness==1) { // Brightness==1
45372             if (sizeof(T)!=1) cimg_forC(*this,c) {
45373                 const T val = (T)*(col++);
45374                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45375                 ptrd+=off;
45376               } else cimg_forC(*this,c) {
45377                 const T val = (T)*(col++);
45378                 std::memset(ptrd,(int)val,dx + 1);
45379                 ptrd+=whd;
45380               }
45381           } else if (brightness<1) { // Brightness<1
45382             if (sizeof(T)!=1) cimg_forC(*this,c) {
45383                 const T val = (T)(*(col++)*brightness);
45384                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45385                 ptrd+=off;
45386               } else cimg_forC(*this,c) {
45387                 const T val = (T)(*(col++)*brightness);
45388                 std::memset(ptrd,(int)val,dx + 1);
45389                 ptrd+=whd;
45390               }
45391           } else { // Brightness>1
45392             if (sizeof(T)!=1) cimg_forC(*this,c) {
45393                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
45394                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45395                 ptrd+=off;
45396               } else cimg_forC(*this,c) {
45397                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
45398                 std::memset(ptrd,(int)val,dx + 1);
45399                 ptrd+=whd;
45400               }
45401           }
45402         } else { // ** Transparent drawing **
45403           if (brightness==1) { // Brightness==1
45404             cimg_forC(*this,c) {
45405               const Tfloat val = *(col++)*nopacity;
45406               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45407               ptrd+=off;
45408             }
45409           } else if (brightness<=1) { // Brightness<1
45410             cimg_forC(*this,c) {
45411               const Tfloat val = *(col++)*brightness*nopacity;
45412               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45413               ptrd+=off;
45414             }
45415           } else { // Brightness>1
45416             cimg_forC(*this,c) {
45417               const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity;
45418               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45419               ptrd+=off;
45420             }
45421           }
45422         }
45423       }
45424       return *this;
45425     }
45426 
45427     //! Draw a 3D point.
45428     /**
45429        \param x0 X-coordinate of the point.
45430        \param y0 Y-coordinate of the point.
45431        \param z0 Z-coordinate of the point.
45432        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45433        \param opacity Drawing opacity.
45434        \note
45435        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
45436        \par Example:
45437        \code
45438        CImg<unsigned char> img(100,100,1,3,0);
45439        const unsigned char color[] = { 255,128,64 };
45440        img.draw_point(50,50,color);
45441        \endcode
45442     **/
45443     template<typename tc>
45444     CImg<T>& draw_point(const int x0, const int y0, const int z0,
45445                         const tc *const color, const float opacity=1) {
45446       if (is_empty()) return *this;
45447       if (!color)
45448         throw CImgArgumentException(_cimg_instance
45449                                     "draw_point(): Specified color is (null).",
45450                                     cimg_instance);
45451       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
45452         const ulongT whd = (ulongT)_width*_height*_depth;
45453         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
45454         T *ptrd = data(x0,y0,z0,0);
45455         const tc *col = color;
45456         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
45457         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
45458       }
45459       return *this;
45460     }
45461 
45462     //! Draw a 2D point \simplification.
45463     template<typename tc>
45464     CImg<T>& draw_point(const int x0, const int y0,
45465                         const tc *const color, const float opacity=1) {
45466       return draw_point(x0,y0,0,color,opacity);
45467     }
45468 
45469     // Draw a points cloud.
45470     /**
45471        \param points Image of vertices coordinates.
45472        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45473        \param opacity Drawing opacity.
45474     **/
45475     template<typename t, typename tc>
45476     CImg<T>& draw_point(const CImg<t>& points,
45477                         const tc *const color, const float opacity=1) {
45478       if (is_empty() || !points) return *this;
45479       switch (points._height) {
45480       case 0 : case 1 :
45481         throw CImgArgumentException(_cimg_instance
45482                                     "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
45483                                     cimg_instance,
45484                                     points._width,points._height,points._depth,points._spectrum,points._data);
45485       case 2 : {
45486         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
45487       } break;
45488       default : {
45489         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
45490       }
45491       }
45492       return *this;
45493     }
45494 
45495     //! Draw a 2D line.
45496     /**
45497        \param x0 X-coordinate of the starting line point.
45498        \param y0 Y-coordinate of the starting line point.
45499        \param x1 X-coordinate of the ending line point.
45500        \param y1 Y-coordinate of the ending line point.
45501        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45502        \param opacity Drawing opacity.
45503        \param pattern An integer whose bits describe the line pattern.
45504        \param init_hatch Tells if a reinitialization of the hash state must be done.
45505        \note
45506        - Line routine uses Bresenham's algorithm.
45507        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
45508        \par Example:
45509        \code
45510        CImg<unsigned char> img(100,100,1,3,0);
45511        const unsigned char color[] = { 255,128,64 };
45512         img.draw_line(40,40,80,70,color);
45513        \endcode
45514     **/
45515     template<typename tc>
45516     CImg<T>& draw_line(int x0, int y0,
45517                        int x1, int y1,
45518                        const tc *const color, const float opacity=1,
45519                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45520       if (is_empty() || !opacity || !pattern ||
45521           std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
45522           std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45523 
45524       int
45525         w1 = width() - 1, h1 = height() - 1,
45526         dx01 = x1 - x0, dy01 = y1 - y0;
45527 
45528       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45529       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45530       if (pattern==~0U && y0>y1) {
45531         cimg::swap(x0,x1,y0,y1);
45532         dx01*=-1; dy01*=-1;
45533       }
45534 
45535       static unsigned int hatch = ~0U - (~0U>>1);
45536       if (init_hatch) hatch = ~0U - (~0U>>1);
45537       cimg_init_scanline(opacity);
45538       const int
45539         step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2,
45540         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45541       dy01+=dy01?0:1;
45542 
45543       for (int y = cy0; y!=cy1; y+=step) {
45544         const int
45545           yy0 = y - y0,
45546           x = x0 + (dx01*yy0 + hdy01)/dy01;
45547         if (x>=0 && x<=w1 && pattern&hatch) {
45548           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45549           cimg_forC(*this,c) {
45550             const T val = color[c];
45551             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45552           }
45553         }
45554         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45555       }
45556       return *this;
45557     }
45558 
45559     //! Draw a 2D line, with z-buffering.
45560     /**
45561        \param zbuffer Zbuffer image.
45562        \param x0 X-coordinate of the starting point.
45563        \param y0 Y-coordinate of the starting point.
45564        \param z0 Z-coordinate of the starting point
45565        \param x1 X-coordinate of the ending point.
45566        \param y1 Y-coordinate of the ending point.
45567        \param z1 Z-coordinate of the ending point.
45568        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45569        \param opacity Drawing opacity.
45570        \param pattern An integer whose bits describe the line pattern.
45571        \param init_hatch Tells if a reinitialization of the hash state must be done.
45572     **/
45573     template<typename tz,typename tc>
45574     CImg<T>& draw_line(CImg<tz>& zbuffer,
45575                        int x0, int y0, const float z0,
45576                        int x1, int y1, const float z1,
45577                        const tc *const color, const float opacity=1,
45578                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45579       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45580       if (!color)
45581         throw CImgArgumentException(_cimg_instance
45582                                     "draw_line(): Specified color is (null).",
45583                                     cimg_instance);
45584       if (!is_sameXY(zbuffer))
45585         throw CImgArgumentException(_cimg_instance
45586                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45587                                     "different dimensions.",
45588                                     cimg_instance,
45589                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45590 
45591       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45592 
45593       float iz0 = 1/z0, iz1 = 1/z1;
45594       int
45595         w1 = width() - 1, h1 = height() - 1,
45596         dx01 = x1 - x0, dy01 = y1 - y0;
45597       float diz01 = iz1 - iz0;
45598 
45599       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45600       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45601       if (pattern==~0U && y0>y1) {
45602         cimg::swap(x0,x1,y0,y1,iz0,iz1);
45603         dx01*=-1; dy01*=-1; diz01*=-1;
45604       }
45605 
45606       static unsigned int hatch = ~0U - (~0U>>1);
45607       if (init_hatch) hatch = ~0U - (~0U>>1);
45608       cimg_init_scanline(opacity);
45609 
45610       const int
45611         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45612         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45613       dy01+=dy01?0:1;
45614 
45615       for (int y = cy0; y!=cy1; y+=step) {
45616         const int
45617           yy0 = y - y0,
45618           x = x0 + (dx01*yy0 + hdy01)/dy01;
45619         const float iz = iz0 + diz01*yy0/dy01;
45620         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
45621 
45622         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
45623           *ptrz = (tz)iz;
45624           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45625           cimg_forC(*this,c) {
45626             const T val = color[c];
45627             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45628           }
45629         }
45630         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45631       }
45632       return *this;
45633     }
45634 
45635     //! Draw a textured 2D line.
45636     /**
45637        \param x0 X-coordinate of the starting line point.
45638        \param y0 Y-coordinate of the starting line point.
45639        \param x1 X-coordinate of the ending line point.
45640        \param y1 Y-coordinate of the ending line point.
45641        \param texture Texture image defining the pixel colors.
45642        \param tx0 X-coordinate of the starting texture point.
45643        \param ty0 Y-coordinate of the starting texture point.
45644        \param tx1 X-coordinate of the ending texture point.
45645        \param ty1 Y-coordinate of the ending texture point.
45646        \param opacity Drawing opacity.
45647        \param pattern An integer whose bits describe the line pattern.
45648        \param init_hatch Tells if the hash variable must be reinitialized.
45649        \note
45650        - Line routine uses the well known Bresenham's algorithm.
45651        \par Example:
45652        \code
45653        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
45654        const unsigned char color[] = { 255,128,64 };
45655        img.draw_line(40,40,80,70,texture,0,0,255,255);
45656        \endcode
45657     **/
45658     template<typename tc>
45659     CImg<T>& draw_line(int x0, int y0,
45660                        int x1, int y1,
45661                        const CImg<tc>& texture,
45662                        int tx0, int ty0,
45663                        int tx1, int ty1,
45664                        const float opacity=1,
45665                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45666 
45667       if (is_empty() || !opacity || !pattern) return *this;
45668       if (texture._depth>1 || texture._spectrum<_spectrum)
45669         throw CImgArgumentException(_cimg_instance
45670                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45671                                     cimg_instance,
45672                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45673       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45674 
45675       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45676 
45677       int
45678         w1 = width() - 1, h1 = height() - 1,
45679         dx01 = x1 - x0, dy01 = y1 - y0;
45680       int
45681         dtx01 = tx1 - tx0, dty01 = ty1 - ty0;
45682 
45683       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45684       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45685       if (pattern==~0U && y0>y1) {
45686         cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
45687         dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1;
45688       }
45689 
45690       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45691       static unsigned int hatch = ~0U - (~0U>>1);
45692       if (init_hatch) hatch = ~0U - (~0U>>1);
45693       cimg_init_scanline(opacity);
45694 
45695       const int
45696         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45697         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2,
45698         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45699       dy01+=dy01?0:1;
45700 
45701       for (int y = cy0; y!=cy1; y+=step) {
45702         const int
45703           yy0 = y - y0,
45704           x = x0 + (dx01*yy0 + hdy01)/dy01,
45705           tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01,
45706           ty = ty0 + (dty01*yy0 + hdy01ty)/dy01;
45707         if (x>=0 && x<=w1 && pattern&hatch) {
45708           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45709           const tc *const color = &texture._atXY(tx,ty);
45710           cimg_forC(*this,c) {
45711             const T val = color[c*twhd];
45712             ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45713           }
45714         }
45715         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45716       }
45717       return *this;
45718     }
45719 
45720     //! Draw a textured 2D line, with perspective correction.
45721     /**
45722        \param x0 X-coordinate of the starting point.
45723        \param y0 Y-coordinate of the starting point.
45724        \param z0 Z-coordinate of the starting point
45725        \param x1 X-coordinate of the ending point.
45726        \param y1 Y-coordinate of the ending point.
45727        \param z1 Z-coordinate of the ending point.
45728        \param texture Texture image defining the pixel colors.
45729        \param tx0 X-coordinate of the starting texture point.
45730        \param ty0 Y-coordinate of the starting texture point.
45731        \param tx1 X-coordinate of the ending texture point.
45732        \param ty1 Y-coordinate of the ending texture point.
45733        \param opacity Drawing opacity.
45734        \param pattern An integer whose bits describe the line pattern.
45735        \param init_hatch Tells if the hash variable must be reinitialized.
45736     **/
45737     template<typename tc>
45738     CImg<T>& draw_line(int x0, int y0, const float z0,
45739                        int x1, int y1, const float z1,
45740                        const CImg<tc>& texture,
45741                        const int tx0, const int ty0,
45742                        const int tx1, const int ty1,
45743                        const float opacity=1,
45744                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45745       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45746       if (texture._depth>1 || texture._spectrum<_spectrum)
45747         throw CImgArgumentException(_cimg_instance
45748                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45749                                     cimg_instance,
45750                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45751       if (is_overlapped(texture))
45752         return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45753 
45754       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45755 
45756       float iz0 = 1/z0, iz1 = 1/z1;
45757       int
45758         w1 = width() - 1, h1 = height() - 1,
45759         dx01 = x1 - x0, dy01 = y1 - y0;
45760       float
45761         diz01 = iz1 - iz0,
45762         txz0 = tx0*iz0, txz1 = tx1*iz1,
45763         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
45764         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
45765 
45766       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45767       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45768       if (pattern==~0U && y0>y1) {
45769         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
45770         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
45771       }
45772 
45773       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45774       static unsigned int hatch = ~0U - (~0U>>1);
45775       if (init_hatch) hatch = ~0U - (~0U>>1);
45776       cimg_init_scanline(opacity);
45777 
45778       const int
45779         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45780         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45781       dy01+=dy01?0:1;
45782 
45783       for (int y = cy0; y!=cy1; y+=step) {
45784         const int
45785           yy0 = y - y0,
45786           x = x0 + (dx01*yy0 + hdy01)/dy01;
45787         const float
45788           iz = iz0 + diz01*yy0/dy01,
45789           txz = txz0 + dtxz01*yy0/dy01,
45790           tyz = tyz0 + dtyz01*yy0/dy01;
45791         if (x>=0 && x<=w1 && pattern&hatch) {
45792           const int
45793             tx = (int)cimg::round(txz/iz),
45794             ty = (int)cimg::round(tyz/iz);
45795           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45796           const tc *const color = &texture._atXY(tx,ty);
45797           cimg_forC(*this,c) {
45798             const T val = color[c*twhd];
45799             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45800           }
45801         }
45802         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45803       }
45804       return *this;
45805     }
45806 
45807     //! Draw a textured 2D line, with perspective correction and z-buffering.
45808     /**
45809        \param zbuffer Z-buffer image.
45810        \param x0 X-coordinate of the starting point.
45811        \param y0 Y-coordinate of the starting point.
45812        \param z0 Z-coordinate of the starting point
45813        \param x1 X-coordinate of the ending point.
45814        \param y1 Y-coordinate of the ending point.
45815        \param z1 Z-coordinate of the ending point.
45816        \param texture Texture image defining the pixel colors.
45817        \param tx0 X-coordinate of the starting texture point.
45818        \param ty0 Y-coordinate of the starting texture point.
45819        \param tx1 X-coordinate of the ending texture point.
45820        \param ty1 Y-coordinate of the ending texture point.
45821        \param opacity Drawing opacity.
45822        \param pattern An integer whose bits describe the line pattern.
45823        \param init_hatch Tells if the hash variable must be reinitialized.
45824     **/
45825     template<typename tz, typename tc>
45826     CImg<T>& draw_line(CImg<tz>& zbuffer,
45827                        int x0, int y0, const float z0,
45828                        int x1, int y1, const float z1,
45829                        const CImg<tc>& texture,
45830                        const int tx0, const int ty0,
45831                        const int tx1, const int ty1,
45832                        const float opacity=1,
45833                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45834       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45835       if (!is_sameXY(zbuffer))
45836         throw CImgArgumentException(_cimg_instance
45837                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45838                                     "different dimensions.",
45839                                     cimg_instance,
45840                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45841       if (texture._depth>1 || texture._spectrum<_spectrum)
45842         throw CImgArgumentException(_cimg_instance
45843                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45844                                     cimg_instance,
45845                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45846       if (is_overlapped(texture))
45847         return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45848 
45849       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45850 
45851       float iz0 = 1/z0, iz1 = 1/z1;
45852       int
45853         w1 = width() - 1, h1 = height() - 1,
45854         dx01 = x1 - x0, dy01 = y1 - y0;
45855       float
45856         diz01 = iz1 - iz0,
45857         txz0 = tx0*iz0, txz1 = tx1*iz1,
45858         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
45859         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
45860 
45861       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45862       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45863       if (pattern==~0U && y0>y1) {
45864         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
45865         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
45866       }
45867 
45868       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45869       static unsigned int hatch = ~0U - (~0U>>1);
45870       if (init_hatch) hatch = ~0U - (~0U>>1);
45871       cimg_init_scanline(opacity);
45872 
45873       const int
45874         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45875         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45876       dy01+=dy01?0:1;
45877 
45878       for (int y = cy0; y!=cy1; y+=step) {
45879         const int
45880           yy0 = y - y0,
45881           x = x0 + (dx01*yy0 + hdy01)/dy01;
45882         const float
45883           iz = iz0 + diz01*yy0/dy01,
45884           txz = txz0 + dtxz01*yy0/dy01,
45885           tyz = tyz0 + dtyz01*yy0/dy01;
45886         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
45887 
45888         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
45889           *ptrz = (tz)iz;
45890           const int
45891             tx = (int)cimg::round(txz/iz),
45892             ty = (int)cimg::round(tyz/iz);
45893           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45894           const tc *const color = &texture._atXY(tx,ty);
45895           cimg_forC(*this,c) {
45896             const T val = color[c*twhd];
45897             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45898           }
45899         }
45900         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45901       }
45902       return *this;
45903     }
45904 
45905     //! Draw a set of consecutive lines.
45906     /**
45907        \param points Coordinates of vertices, stored as a list of vectors.
45908        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45909        \param opacity Drawing opacity.
45910        \param pattern An integer whose bits describe the line pattern.
45911        \param init_hatch If set to true, init hatch motif.
45912        \note
45913        - This function uses several call to the single CImg::draw_line() procedure,
45914        depending on the vectors size in \p points.
45915     **/
45916     template<typename t, typename tc>
45917     CImg<T>& draw_line(const CImg<t>& points,
45918                        const tc *const color, const float opacity=1,
45919                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45920       if (is_empty() || !points || points._width<2) return *this;
45921       bool ninit_hatch = init_hatch;
45922       switch (points._height) {
45923       case 0 : case 1 :
45924         throw CImgArgumentException(_cimg_instance
45925                                     "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
45926                                     cimg_instance,
45927                                     points._width,points._height,points._depth,points._spectrum,points._data);
45928 
45929       default : {
45930         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
45931         int ox = x0, oy = y0;
45932         for (unsigned int i = 1; i<points._width; ++i) {
45933           const int x = (int)points(i,0), y = (int)points(i,1);
45934           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
45935           ninit_hatch = false;
45936           ox = x; oy = y;
45937         }
45938       }
45939       }
45940       return *this;
45941     }
45942 
45943     //! Draw a 2D arrow.
45944     /**
45945        \param x0 X-coordinate of the starting arrow point (tail).
45946        \param y0 Y-coordinate of the starting arrow point (tail).
45947        \param x1 X-coordinate of the ending arrow point (head).
45948        \param y1 Y-coordinate of the ending arrow point (head).
45949        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45950        \param angle Aperture angle of the arrow head.
45951        \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
45952        \param opacity Drawing opacity.
45953        \param pattern An integer whose bits describe the line pattern.
45954     **/
45955     template<typename tc>
45956     CImg<T>& draw_arrow(const int x0, const int y0,
45957                         const int x1, const int y1,
45958                         const tc *const color, const float opacity=1,
45959                         const float angle=30, const float length=-10,
45960                         const unsigned int pattern=~0U) {
45961       if (is_empty()) return *this;
45962       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
45963         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f,
45964         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
45965       if (sq>0) {
45966         const float
45967             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
45968             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
45969         const int
45970           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
45971           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
45972           xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
45973         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
45974       } else draw_point(x0,y0,color,opacity);
45975       return *this;
45976     }
45977 
45978     //! Draw a 2D spline.
45979     /**
45980        \param x0 X-coordinate of the starting curve point
45981        \param y0 Y-coordinate of the starting curve point
45982        \param u0 X-coordinate of the starting velocity
45983        \param v0 Y-coordinate of the starting velocity
45984        \param x1 X-coordinate of the ending curve point
45985        \param y1 Y-coordinate of the ending curve point
45986        \param u1 X-coordinate of the ending velocity
45987        \param v1 Y-coordinate of the ending velocity
45988        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45989        \param precision Curve drawing precision.
45990        \param opacity Drawing opacity.
45991        \param pattern An integer whose bits describe the line pattern.
45992        \param init_hatch If \c true, init hatch motif.
45993        \note
45994        - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points
45995        and corresponding velocity vectors.
45996        - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the
45997        average number of pixels in each drawn segment.
45998        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
45999          (\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
46000          and (\p xa,\p ya), (\p xb,\p yb) are two
46001        \e control points.
46002        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
46003        the control points as
46004        \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).
46005        \par Example:
46006        \code
46007        CImg<unsigned char> img(100,100,1,3,0);
46008        const unsigned char color[] = { 255,255,255 };
46009        img.draw_spline(30,30,0,100,90,40,0,-100,color);
46010        \endcode
46011     **/
46012     template<typename tc>
46013     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
46014                          const int x1, const int y1, const float u1, const float v1,
46015                          const tc *const color, const float opacity=1,
46016                          const float precision=0.25, const unsigned int pattern=~0U,
46017                          const bool init_hatch=true) {
46018       if (is_empty()) return *this;
46019       if (!color)
46020         throw CImgArgumentException(_cimg_instance
46021                                     "draw_spline(): Specified color is (null).",
46022                                     cimg_instance);
46023       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
46024       bool ninit_hatch = init_hatch;
46025       const float
46026         ax = u0 + u1 + 2*(x0 - x1),
46027         bx = 3*(x1 - x0) - 2*u0 - u1,
46028         ay = v0 + v1 + 2*(y0 - y1),
46029         by = 3*(y1 - y0) - 2*v0 - v1,
46030         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
46031       int ox = x0, oy = y0;
46032       for (float t = 0; t<1; t+=_precision) {
46033         const float t2 = t*t, t3 = t2*t;
46034         const int
46035           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
46036           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
46037         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
46038         ninit_hatch = false;
46039         ox = nx; oy = ny;
46040       }
46041       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
46042     }
46043 
46044     //! Draw a textured 2D spline.
46045     /**
46046        \param x0 X-coordinate of the starting curve point
46047        \param y0 Y-coordinate of the starting curve point
46048        \param u0 X-coordinate of the starting velocity
46049        \param v0 Y-coordinate of the starting velocity
46050        \param x1 X-coordinate of the ending curve point
46051        \param y1 Y-coordinate of the ending curve point
46052        \param u1 X-coordinate of the ending velocity
46053        \param v1 Y-coordinate of the ending velocity
46054        \param texture Texture image defining line pixel colors.
46055        \param tx0 X-coordinate of the starting texture point.
46056        \param ty0 Y-coordinate of the starting texture point.
46057        \param tx1 X-coordinate of the ending texture point.
46058        \param ty1 Y-coordinate of the ending texture point.
46059        \param precision Curve drawing precision.
46060        \param opacity Drawing opacity.
46061        \param pattern An integer whose bits describe the line pattern.
46062        \param init_hatch if \c true, reinit hatch motif.
46063     **/
46064     template<typename t>
46065     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
46066                          const int x1, const int y1, const float u1, const float v1,
46067                          const CImg<t>& texture,
46068                          const int tx0, const int ty0, const int tx1, const int ty1,
46069                          const float opacity=1,
46070                          const float precision=4, const unsigned int pattern=~0U,
46071                          const bool init_hatch=true) {
46072       if (texture._depth>1 || texture._spectrum<_spectrum)
46073         throw CImgArgumentException(_cimg_instance
46074                                     "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
46075                                     cimg_instance,
46076                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46077       if (is_empty()) return *this;
46078       if (is_overlapped(texture))
46079         return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
46080       if (x0==x1 && y0==y1)
46081         return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
46082                                                       y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(),
46083                           opacity);
46084       bool ninit_hatch = init_hatch;
46085       const float
46086         ax = u0 + u1 + 2*(x0 - x1),
46087         bx = 3*(x1 - x0) - 2*u0 - u1,
46088         ay = v0 + v1 + 2*(y0 - y1),
46089         by = 3*(y1 - y0) - 2*v0 - v1,
46090         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
46091       int ox = x0, oy = y0, otx = tx0, oty = ty0;
46092       for (float t1 = 0; t1<1; t1+=_precision) {
46093         const float t2 = t1*t1, t3 = t2*t1;
46094         const int
46095           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
46096           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
46097           ntx = tx0 + (int)((tx1 - tx0)*t1),
46098           nty = ty0 + (int)((ty1 - ty0)*t1);
46099         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
46100         ninit_hatch = false;
46101         ox = nx; oy = ny; otx = ntx; oty = nty;
46102       }
46103       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
46104     }
46105 
46106     //! Draw a set of consecutive splines.
46107     /**
46108        \param points Vertices data.
46109        \param tangents Tangents data.
46110        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46111        \param opacity Drawing opacity.
46112        \param is_closed_set Tells if the drawn spline set is closed.
46113        \param precision Precision of the drawing.
46114        \param pattern An integer whose bits describe the line pattern.
46115        \param init_hatch If \c true, init hatch motif.
46116     **/
46117     template<typename tp, typename tt, typename tc>
46118     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
46119                          const tc *const color, const float opacity=1,
46120                          const bool is_closed_set=false, const float precision=4,
46121                          const unsigned int pattern=~0U, const bool init_hatch=true) {
46122       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
46123       bool ninit_hatch = init_hatch;
46124       switch (points._height) {
46125       case 0 : case 1 :
46126         throw CImgArgumentException(_cimg_instance
46127                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
46128                                     cimg_instance,
46129                                     points._width,points._height,points._depth,points._spectrum,points._data);
46130 
46131       default : {
46132         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
46133         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
46134         int ox = x0, oy = y0;
46135         float ou = u0, ov = v0;
46136         for (unsigned int i = 1; i<points._width; ++i) {
46137           const int x = (int)points(i,0), y = (int)points(i,1);
46138           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
46139           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
46140           ninit_hatch = false;
46141           ox = x; oy = y; ou = u; ov = v;
46142         }
46143         if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
46144       }
46145       }
46146       return *this;
46147     }
46148 
46149     //! Draw a set of consecutive splines \overloading.
46150     /**
46151        Similar to previous function, with the point tangents automatically estimated from the given points set.
46152     **/
46153     template<typename tp, typename tc>
46154     CImg<T>& draw_spline(const CImg<tp>& points,
46155                          const tc *const color, const float opacity=1,
46156                          const bool is_closed_set=false, const float precision=4,
46157                          const unsigned int pattern=~0U, const bool init_hatch=true) {
46158       if (is_empty() || !points || points._width<2) return *this;
46159       CImg<Tfloat> tangents;
46160       switch (points._height) {
46161       case 0 : case 1 :
46162         throw CImgArgumentException(_cimg_instance
46163                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
46164                                     cimg_instance,
46165                                     points._width,points._height,points._depth,points._spectrum,points._data);
46166       case 2 : {
46167         tangents.assign(points._width,points._height);
46168         cimg_forX(points,p) {
46169           const unsigned int
46170             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
46171             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
46172           const float
46173             x = (float)points(p,0),
46174             y = (float)points(p,1),
46175             x0 = (float)points(p0,0),
46176             y0 = (float)points(p0,1),
46177             x1 = (float)points(p1,0),
46178             y1 = (float)points(p1,1),
46179             u0 = x - x0,
46180             v0 = y - y0,
46181             n0 = 1e-8f + cimg::hypot(u0,v0),
46182             u1 = x1 - x,
46183             v1 = y1 - y,
46184             n1 = 1e-8f + cimg::hypot(u1,v1),
46185             u = u0/n0 + u1/n1,
46186             v = v0/n0 + v1/n1,
46187             n = 1e-8f + cimg::hypot(u,v),
46188             fact = 0.5f*(n0 + n1);
46189           tangents(p,0) = (Tfloat)(fact*u/n);
46190           tangents(p,1) = (Tfloat)(fact*v/n);
46191         }
46192       } break;
46193       default : {
46194         tangents.assign(points._width,points._height);
46195         cimg_forX(points,p) {
46196           const unsigned int
46197             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
46198             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
46199           const float
46200             x = (float)points(p,0),
46201             y = (float)points(p,1),
46202             z = (float)points(p,2),
46203             x0 = (float)points(p0,0),
46204             y0 = (float)points(p0,1),
46205             z0 = (float)points(p0,2),
46206             x1 = (float)points(p1,0),
46207             y1 = (float)points(p1,1),
46208             z1 = (float)points(p1,2),
46209             u0 = x - x0,
46210             v0 = y - y0,
46211             w0 = z - z0,
46212             n0 = 1e-8f + cimg::hypot(u0,v0,w0),
46213             u1 = x1 - x,
46214             v1 = y1 - y,
46215             w1 = z1 - z,
46216             n1 = 1e-8f + cimg::hypot(u1,v1,w1),
46217             u = u0/n0 + u1/n1,
46218             v = v0/n0 + v1/n1,
46219             w = w0/n0 + w1/n1,
46220             n = 1e-8f + cimg::hypot(u,v,w),
46221             fact = 0.5f*(n0 + n1);
46222           tangents(p,0) = (Tfloat)(fact*u/n);
46223           tangents(p,1) = (Tfloat)(fact*v/n);
46224           tangents(p,2) = (Tfloat)(fact*w/n);
46225         }
46226       }
46227       }
46228       return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
46229     }
46230 
46231     // [internal] Draw a filled triangle.
46232     template<typename tc>
46233     CImg<T>& _draw_triangle(int x0, int y0,
46234                             int x1, int y1,
46235                             int x2, int y2,
46236                             const tc *const color, const float opacity,
46237                             const float brightness) {
46238       if (y0>y1) cimg::swap(x0,x1,y0,y1);
46239       if (y0>y2) cimg::swap(x0,x2,y0,y2);
46240       if (y1>y2) cimg::swap(x1,x2,y1,y2);
46241       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46242 
46243       const int
46244         h1 = height() - 1,
46245         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46246         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46247         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46248         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46249 
46250       const float cbs = cimg::cut(brightness,0,2);
46251       cimg_init_scanline(opacity);
46252 
46253       for (int y = cy0; y<=cy2; ++y) {
46254         const int yy0 = y - y0, yy1 = y - y1;
46255         int
46256           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46257           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46258         if (xm>xM) cimg::swap(xm,xM);
46259         cimg_draw_scanline(xm,xM,y,color,opacity,cbs);
46260       }
46261       return *this;
46262     }
46263 
46264     //! Draw a filled 2D triangle.
46265     /**
46266        \param x0 X-coordinate of the first vertex.
46267        \param y0 Y-coordinate of the first vertex.
46268        \param x1 X-coordinate of the second vertex.
46269        \param y1 Y-coordinate of the second vertex.
46270        \param x2 X-coordinate of the third vertex.
46271        \param y2 Y-coordinate of the third vertex.
46272        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46273        \param opacity Drawing opacity.
46274      **/
46275     template<typename tc>
46276     CImg<T>& draw_triangle(const int x0, const int y0,
46277                            const int x1, const int y1,
46278                            const int x2, const int y2,
46279                            const tc *const color, const float opacity=1) {
46280       if (is_empty()) return *this;
46281       if (!color)
46282         throw CImgArgumentException(_cimg_instance
46283                                     "draw_triangle(): Specified color is (null).",
46284                                     cimg_instance);
46285       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
46286       return *this;
46287     }
46288 
46289     //! Draw a outlined 2D triangle.
46290     /**
46291        \param x0 X-coordinate of the first vertex.
46292        \param y0 Y-coordinate of the first vertex.
46293        \param x1 X-coordinate of the second vertex.
46294        \param y1 Y-coordinate of the second vertex.
46295        \param x2 X-coordinate of the third vertex.
46296        \param y2 Y-coordinate of the third vertex.
46297        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46298        \param opacity Drawing opacity.
46299        \param pattern An integer whose bits describe the outline pattern.
46300      **/
46301     template<typename tc>
46302     CImg<T>& draw_triangle(const int x0, const int y0,
46303                            const int x1, const int y1,
46304                            const int x2, const int y2,
46305                            const tc *const color, const float opacity,
46306                            const unsigned int pattern) {
46307       if (is_empty()) return *this;
46308       if (!color)
46309         throw CImgArgumentException(_cimg_instance
46310                                     "draw_triangle(): Specified color is (null).",
46311                                     cimg_instance);
46312       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
46313         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
46314         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
46315       return *this;
46316     }
46317 
46318     //! Draw a filled 2D triangle, with z-buffering.
46319     /**
46320        \param zbuffer Z-buffer image.
46321        \param x0 X-coordinate of the first vertex.
46322        \param y0 Y-coordinate of the first vertex.
46323        \param z0 Z-coordinate of the first vertex.
46324        \param x1 X-coordinate of the second vertex.
46325        \param y1 Y-coordinate of the second vertex.
46326        \param z1 Z-coordinate of the second vertex.
46327        \param x2 X-coordinate of the third vertex.
46328        \param y2 Y-coordinate of the third vertex.
46329        \param z2 Z-coordinate of the third vertex.
46330        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46331        \param opacity Drawing opacity.
46332        \param brightness Brightness factor.
46333     **/
46334     template<typename tz, typename tc>
46335     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46336                            int x0, int y0, const float z0,
46337                            int x1, int y1, const float z1,
46338                            int x2, int y2, const float z2,
46339                            const tc *const color, const float opacity=1,
46340                            const float brightness=1) {
46341       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46342       if (!color)
46343         throw CImgArgumentException(_cimg_instance
46344                                     "draw_triangle(): Specified color is (null).",
46345                                     cimg_instance);
46346       if (!is_sameXY(zbuffer))
46347         throw CImgArgumentException(_cimg_instance
46348                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46349                                     "different dimensions.",
46350                                     cimg_instance,
46351                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46352 
46353       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46354       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1);
46355       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2);
46356       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2);
46357       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46358 
46359       const int
46360         w1 = width() - 1, h1 = height() - 1,
46361         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46362         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46363         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46364         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46365       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
46366 
46367       const float cbs = cimg::cut(brightness,0,2);
46368       cimg_init_scanline(opacity);
46369 
46370       for (int y = cy0; y<=cy2; ++y) {
46371         const int yy0 = y - y0, yy1 = y - y1;
46372         int
46373           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46374           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46375         float
46376           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46377           izM = iz0 + diz02*yy0/dy02;
46378         if (xm>xM) cimg::swap(xm,xM,izm,izM);
46379         if (xM>=0 && xm<=w1) {
46380           const int
46381             cxm = cimg::cut(xm,0,w1),
46382             cxM = cimg::cut(xM,0,w1);
46383           T *ptrd = data(cxm,y);
46384           tz *ptrz = zbuffer.data(cxm,y);
46385           const int dxmM = std::max(1,xM - xm);
46386           const float dizmM = izM - izm;
46387 
46388           for (int x = cxm; x<=cxM; ++x) {
46389             const int xxm = x - xm;
46390             const float iz = izm + dizmM*xxm/dxmM;
46391             if (iz>=*ptrz) {
46392               *ptrz = (tz)iz;
46393               cimg_forC(*this,c) {
46394                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
46395                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46396               }
46397             }
46398             ++ptrd; ++ptrz;
46399           }
46400         }
46401       }
46402       return *this;
46403     }
46404 
46405     //! Draw a Gouraud-shaded 2D triangle.
46406     /**
46407        \param x0 X-coordinate of the first vertex in the image instance.
46408        \param y0 Y-coordinate of the first vertex in the image instance.
46409        \param x1 X-coordinate of the second vertex in the image instance.
46410        \param y1 Y-coordinate of the second vertex in the image instance.
46411        \param x2 X-coordinate of the third vertex in the image instance.
46412        \param y2 Y-coordinate of the third vertex in the image instance.
46413        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
46414        \param bs0 Brightness factor of the first vertex (in [0,2]).
46415        \param bs1 brightness factor of the second vertex (in [0,2]).
46416        \param bs2 brightness factor of the third vertex (in [0,2]).
46417        \param opacity Drawing opacity.
46418     **/
46419     template<typename tc>
46420     CImg<T>& draw_triangle(int x0, int y0,
46421                            int x1, int y1,
46422                            int x2, int y2,
46423                            const tc *const color,
46424                            float bs0,
46425                            float bs1,
46426                            float bs2,
46427                            const float opacity=1) {
46428       if (is_empty()) return *this;
46429       if (!color)
46430         throw CImgArgumentException(_cimg_instance
46431                                     "draw_triangle(): Specified color is (null).",
46432                                     cimg_instance);
46433 
46434       if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1);
46435       if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2);
46436       if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2);
46437       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46438 
46439       const int
46440         w1 = width() - 1, h1 = height() - 1,
46441         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46442         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46443         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46444         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46445       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46446 
46447       cimg_init_scanline(opacity);
46448 
46449       for (int y = cy0; y<=cy2; ++y) {
46450         const int yy0 = y - y0, yy1 = y - y1;
46451         int
46452           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46453           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46454         float
46455           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46456           bsM = bs0 + dbs02*yy0/dy02;
46457         if (xm>xM) cimg::swap(xm,xM,bsm,bsM);
46458         if (xM>=0 && xm<=w1) {
46459           const int
46460             cxm = cimg::cut(xm,0,w1),
46461             cxM = cimg::cut(xM,0,w1);
46462           T *ptrd = data(cxm,y);
46463           const int dxmM = std::max(1,xM - xm);
46464           const float dbsmM = bsM - bsm;
46465 
46466           for (int x = cxm; x<=cxM; ++x) {
46467             const int xxm = x - xm;
46468             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46469             cimg_forC(*this,c) {
46470               const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
46471               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46472             }
46473             ++ptrd;
46474           }
46475         }
46476       }
46477       return *this;
46478     }
46479 
46480     //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading.
46481     template<typename tz, typename tc>
46482     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46483                            int x0, int y0, const float z0,
46484                            int x1, int y1, const float z1,
46485                            int x2, int y2, const float z2,
46486                            const tc *const color,
46487                            float bs0,
46488                            float bs1,
46489                            float bs2,
46490                            float opacity=1) {
46491       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46492       if (!color)
46493         throw CImgArgumentException(_cimg_instance
46494                                     "draw_triangle(): Specified color is (null).",
46495                                     cimg_instance);
46496       if (!is_sameXY(zbuffer))
46497         throw CImgArgumentException(_cimg_instance
46498                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46499                                     "different dimensions.",
46500                                     cimg_instance,
46501                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46502 
46503       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46504       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1);
46505       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2);
46506       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2);
46507       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46508 
46509       const int
46510         w1 = width() - 1, h1 = height() - 1,
46511         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46512         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46513         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46514         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46515       const float
46516         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46517         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46518 
46519       cimg_init_scanline(opacity);
46520 
46521       for (int y = cy0; y<=cy2; ++y) {
46522         const int yy0 = y - y0, yy1 = y - y1;
46523         int
46524           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46525           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46526         float
46527           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46528           izM = iz0 + diz02*yy0/dy02,
46529           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46530           bsM = bs0 + dbs02*yy0/dy02;
46531         if (xm>xM) cimg::swap(xm,xM,izm,izM,bsm,bsM);
46532         if (xM>=0 && xm<=w1) {
46533           const int
46534             cxm = cimg::cut(xm,0,w1),
46535             cxM = cimg::cut(xM,0,w1);
46536           T *ptrd = data(cxm,y);
46537           tz *ptrz = zbuffer.data(cxm,y);
46538           const int dxmM = std::max(1,xM - xm);
46539           const float dizmM = izM - izm, dbsmM = bsM - bsm;
46540 
46541           for (int x = cxm; x<=cxM; ++x) {
46542             const int xxm = x - xm;
46543             const float iz = izm + dizmM*xxm/dxmM;
46544             if (iz>=*ptrz) {
46545               *ptrz = (tz)iz;
46546               const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46547               cimg_forC(*this,c) {
46548                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
46549                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46550               }
46551             }
46552             ++ptrd; ++ptrz;
46553           }
46554         }
46555       }
46556       return *this;
46557     }
46558 
46559     //! Draw a color-interpolated 2D triangle.
46560     /**
46561        \param x0 X-coordinate of the first vertex in the image instance.
46562        \param y0 Y-coordinate of the first vertex in the image instance.
46563        \param x1 X-coordinate of the second vertex in the image instance.
46564        \param y1 Y-coordinate of the second vertex in the image instance.
46565        \param x2 X-coordinate of the third vertex in the image instance.
46566        \param y2 Y-coordinate of the third vertex in the image instance.
46567        \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
46568        \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex.
46569        \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
46570        \param opacity Drawing opacity.
46571      **/
46572     template<typename tc1, typename tc2, typename tc3>
46573     CImg<T>& draw_triangle(const int x0, const int y0,
46574                            const int x1, const int y1,
46575                            const int x2, const int y2,
46576                            const tc1 *const color1,
46577                            const tc2 *const color2,
46578                            const tc3 *const color3,
46579                            const float opacity=1) {
46580       const unsigned char one = 1;
46581       cimg_forC(*this,c)
46582         get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
46583       return *this;
46584     }
46585 
46586     //! Draw a textured 2D triangle.
46587     /**
46588        \param x0 X-coordinate of the first vertex in the image instance.
46589        \param y0 Y-coordinate of the first vertex in the image instance.
46590        \param x1 X-coordinate of the second vertex in the image instance.
46591        \param y1 Y-coordinate of the second vertex in the image instance.
46592        \param x2 X-coordinate of the third vertex in the image instance.
46593        \param y2 Y-coordinate of the third vertex in the image instance.
46594        \param texture Texture image used to fill the triangle.
46595        \param tx0 X-coordinate of the first vertex in the texture image.
46596        \param ty0 Y-coordinate of the first vertex in the texture image.
46597        \param tx1 X-coordinate of the second vertex in the texture image.
46598        \param ty1 Y-coordinate of the second vertex in the texture image.
46599        \param tx2 X-coordinate of the third vertex in the texture image.
46600        \param ty2 Y-coordinate of the third vertex in the texture image.
46601        \param opacity Drawing opacity.
46602        \param brightness Brightness factor of the drawing (in [0,2]).
46603     **/
46604     template<typename tc>
46605     CImg<T>& draw_triangle(int x0, int y0,
46606                            int x1, int y1,
46607                            int x2, int y2,
46608                            const CImg<tc>& texture,
46609                            int tx0, int ty0,
46610                            int tx1, int ty1,
46611                            int tx2, int ty2,
46612                            const float opacity=1,
46613                            const float brightness=1) {
46614       if (is_empty()) return *this;
46615       if (texture._depth>1 || texture._spectrum<_spectrum)
46616         throw CImgArgumentException(_cimg_instance
46617                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46618                                     cimg_instance,
46619                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46620       if (is_overlapped(texture))
46621         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46622 
46623       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
46624       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2);
46625       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2);
46626       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46627 
46628       const int
46629         w1 = width() - 1, h1 = height() - 1,
46630         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46631         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46632         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46633         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46634         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
46635         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
46636         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
46637         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
46638       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46639       const float cbs = cimg::cut(brightness,0,2);
46640       cimg_init_scanline(opacity);
46641 
46642       for (int y = cy0; y<=cy2; ++y) {
46643         const int yy0 = y - y0, yy1 = y - y1;
46644         int
46645           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46646           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46647           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
46648           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
46649           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
46650           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
46651         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM);
46652         if (xM>=0 && xm<=w1) {
46653           const int
46654             cxm = cimg::cut(xm,0,w1),
46655             cxM = cimg::cut(xM,0,w1);
46656           T *ptrd = data(cxm,y);
46657           const int
46658             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46659             dtxmM = txM - txm, dtymM = tyM - tym;
46660 
46661           for (int x = cxm; x<=cxM; ++x) {
46662             const int
46663               xxm = x - xm,
46664               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
46665               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
46666             const tc *const color = &texture._atXY(tx,ty);
46667             cimg_forC(*this,c) {
46668               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46669               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46670             }
46671             ++ptrd;
46672           }
46673         }
46674       }
46675       return *this;
46676     }
46677 
46678     //! Draw a 2D textured triangle, with perspective correction.
46679     template<typename tc>
46680     CImg<T>& draw_triangle(int x0, int y0, const float z0,
46681                            int x1, int y1, const float z1,
46682                            int x2, int y2, const float z2,
46683                            const CImg<tc>& texture,
46684                            int tx0, int ty0,
46685                            int tx1, int ty1,
46686                            int tx2, int ty2,
46687                            const float opacity=1,
46688                            const float brightness=1) {
46689       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46690       if (texture._depth>1 || texture._spectrum<_spectrum)
46691         throw CImgArgumentException(_cimg_instance
46692                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46693                                     cimg_instance,
46694                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46695       if (is_overlapped(texture))
46696         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46697 
46698       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46699       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
46700       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
46701       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
46702       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46703 
46704       const int
46705         w1 = width() - 1, h1 = height() - 1,
46706         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46707         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46708         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46709         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46710       const float
46711         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46712         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46713         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46714         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46715         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
46716 
46717       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46718       const float cbs = cimg::cut(brightness,0,2);
46719       cimg_init_scanline(opacity);
46720 
46721       for (int y = cy0; y<=cy2; ++y) {
46722         const int yy0 = y - y0, yy1 = y - y1;
46723         int
46724           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46725           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46726         float
46727           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46728           izM = iz0 + diz02*yy0/dy02,
46729           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46730           txzM = txz0 + dtxz02*yy0/dy02,
46731           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46732           tyzM = tyz0 + dtyz02*yy0/dy02;
46733         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
46734         if (xM>=0 && xm<=w1) {
46735           const int
46736             cxm = cimg::cut(xm,0,w1),
46737             cxM = cimg::cut(xM,0,w1);
46738           T *ptrd = data(cxm,y);
46739           const int dxmM = std::max(1,xM - xm);
46740           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
46741 
46742           for (int x = cxm; x<=cxM; ++x) {
46743             const int xxm = x - xm;
46744             const float
46745               iz = izm + dizmM*xxm/dxmM,
46746               txz = txzm + dtxzmM*xxm/dxmM,
46747               tyz = tyzm + dtyzmM*xxm/dxmM;
46748             const int
46749               tx = (int)cimg::round(txz/iz),
46750               ty = (int)cimg::round(tyz/iz);
46751             const tc *const color = &texture._atXY(tx,ty);
46752             cimg_forC(*this,c) {
46753               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46754               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46755             }
46756             ++ptrd;
46757           }
46758         }
46759       }
46760       return *this;
46761     }
46762 
46763     //! Draw a textured 2D triangle, with perspective correction and z-buffering.
46764     template<typename tz, typename tc>
46765     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46766                            int x0, int y0, const float z0,
46767                            int x1, int y1, const float z1,
46768                            int x2, int y2, const float z2,
46769                            const CImg<tc>& texture,
46770                            int tx0, int ty0,
46771                            int tx1, int ty1,
46772                            int tx2, int ty2,
46773                            const float opacity=1,
46774                            const float brightness=1) {
46775       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46776       if (!is_sameXY(zbuffer))
46777         throw CImgArgumentException(_cimg_instance
46778                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46779                                     "different dimensions.",
46780                                     cimg_instance,
46781                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46782 
46783       if (texture._depth>1 || texture._spectrum<_spectrum)
46784         throw CImgArgumentException(_cimg_instance
46785                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46786                                     cimg_instance,
46787                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46788       if (is_overlapped(texture))
46789         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46790 
46791       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46792       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
46793       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
46794       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
46795       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46796 
46797       const int
46798         w1 = width() - 1, h1 = height() - 1,
46799         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46800         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46801         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46802         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46803       const float
46804         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46805         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46806         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46807         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46808         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
46809 
46810       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46811       const float cbs = cimg::cut(brightness,0,2);
46812       cimg_init_scanline(opacity);
46813 
46814       for (int y = cy0; y<=cy2; ++y) {
46815         const int yy0 = y - y0, yy1 = y - y1;
46816         int
46817           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46818           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46819         float
46820           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46821           izM = iz0 + diz02*yy0/dy02,
46822           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46823           txzM = txz0 + dtxz02*yy0/dy02,
46824           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46825           tyzM = tyz0 + dtyz02*yy0/dy02;
46826         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
46827         if (xM>=0 && xm<=w1) {
46828           const int
46829             cxm = cimg::cut(xm,0,w1),
46830             cxM = cimg::cut(xM,0,w1);
46831           T *ptrd = data(cxm,y);
46832           tz *ptrz = zbuffer.data(cxm,y);
46833           const int dxmM = std::max(1,xM - xm);
46834           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
46835 
46836           for (int x = cxm; x<=cxM; ++x) {
46837             const int xxm = x - xm;
46838             const float iz = izm + dizmM*xxm/dxmM;
46839             if (iz>=*ptrz) {
46840               *ptrz = (tz)iz;
46841               const float
46842                 txz = txzm + dtxzmM*xxm/dxmM,
46843                 tyz = tyzm + dtyzmM*xxm/dxmM;
46844               const int
46845                 tx = (int)cimg::round(txz/iz),
46846                 ty = (int)cimg::round(tyz/iz);
46847               const tc *const color = &texture._atXY(tx,ty);
46848               cimg_forC(*this,c) {
46849                 const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46850                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46851               }
46852             }
46853             ++ptrd; ++ptrz;
46854           }
46855         }
46856       }
46857       return *this;
46858     }
46859 
46860     //! Draw a Phong-shaded 2D triangle.
46861     /**
46862        \param x0 X-coordinate of the first vertex in the image instance.
46863        \param y0 Y-coordinate of the first vertex in the image instance.
46864        \param x1 X-coordinate of the second vertex in the image instance.
46865        \param y1 Y-coordinate of the second vertex in the image instance.
46866        \param x2 X-coordinate of the third vertex in the image instance.
46867        \param y2 Y-coordinate of the third vertex in the image instance.
46868        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
46869        \param light Light image.
46870        \param lx0 X-coordinate of the first vertex in the light image.
46871        \param ly0 Y-coordinate of the first vertex in the light image.
46872        \param lx1 X-coordinate of the second vertex in the light image.
46873        \param ly1 Y-coordinate of the second vertex in the light image.
46874        \param lx2 X-coordinate of the third vertex in the light image.
46875        \param ly2 Y-coordinate of the third vertex in the light image.
46876        \param opacity Drawing opacity.
46877     **/
46878     template<typename tc, typename tl>
46879     CImg<T>& draw_triangle(int x0, int y0,
46880                            int x1, int y1,
46881                            int x2, int y2,
46882                            const tc *const color,
46883                            const CImg<tl>& light,
46884                            int lx0, int ly0,
46885                            int lx1, int ly1,
46886                            int lx2, int ly2,
46887                            const float opacity=1) {
46888       if (is_empty()) return *this;
46889       if (!color)
46890         throw CImgArgumentException(_cimg_instance
46891                                     "draw_triangle(): Specified color is (null).",
46892                                     cimg_instance);
46893       if (light._depth>1 || light._spectrum<_spectrum)
46894         throw CImgArgumentException(_cimg_instance
46895                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46896                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46897 
46898       if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1);
46899       if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2);
46900       if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2);
46901       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46902 
46903       const int
46904         w1 = width() - 1, h1 = height() - 1,
46905         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46906         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46907         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46908         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46909         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
46910         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
46911         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
46912         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
46913 
46914       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
46915       cimg_init_scanline(opacity);
46916 
46917       for (int y = cy0; y<=cy2; ++y) {
46918         const int yy0 = y - y0, yy1 = y - y1;
46919         int
46920           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46921           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46922           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
46923           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
46924           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
46925           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
46926         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM);
46927         if (xM>=0 && xm<=w1) {
46928           const int
46929             cxm = cimg::cut(xm,0,w1),
46930             cxM = cimg::cut(xM,0,w1);
46931           T *ptrd = data(cxm,y);
46932           const int
46933             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46934             dlxmM = lxM - lxm, dlymM = lyM - lym;
46935 
46936           for (int x = cxm; x<=cxM; ++x) {
46937             const int
46938               xxm = x - xm,
46939               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
46940               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
46941             const tl *const lig = &light._atXY(lx,ly);
46942             cimg_forC(*this,c) {
46943               const tc col = color[c];
46944               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
46945               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46946               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46947             }
46948             ++ptrd;
46949           }
46950         }
46951       }
46952       return *this;
46953     }
46954 
46955     //! Draw a Phong-shaded 2D triangle, with z-buffering.
46956     template<typename tz, typename tc, typename tl>
46957     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46958                            int x0, int y0, const float z0,
46959                            int x1, int y1, const float z1,
46960                            int x2, int y2, const float z2,
46961                            const tc *const color,
46962                            const CImg<tl>& light,
46963                            int lx0, int ly0,
46964                            int lx1, int ly1,
46965                            int lx2, int ly2,
46966                            const float opacity=1) {
46967       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46968       if (!color)
46969         throw CImgArgumentException(_cimg_instance
46970                                     "draw_triangle(): Specified color is (null).",
46971                                     cimg_instance);
46972       if (light._depth>1 || light._spectrum<_spectrum)
46973         throw CImgArgumentException(_cimg_instance
46974                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46975                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46976       if (!is_sameXY(zbuffer))
46977         throw CImgArgumentException(_cimg_instance
46978                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46979                                     "different dimensions.",
46980                                     cimg_instance,
46981                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46982       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
46983                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46984 
46985       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46986       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
46987       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
46988       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
46989       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46990 
46991       const int
46992         w1 = width() - 1, h1 = height() - 1,
46993         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46994         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46995         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46996         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46997         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
46998         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
46999         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
47000         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
47001       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
47002 
47003       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
47004       cimg_init_scanline(opacity);
47005 
47006       for (int y = cy0; y<=cy2; ++y) {
47007         const int yy0 = y - y0, yy1 = y - y1;
47008         int
47009           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47010           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47011           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
47012           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
47013           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
47014           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
47015         float
47016           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47017           izM = iz0 + diz02*yy0/dy02;
47018 
47019         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
47020         if (xM>=0 && xm<=w1) {
47021           const int
47022             cxm = cimg::cut(xm,0,w1),
47023             cxM = cimg::cut(xM,0,w1);
47024           T *ptrd = data(cxm,y);
47025           tz *ptrz = zbuffer.data(cxm,y);
47026           const int
47027             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47028             dlxmM = lxM - lxm, dlymM = lyM - lym;
47029           const float dizmM = izM - izm;
47030 
47031           for (int x = cxm; x<=cxM; ++x) {
47032             const int xxm = x - xm;
47033             const float iz = izm + dizmM*xxm/dxmM;
47034             if (iz>=*ptrz) {
47035               *ptrz = (tz)iz;
47036               const int
47037                 lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
47038                 ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
47039               const tl *const lig = &light._atXY(lx,ly);
47040               cimg_forC(*this,c) {
47041                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47042                 const tc col = color[c];
47043                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47044                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47045               }
47046             }
47047             ++ptrd; ++ptrz;
47048           }
47049         }
47050       }
47051       return *this;
47052     }
47053 
47054     //! Draw a textured Gouraud-shaded 2D triangle.
47055     /**
47056        \param x0 X-coordinate of the first vertex in the image instance.
47057        \param y0 Y-coordinate of the first vertex in the image instance.
47058        \param x1 X-coordinate of the second vertex in the image instance.
47059        \param y1 Y-coordinate of the second vertex in the image instance.
47060        \param x2 X-coordinate of the third vertex in the image instance.
47061        \param y2 Y-coordinate of the third vertex in the image instance.
47062        \param texture Texture image used to fill the triangle.
47063        \param tx0 X-coordinate of the first vertex in the texture image.
47064        \param ty0 Y-coordinate of the first vertex in the texture image.
47065        \param tx1 X-coordinate of the second vertex in the texture image.
47066        \param ty1 Y-coordinate of the second vertex in the texture image.
47067        \param tx2 X-coordinate of the third vertex in the texture image.
47068        \param ty2 Y-coordinate of the third vertex in the texture image.
47069        \param bs0 Brightness factor of the first vertex.
47070        \param bs1 Brightness factor of the second vertex.
47071        \param bs2 Brightness factor of the third vertex.
47072        \param opacity Drawing opacity.
47073     **/
47074     template<typename tc>
47075     CImg<T>& draw_triangle(int x0, int y0,
47076                            int x1, int y1,
47077                            int x2, int y2,
47078                            const CImg<tc>& texture,
47079                            int tx0, int ty0,
47080                            int tx1, int ty1,
47081                            int tx2, int ty2,
47082                            float bs0,
47083                            float bs1,
47084                            float bs2,
47085                            const float opacity=1) {
47086       if (is_empty()) return *this;
47087       if (texture._depth>1 || texture._spectrum<_spectrum)
47088         throw CImgArgumentException(_cimg_instance
47089                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47090                                     cimg_instance,
47091                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47092       if (is_overlapped(texture))
47093         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
47094                              bs0,bs1,bs2,opacity);
47095 
47096       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1);
47097       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2);
47098       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2);
47099       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47100 
47101       const int
47102         w1 = width() - 1, h1 = height() - 1,
47103         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47104         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47105         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47106         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47107         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
47108         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
47109         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
47110         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
47111       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47112 
47113       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47114       cimg_init_scanline(opacity);
47115 
47116       for (int y = cy0; y<=cy2; ++y) {
47117         const int yy0 = y - y0, yy1 = y - y1;
47118         int
47119           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47120           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47121           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
47122           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
47123           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
47124           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
47125         float
47126           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47127           bsM = bs0 + dbs02*yy0/dy02;
47128         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM);
47129         if (xM>=0 && xm<=w1) {
47130           const int
47131             cxm = cimg::cut(xm,0,w1),
47132             cxM = cimg::cut(xM,0,w1);
47133           T *ptrd = data(cxm,y);
47134           const int
47135             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47136             dtxmM = txM - txm, dtymM = tyM - tym;
47137           const float dbsmM = bsM - bsm;
47138 
47139           for (int x = cxm; x<=cxM; ++x) {
47140             const int
47141               xxm = x - xm,
47142               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
47143               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
47144             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47145             const tc *const color = &texture._atXY(tx,ty);
47146             cimg_forC(*this,c) {
47147               const tc col = color[c*twhd];
47148               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47149               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47150             }
47151             ++ptrd;
47152           }
47153         }
47154       }
47155       return *this;
47156     }
47157 
47158     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading.
47159     template<typename tc>
47160     CImg<T>& draw_triangle(int x0, int y0, const float z0,
47161                            int x1, int y1, const float z1,
47162                            int x2, int y2, const float z2,
47163                            const CImg<tc>& texture,
47164                            int tx0, int ty0,
47165                            int tx1, int ty1,
47166                            int tx2, int ty2,
47167                            float bs0,
47168                            float bs1,
47169                            float bs2,
47170                            const float opacity=1) {
47171       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47172       if (texture._depth>1 || texture._spectrum<_spectrum)
47173         throw CImgArgumentException(_cimg_instance
47174                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47175                                     cimg_instance,
47176                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47177       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
47178                                                        bs0,bs1,bs2,opacity);
47179 
47180       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47181       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
47182       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
47183       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
47184       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47185 
47186       const int
47187         w1 = width() - 1, h1 = height() - 1,
47188         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47189         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47190         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47191         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47192       const float
47193         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47194         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47195         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47196         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47197         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47198         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47199 
47200       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47201       cimg_init_scanline(opacity);
47202 
47203       for (int y = cy0; y<=cy2; ++y) {
47204         const int yy0 = y - y0, yy1 = y - y1;
47205         int
47206           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47207           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47208         float
47209           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47210           izM = iz0 + diz02*yy0/dy02,
47211           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47212           txzM = txz0 + dtxz02*yy0/dy02,
47213           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47214           tyzM = tyz0 + dtyz02*yy0/dy02,
47215           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47216           bsM = bs0 + dbs02*yy0/dy02;
47217         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
47218         if (xM>=0 && xm<=w1) {
47219           const int
47220             cxm = cimg::cut(xm,0,w1),
47221             cxM = cimg::cut(xM,0,w1);
47222           T *ptrd = data(cxm,y);
47223           const int dxmM = std::max(1,xM - xm);
47224           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
47225 
47226           for (int x = cxm; x<=cxM; ++x) {
47227             const int xxm = x - xm;
47228             const float
47229               iz = izm + dizmM*xxm/dxmM,
47230               txz = txzm + dtxzmM*xxm/dxmM,
47231               tyz = tyzm + dtyzmM*xxm/dxmM,
47232               cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47233             const int
47234               tx = (int)cimg::round(txz/iz),
47235               ty = (int)cimg::round(tyz/iz);
47236             const tc *const color = &texture._atXY(tx,ty);
47237             cimg_forC(*this,c) {
47238               const tc col = color[c*twhd];
47239               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47240               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47241             }
47242             ++ptrd;
47243           }
47244         }
47245       }
47246       return *this;
47247     }
47248 
47249     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading.
47250     template<typename tz, typename tc>
47251     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47252                            int x0, int y0, const float z0,
47253                            int x1, int y1, const float z1,
47254                            int x2, int y2, const float z2,
47255                            const CImg<tc>& texture,
47256                            int tx0, int ty0,
47257                            int tx1, int ty1,
47258                            int tx2, int ty2,
47259                            float bs0,
47260                            float bs1,
47261                            float bs2,
47262                            const float opacity=1) {
47263       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47264       if (!is_sameXY(zbuffer))
47265         throw CImgArgumentException(_cimg_instance
47266                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47267                                     "different dimensions.",
47268                                     cimg_instance,
47269                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47270       if (texture._depth>1 || texture._spectrum<_spectrum)
47271         throw CImgArgumentException(_cimg_instance
47272                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47273                                     cimg_instance,
47274                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47275       if (is_overlapped(texture))
47276         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity);
47277 
47278       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47279       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
47280       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
47281       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
47282       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47283 
47284       const int
47285         w1 = width() - 1, h1 = height() - 1,
47286         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47287         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47288         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47289         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47290       const float
47291         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47292         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47293         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47294         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47295         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47296         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47297 
47298       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47299       cimg_init_scanline(opacity);
47300 
47301       for (int y = cy0; y<=cy2; ++y) {
47302         const int yy0 = y - y0, yy1 = y - y1;
47303         int
47304           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47305           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47306         float
47307           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47308           izM = iz0 + diz02*yy0/dy02,
47309           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47310           txzM = txz0 + dtxz02*yy0/dy02,
47311           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47312           tyzM = tyz0 + dtyz02*yy0/dy02,
47313           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47314           bsM = bs0 + dbs02*yy0/dy02;
47315         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
47316         if (xM>=0 && xm<=w1) {
47317           const int
47318             cxm = cimg::cut(xm,0,w1),
47319             cxM = cimg::cut(xM,0,w1);
47320           T *ptrd = data(cxm,y);
47321           tz *ptrz = zbuffer.data(cxm,y);
47322           const int dxmM = std::max(1,xM - xm);
47323           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
47324 
47325           for (int x = cxm; x<=cxM; ++x) {
47326             const int xxm = x - xm;
47327             const float iz = izm + dizmM*xxm/dxmM;
47328             if (iz>=*ptrz) {
47329               *ptrz = (tz)iz;
47330               const float
47331                 txz = txzm + dtxzmM*xxm/dxmM,
47332                 tyz = tyzm + dtyzmM*xxm/dxmM,
47333                 cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47334               const int
47335                 tx = (int)cimg::round(txz/iz),
47336                 ty = (int)cimg::round(tyz/iz);
47337               const tc *const color = &texture._atXY(tx,ty);
47338               cimg_forC(*this,c) {
47339                 const tc col = color[c*twhd];
47340                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47341                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47342               }
47343             }
47344             ++ptrd; ++ptrz;
47345           }
47346         }
47347       }
47348       return *this;
47349     }
47350 
47351     //! Draw a textured Phong-shaded 2D triangle.
47352     /**
47353        \param x0 X-coordinate of the first vertex in the image instance.
47354        \param y0 Y-coordinate of the first vertex in the image instance.
47355        \param x1 X-coordinate of the second vertex in the image instance.
47356        \param y1 Y-coordinate of the second vertex in the image instance.
47357        \param x2 X-coordinate of the third vertex in the image instance.
47358        \param y2 Y-coordinate of the third vertex in the image instance.
47359        \param texture Texture image used to fill the triangle.
47360        \param tx0 X-coordinate of the first vertex in the texture image.
47361        \param ty0 Y-coordinate of the first vertex in the texture image.
47362        \param tx1 X-coordinate of the second vertex in the texture image.
47363        \param ty1 Y-coordinate of the second vertex in the texture image.
47364        \param tx2 X-coordinate of the third vertex in the texture image.
47365        \param ty2 Y-coordinate of the third vertex in the texture image.
47366        \param light Light image.
47367        \param lx0 X-coordinate of the first vertex in the light image.
47368        \param ly0 Y-coordinate of the first vertex in the light image.
47369        \param lx1 X-coordinate of the second vertex in the light image.
47370        \param ly1 Y-coordinate of the second vertex in the light image.
47371        \param lx2 X-coordinate of the third vertex in the light image.
47372        \param ly2 Y-coordinate of the third vertex in the light image.
47373        \param opacity Drawing opacity.
47374     **/
47375     template<typename tc, typename tl>
47376     CImg<T>& draw_triangle(int x0, int y0,
47377                            int x1, int y1,
47378                            int x2, int y2,
47379                            const CImg<tc>& texture,
47380                            int tx0, int ty0,
47381                            int tx1, int ty1,
47382                            int tx2, int ty2,
47383                            const CImg<tl>& light,
47384                            int lx0, int ly0,
47385                            int lx1, int ly1,
47386                            int lx2, int ly2,
47387                            const float opacity=1) {
47388       if (is_empty()) return *this;
47389       if (texture._depth>1 || texture._spectrum<_spectrum)
47390         throw CImgArgumentException(_cimg_instance
47391                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47392                                     cimg_instance,
47393                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47394       if (light._depth>1 || light._spectrum<_spectrum)
47395         throw CImgArgumentException(_cimg_instance
47396                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47397                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47398       if (is_overlapped(texture))
47399         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47400       if (is_overlapped(light))
47401         return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47402 
47403       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
47404       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
47405       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
47406       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47407 
47408       const int
47409         w1 = width() - 1, h1 = height() - 1,
47410         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47411         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47412         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47413         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47414         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
47415         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
47416         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
47417         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2,
47418         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
47419         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
47420         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
47421         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
47422 
47423       const ulongT
47424         twhd = (ulongT)texture._width*texture._height*texture._depth,
47425         lwhd = (ulongT)light._width*light._height*light._depth;
47426       cimg_init_scanline(opacity);
47427 
47428       for (int y = cy0; y<=cy2; ++y) {
47429         const int yy0 = y - y0, yy1 = y - y1;
47430         int
47431           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47432           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47433           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
47434           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
47435           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
47436           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02,
47437           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
47438           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
47439           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
47440           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
47441         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM);
47442         if (xM>=0 && xm<=w1) {
47443           const int
47444             cxm = cimg::cut(xm,0,w1),
47445             cxM = cimg::cut(xM,0,w1);
47446           T *ptrd = data(cxm,y);
47447           const int
47448             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47449             dtxmM = txM - txm, dtymM = tyM - tym,
47450             dlxmM = lxM - lxm, dlymM = lyM - lym;
47451 
47452           for (int x = cxm; x<=cxM; ++x) {
47453             const int
47454               xxm = x - xm,
47455               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
47456               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM,
47457               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
47458               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
47459             const tc *const color = &texture._atXY(tx,ty);
47460             const tl *const lig = &light._atXY(lx,ly);
47461             cimg_forC(*this,c) {
47462               const tc col = color[c*twhd];
47463               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47464               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47465               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47466             }
47467             ++ptrd;
47468           }
47469         }
47470       }
47471       return *this;
47472     }
47473 
47474     //! Draw a textured Phong-shaded 2D triangle, with perspective correction.
47475     template<typename tc, typename tl>
47476     CImg<T>& draw_triangle(int x0, int y0, const float z0,
47477                            int x1, int y1, const float z1,
47478                            int x2, int y2, const float z2,
47479                            const CImg<tc>& texture,
47480                            int tx0, int ty0,
47481                            int tx1, int ty1,
47482                            int tx2, int ty2,
47483                            const CImg<tl>& light,
47484                            int lx0, int ly0,
47485                            int lx1, int ly1,
47486                            int lx2, int ly2,
47487                            const float opacity=1) {
47488       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47489       if (texture._depth>1 || texture._spectrum<_spectrum)
47490         throw CImgArgumentException(_cimg_instance
47491                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47492                                     cimg_instance,
47493                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47494       if (light._depth>1 || light._spectrum<_spectrum)
47495         throw CImgArgumentException(_cimg_instance
47496                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47497                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47498       if (is_overlapped(texture))
47499         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
47500                              light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47501       if (is_overlapped(light))
47502         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
47503                              +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47504 
47505       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47506       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
47507       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
47508       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
47509       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47510 
47511       const int
47512         w1 = width() - 1, h1 = height() - 1,
47513         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47514         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47515         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47516         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47517       const float
47518         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47519         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47520         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47521         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47522         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47523         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
47524         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
47525         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
47526         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
47527 
47528       const ulongT
47529         twhd = (ulongT)texture._width*texture._height*texture._depth,
47530         lwhd = (ulongT)light._width*light._height*light._depth;
47531       cimg_init_scanline(opacity);
47532 
47533       for (int y = cy0; y<=cy2; ++y) {
47534         const int yy0 = y - y0, yy1 = y - y1;
47535         int
47536           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47537           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47538         float
47539           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47540           izM = iz0 + diz02*yy0/dy02,
47541           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47542           txzM = txz0 + dtxz02*yy0/dy02,
47543           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47544           tyzM = tyz0 + dtyz02*yy0/dy02,
47545           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
47546           lxzM = lxz0 + dlxz02*yy0/dy02,
47547           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
47548           lyzM = lyz0 + dlyz02*yy0/dy02;
47549         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
47550         if (xM>=0 && xm<=w1) {
47551           const int
47552             cxm = cimg::cut(xm,0,w1),
47553             cxM = cimg::cut(xM,0,w1);
47554           T *ptrd = data(cxm,y);
47555           const int dxmM = std::max(1,xM - xm);
47556           const float
47557             dizmM = izM - izm,
47558             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
47559             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
47560 
47561           for (int x = cxm; x<=cxM; ++x) {
47562             const int xxm = x - xm;
47563             const float
47564               iz = izm + dizmM*xxm/dxmM,
47565               txz = txzm + dtxzmM*xxm/dxmM,
47566               tyz = tyzm + dtyzmM*xxm/dxmM,
47567               lxz = lxzm + dlxzmM*xxm/dxmM,
47568               lyz = lyzm + dlyzmM*xxm/dxmM;
47569             const int
47570               tx = (int)cimg::round(txz/iz),
47571               ty = (int)cimg::round(tyz/iz),
47572               lx = (int)cimg::round(lxz/iz),
47573               ly = (int)cimg::round(lyz/iz);
47574             const tc *const color = &texture._atXY(tx,ty);
47575             const tl *const lig = &light._atXY(lx,ly);
47576             cimg_forC(*this,c) {
47577               const tc col = color[c*twhd];
47578               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47579               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47580               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47581             }
47582             ++ptrd;
47583           }
47584         }
47585       }
47586       return *this;
47587     }
47588 
47589     //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering.
47590     template<typename tz, typename tc, typename tl>
47591     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47592                            int x0, int y0, const float z0,
47593                            int x1, int y1, const float z1,
47594                            int x2, int y2, const float z2,
47595                            const CImg<tc>& texture,
47596                            int tx0, int ty0,
47597                            int tx1, int ty1,
47598                            int tx2, int ty2,
47599                            const CImg<tl>& light,
47600                            int lx0, int ly0,
47601                            int lx1, int ly1,
47602                            int lx2, int ly2,
47603                            const float opacity=1) {
47604       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47605       if (!is_sameXY(zbuffer))
47606         throw CImgArgumentException(_cimg_instance
47607                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47608                                     "different dimensions.",
47609                                     cimg_instance,
47610                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47611       if (texture._depth>1 || texture._spectrum<_spectrum)
47612         throw CImgArgumentException(_cimg_instance
47613                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47614                                     cimg_instance,
47615                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47616       if (light._depth>1 || light._spectrum<_spectrum)
47617         throw CImgArgumentException(_cimg_instance
47618                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47619                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47620       if (is_overlapped(texture))
47621         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
47622                              +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47623       if (is_overlapped(light))
47624         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
47625                              texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47626 
47627       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47628       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
47629       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
47630       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
47631       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47632 
47633       const int
47634         w1 = width() - 1, h1 = height() - 1,
47635         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47636         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47637         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47638         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47639       const float
47640         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47641         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47642         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47643         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47644         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47645         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
47646         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
47647         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
47648         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
47649 
47650       const ulongT
47651         twhd = (ulongT)texture._width*texture._height*texture._depth,
47652         lwhd = (ulongT)light._width*light._height*light._depth;
47653       cimg_init_scanline(opacity);
47654 
47655       for (int y = cy0; y<=cy2; ++y) {
47656         const int yy0 = y - y0, yy1 = y - y1;
47657         int
47658           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47659           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47660         float
47661           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47662           izM = iz0 + diz02*yy0/dy02,
47663           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47664           txzM = txz0 + dtxz02*yy0/dy02,
47665           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47666           tyzM = tyz0 + dtyz02*yy0/dy02,
47667           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
47668           lxzM = lxz0 + dlxz02*yy0/dy02,
47669           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
47670           lyzM = lyz0 + dlyz02*yy0/dy02;
47671         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
47672         if (xM>=0 && xm<=w1) {
47673           const int
47674             cxm = cimg::cut(xm,0,w1),
47675             cxM = cimg::cut(xM,0,w1);
47676           T *ptrd = data(cxm,y);
47677           tz *ptrz = zbuffer.data(cxm,y);
47678           const int dxmM = std::max(1,xM - xm);
47679           const float
47680             dizmM = izM - izm,
47681             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
47682             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
47683 
47684           for (int x = cxm; x<=cxM; ++x) {
47685             const int xxm = x - xm;
47686             const float iz = izm + dizmM*xxm/dxmM;
47687             if (iz>=*ptrz) {
47688               *ptrz = (tz)iz;
47689               const float
47690                 txz = txzm + dtxzmM*xxm/dxmM,
47691                 tyz = tyzm + dtyzmM*xxm/dxmM,
47692                 lxz = lxzm + dlxzmM*xxm/dxmM,
47693                 lyz = lyzm + dlyzmM*xxm/dxmM;
47694               const int
47695                 tx = (int)cimg::round(txz/iz),
47696                 ty = (int)cimg::round(tyz/iz),
47697                 lx = (int)cimg::round(lxz/iz),
47698                 ly = (int)cimg::round(lyz/iz);
47699               const tc *const color = &texture._atXY(tx,ty);
47700               const tl *const lig = &light._atXY(lx,ly);
47701               cimg_forC(*this,c) {
47702                 const tc col = color[c*twhd];
47703                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47704                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47705                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47706               }
47707             }
47708             ++ptrd; ++ptrz;
47709           }
47710         }
47711       }
47712       return *this;
47713     }
47714 
47715     //! Draw a filled 4D rectangle.
47716     /**
47717        \param x0 X-coordinate of the upper-left rectangle corner.
47718        \param y0 Y-coordinate of the upper-left rectangle corner.
47719        \param z0 Z-coordinate of the upper-left rectangle corner.
47720        \param c0 C-coordinate of the upper-left rectangle corner.
47721        \param x1 X-coordinate of the lower-right rectangle corner.
47722        \param y1 Y-coordinate of the lower-right rectangle corner.
47723        \param z1 Z-coordinate of the lower-right rectangle corner.
47724        \param c1 C-coordinate of the lower-right rectangle corner.
47725        \param val Scalar value used to fill the rectangle area.
47726        \param opacity Drawing opacity.
47727     **/
47728     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
47729                             const int x1, const int y1, const int z1, const int c1,
47730                             const T val, const float opacity=1) {
47731       if (is_empty()) return *this;
47732       const int
47733         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
47734         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
47735         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
47736         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
47737       const int
47738         lx = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
47739         ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
47740         lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
47741         lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
47742       const ulongT
47743         offX = (ulongT)_width - lx,
47744         offY = (ulongT)_width*(_height - ly),
47745         offZ = (ulongT)_width*_height*(_depth - lz);
47746       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
47747       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
47748       if (lx>0 && ly>0 && lz>0 && lc>0)
47749         for (int v = 0; v<lc; ++v) {
47750           for (int z = 0; z<lz; ++z) {
47751             for (int y = 0; y<ly; ++y) {
47752               if (opacity>=1) {
47753                 if (sizeof(T)!=1) { for (int x = 0; x<lx; ++x) *(ptrd++) = val; ptrd+=offX; }
47754                 else { std::memset(ptrd,(int)val,lx); ptrd+=_width; }
47755               } else { for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
47756             }
47757             ptrd+=offY;
47758           }
47759           ptrd+=offZ;
47760         }
47761       return *this;
47762     }
47763 
47764     //! Draw a filled 3D rectangle.
47765     /**
47766        \param x0 X-coordinate of the upper-left rectangle corner.
47767        \param y0 Y-coordinate of the upper-left rectangle corner.
47768        \param z0 Z-coordinate of the upper-left rectangle corner.
47769        \param x1 X-coordinate of the lower-right rectangle corner.
47770        \param y1 Y-coordinate of the lower-right rectangle corner.
47771        \param z1 Z-coordinate of the lower-right rectangle corner.
47772        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47773        \param opacity Drawing opacity.
47774     **/
47775     template<typename tc>
47776     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
47777                             const int x1, const int y1, const int z1,
47778                             const tc *const color, const float opacity=1) {
47779       if (is_empty()) return *this;
47780       if (!color)
47781         throw CImgArgumentException(_cimg_instance
47782                                     "draw_rectangle(): Specified color is (null).",
47783                                     cimg_instance);
47784       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
47785       return *this;
47786     }
47787 
47788     //! Draw a filled 2D rectangle.
47789     /**
47790        \param x0 X-coordinate of the upper-left rectangle corner.
47791        \param y0 Y-coordinate of the upper-left rectangle corner.
47792        \param x1 X-coordinate of the lower-right rectangle corner.
47793        \param y1 Y-coordinate of the lower-right rectangle corner.
47794        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47795        \param opacity Drawing opacity.
47796     **/
47797     template<typename tc>
47798     CImg<T>& draw_rectangle(const int x0, const int y0,
47799                             const int x1, const int y1,
47800                             const tc *const color, const float opacity=1) {
47801       return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
47802     }
47803 
47804     //! Draw a outlined 2D rectangle \overloading.
47805     template<typename tc>
47806     CImg<T>& draw_rectangle(const int x0, const int y0,
47807                             const int x1, const int y1,
47808                             const tc *const color, const float opacity,
47809                             const unsigned int pattern) {
47810       if (is_empty()) return *this;
47811       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
47812       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
47813       const int
47814         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
47815         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
47816       if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
47817                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
47818       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
47819         draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
47820         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
47821         draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
47822     }
47823 
47824     //! Draw a filled 2D polygon.
47825     /**
47826        \param points Set of polygon vertices.
47827        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47828        \param opacity Drawing opacity.
47829      **/
47830     template<typename tp, typename tc>
47831     CImg<T>& draw_polygon(const CImg<tp>& points,
47832                           const tc *const color, const float opacity=1) {
47833       if (is_empty() || !points) return *this;
47834       if (!color)
47835         throw CImgArgumentException(_cimg_instance
47836                                     "draw_polygon(): Specified color is (null).",
47837                                     cimg_instance);
47838       if (points.height()!=2)
47839         throw CImgArgumentException(_cimg_instance
47840                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
47841                                     cimg_instance,
47842                                     points._width,points._height,points._depth,points._spectrum);
47843       if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity);
47844       if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
47845                                              cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity);
47846       if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
47847                                                  cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),
47848                                                  cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity);
47849       cimg_init_scanline(opacity);
47850       int
47851         xmin = 0, ymin = 0,
47852         xmax = points.get_shared_row(0).max_min(xmin),
47853         ymax = points.get_shared_row(1).max_min(ymin);
47854       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
47855       if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
47856 
47857       ymin = std::max(0,ymin);
47858       ymax = std::min(height() - 1,ymax);
47859       CImg<intT> Xs(points._width,ymax - ymin + 1);
47860       CImg<uintT> count(Xs._height,1,1,1,0);
47861       unsigned int n = 0, nn = 1;
47862       bool go_on = true;
47863 
47864       while (go_on) {
47865         unsigned int an = (nn + 1)%points._width;
47866         const int
47867           x0 = cimg::uiround(points(n,0)),
47868           y0 = cimg::uiround(points(n,1));
47869         if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
47870         const int
47871           x1 = cimg::uiround(points(nn,0)),
47872           y1 = cimg::uiround(points(nn,1));
47873         unsigned int tn = an;
47874         while (points(tn,1)==y1) (tn+=1)%=points._width;
47875 
47876         if (y0!=y1) {
47877           const int
47878             y2 = cimg::uiround(points(tn,1)),
47879             x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
47880             step = cimg::sign(y01),
47881             tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2,
47882             tend = tmax - (step==cimg::sign(y12));
47883           unsigned int y = (unsigned int)y0 - ymin;
47884           for (int t = 0; t<=tend; ++t, y+=step)
47885             if (y<Xs._height) Xs(count[y]++,y) = x0 + (t*x01 + htmax)/tmax;
47886         }
47887         go_on = nn>n;
47888         n = nn;
47889         nn = an;
47890       }
47891 
47892       cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512))
47893       cimg_forY(Xs,y) {
47894         const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
47895         int px = width();
47896         for (unsigned int k = 0; k<Xsy._width; k+=2) {
47897           int x0 = Xsy[k];
47898           const int x1 = Xsy[k + 1];
47899           x0+=x0==px;
47900           cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
47901           px = x1;
47902         }
47903       }
47904       return *this;
47905     }
47906 
47907     //! Draw a outlined 2D or 3D polygon \overloading.
47908     template<typename t, typename tc>
47909     CImg<T>& draw_polygon(const CImg<t>& points,
47910                           const tc *const color, const float opacity, const unsigned int pattern) {
47911       if (is_empty() || !points) return *this;
47912       if (!color)
47913         throw CImgArgumentException(_cimg_instance
47914                                     "draw_polygon(): Specified color is (null).",
47915                                     cimg_instance);
47916       if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
47917       if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
47918                                              (int)points(1,0),(int)points(1,1),color,opacity,pattern);
47919       bool ninit_hatch = true;
47920       switch (points._height) {
47921       case 0 : case 1 :
47922         throw CImgArgumentException(_cimg_instance
47923                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
47924                                     cimg_instance,
47925                                     points._width,points._height,points._depth,points._spectrum);
47926       default : {
47927         CImg<intT> npoints(points._width,2);
47928         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
47929         unsigned int nb_points = 1;
47930         for (unsigned int p = 1; p<points._width; ++p) {
47931           const int nx = (int)points(p,0), ny = (int)points(p,1);
47932           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
47933         }
47934         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
47935         int ox = x0, oy = y0;
47936         for (unsigned int i = 1; i<nb_points; ++i) {
47937           const int _x = (int)npoints(i,0), _y = (int)npoints(i,1);
47938           draw_line(ox,oy,_x,_y,color,opacity,pattern,ninit_hatch);
47939           ninit_hatch = false;
47940           ox = _x; oy = _y;
47941         }
47942         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
47943       }
47944       }
47945       return *this;
47946     }
47947 
47948     //! Draw a filled 2D ellipse.
47949     /**
47950        \param x0 X-coordinate of the ellipse center.
47951        \param y0 Y-coordinate of the ellipse center.
47952        \param r1 First radius of the ellipse.
47953        \param r2 Second radius of the ellipse.
47954        \param angle Angle of the first radius.
47955        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47956        \param opacity Drawing opacity.
47957     **/
47958     template<typename tc>
47959     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
47960                           const tc *const color, const float opacity=1) {
47961       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true);
47962     }
47963 
47964     //! Draw a filled 2D ellipse \overloading.
47965     /**
47966        \param x0 X-coordinate of the ellipse center.
47967        \param y0 Y-coordinate of the ellipse center.
47968        \param tensor Diffusion tensor describing the ellipse.
47969        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47970        \param opacity Drawing opacity.
47971     **/
47972     template<typename t, typename tc>
47973     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
47974                           const tc *const color, const float opacity=1) {
47975       CImgList<t> eig = tensor.get_symmetric_eigen();
47976       const CImg<t> &val = eig[0], &vec = eig[1];
47977       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
47978                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
47979                           color,opacity);
47980     }
47981 
47982     //! Draw an outlined 2D ellipse.
47983     /**
47984        \param x0 X-coordinate of the ellipse center.
47985        \param y0 Y-coordinate of the ellipse center.
47986        \param r1 First radius of the ellipse.
47987        \param r2 Second radius of the ellipse.
47988        \param angle Angle of the first radius.
47989        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47990        \param opacity Drawing opacity.
47991        \param pattern An integer whose bits describe the outline pattern.
47992     **/
47993     template<typename tc>
47994     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
47995                           const tc *const color, const float opacity, const unsigned int pattern) {
47996       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false);
47997       return *this;
47998     }
47999 
48000     //! Draw an outlined 2D ellipse \overloading.
48001     /**
48002        \param x0 X-coordinate of the ellipse center.
48003        \param y0 Y-coordinate of the ellipse center.
48004        \param tensor Diffusion tensor describing the ellipse.
48005        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48006        \param opacity Drawing opacity.
48007        \param pattern An integer whose bits describe the outline pattern.
48008     **/
48009     template<typename t, typename tc>
48010     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
48011                           const tc *const color, const float opacity,
48012                           const unsigned int pattern) {
48013       CImgList<t> eig = tensor.get_symmetric_eigen();
48014       const CImg<t> &val = eig[0], &vec = eig[1];
48015       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
48016                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
48017                           color,opacity,pattern);
48018     }
48019 
48020     template<typename tc>
48021     CImg<T>& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle,
48022                            const tc *const color, const float opacity,
48023                            const unsigned int pattern, const bool is_filled) {
48024       if (is_empty() || (!is_filled && !pattern)) return *this;
48025       const float radiusM = std::max(radius1,radius2);
48026       if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this;
48027       if (!color)
48028         throw CImgArgumentException(_cimg_instance
48029                                     "draw_ellipse(): Specified color is (null).",
48030                                     cimg_instance);
48031       const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2);
48032       if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity);
48033       if (iradius1==iradius2) {
48034         if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity);
48035         else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern);
48036       }
48037       const float ang = (float)(angle*cimg::PI/180);
48038 
48039       if (!is_filled) { // Outlined
48040         const float ca = std::cos(ang), sa = std::sin(ang);
48041         CImg<int> points((unsigned int)cimg::round(6*radiusM),2);
48042         cimg_forX(points,k) {
48043           const float
48044             _ang = (float)(2*cimg::PI*k/points._width),
48045             X = (float)(radius1*std::cos(_ang)),
48046             Y = (float)(radius2*std::sin(_ang));
48047           points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa));
48048           points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca));
48049         }
48050         draw_polygon(points,color,opacity,pattern);
48051       } else { // Filled
48052         cimg_init_scanline(opacity);
48053         const float
48054           ca = std::cos(ang),
48055           sa = -std::sin(ang),
48056           ca2 = ca*ca,
48057           sa2 = sa*sa,
48058           casa = ca*sa,
48059           i1 = 1/cimg::sqr(radius1),
48060           i2 = 1/cimg::sqr(radius2),
48061           t1 = i1*ca2 + i2*sa2,
48062           t2 = (i2 - i1)*casa,
48063           t3 = i2*ca2 + i1*sa2,
48064           t12 = t1*2;
48065         const int
48066           _ymin = (int)std::floor(y0 - radiusM),
48067           _ymax = (int)std::ceil(y0 + radiusM),
48068           ymin = _ymin<0?0:_ymin,
48069           ymax = _ymax>=height()?height() - 1:_ymax;
48070         for (int y = ymin; y<=ymax; ++y) {
48071           const float
48072             Y = y - y0 + 0.5f,
48073             B = 2*t2*Y,
48074             C = t3*Y*Y - 1,
48075             D = B*B - 4*t1*C;
48076           if (D>=0) {
48077             const float sD = std::sqrt(D);
48078             const int
48079               xmin = (int)(x0 + cimg::round((-B - sD)/t12)),
48080               xmax = (int)(x0 + cimg::round((-B + sD)/t12));
48081             cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
48082           }
48083         }
48084       }
48085       return *this;
48086     }
48087 
48088     //! Draw a filled 2D circle.
48089     /**
48090        \param x0 X-coordinate of the circle center.
48091        \param y0 Y-coordinate of the circle center.
48092        \param radius  Circle radius.
48093        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48094        \param opacity Drawing opacity.
48095        \note
48096        - Circle version of the Bresenham's algorithm is used.
48097     **/
48098     template<typename tc>
48099     CImg<T>& draw_circle(const int x0, const int y0, int radius,
48100                          const tc *const color, const float opacity=1) {
48101       if (is_empty()) return *this;
48102       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
48103       if (!color)
48104         throw CImgArgumentException(_cimg_instance
48105                                     "draw_circle(): Specified color is (null).",
48106                                     cimg_instance);
48107       if (!radius) return draw_point(x0,y0,color,opacity);
48108       cimg_init_scanline(opacity);
48109       if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
48110       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
48111         if (f>=0) {
48112           const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
48113           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
48114           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
48115           f+=(ddFy+=2); --y;
48116         }
48117         const bool no_diag = y!=(x++);
48118         ++(f+=(ddFx+=2));
48119         const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
48120         if (no_diag) {
48121           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
48122           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
48123         }
48124       }
48125       return *this;
48126     }
48127 
48128     //! Draw an outlined 2D circle.
48129     /**
48130        \param x0 X-coordinate of the circle center.
48131        \param y0 Y-coordinate of the circle center.
48132        \param radius Circle radius.
48133        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48134        \param opacity Drawing opacity.
48135        \param pattern An integer whose bits describe the outline pattern.
48136     **/
48137     template<typename tc>
48138     CImg<T>& draw_circle(const int x0, const int y0, int radius,
48139                          const tc *const color, const float opacity,
48140                          const unsigned int pattern) {
48141       if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern);
48142       if (is_empty()) return *this;
48143       if (!color)
48144         throw CImgArgumentException(_cimg_instance
48145                                     "draw_circle(): Specified color is (null).",
48146                                     cimg_instance);
48147       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
48148       if (!radius) return draw_point(x0,y0,color,opacity);
48149 
48150       draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
48151         draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
48152       if (radius==1) return *this;
48153       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
48154         if (f>=0) { f+=(ddFy+=2); --y; }
48155         ++x; ++(f+=(ddFx+=2));
48156         if (x!=y + 1) {
48157           const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
48158             x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
48159           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
48160             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
48161           if (x!=y)
48162             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
48163               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
48164         }
48165       }
48166       return *this;
48167     }
48168 
48169     //! Draw an image.
48170     /**
48171        \param sprite Sprite image.
48172        \param x0 X-coordinate of the sprite position.
48173        \param y0 Y-coordinate of the sprite position.
48174        \param z0 Z-coordinate of the sprite position.
48175        \param c0 C-coordinate of the sprite position.
48176        \param opacity Drawing opacity.
48177     **/
48178     template<typename t>
48179     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48180                         const CImg<t>& sprite, const float opacity=1) {
48181       if (is_empty() || !sprite) return *this;
48182       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
48183       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
48184         return assign(sprite,false);
48185       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48186       const int
48187         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48188         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48189         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48190         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48191         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48192         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48193 
48194       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48195       if (lx>0 && ly>0 && lz>0 && lc>0) {
48196         for (int c = 0; c<lc; ++c)
48197           for (int z = 0; z<lz; ++z)
48198             for (int y = 0; y<ly; ++y) {
48199               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48200               const t *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48201               if (opacity>=1) for (int x = 0; x<lx; ++x) *(ptrd++) = (T)*(ptrs++);
48202               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
48203             }
48204       }
48205       return *this;
48206     }
48207 
48208     //! Draw an image \specialization.
48209     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48210                         const CImg<T>& sprite, const float opacity=1) {
48211       if (is_empty() || !sprite) return *this;
48212       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
48213       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
48214         return assign(sprite,false);
48215       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48216       const int
48217         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48218         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48219         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48220         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48221         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48222         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48223       const ulongT slx = lx*sizeof(T);
48224 
48225       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48226       if (lx>0 && ly>0 && lz>0 && lc>0) {
48227         for (int c = 0; c<lc; ++c)
48228           for (int z = 0; z<lz; ++z)
48229             for (int y = 0; y<ly; ++y) {
48230               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48231               const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48232               if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
48233               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
48234             }
48235       }
48236       return *this;
48237     }
48238 
48239     //! Draw an image \overloading.
48240     template<typename t>
48241     CImg<T>& draw_image(const int x0, const int y0, const int z0,
48242                         const CImg<t>& sprite, const float opacity=1) {
48243       return draw_image(x0,y0,z0,0,sprite,opacity);
48244     }
48245 
48246     //! Draw an image \overloading.
48247     template<typename t>
48248     CImg<T>& draw_image(const int x0, const int y0,
48249                         const CImg<t>& sprite, const float opacity=1) {
48250       return draw_image(x0,y0,0,sprite,opacity);
48251     }
48252 
48253     //! Draw an image \overloading.
48254     template<typename t>
48255     CImg<T>& draw_image(const int x0,
48256                         const CImg<t>& sprite, const float opacity=1) {
48257       return draw_image(x0,0,sprite,opacity);
48258     }
48259 
48260     //! Draw an image \overloading.
48261     template<typename t>
48262     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
48263       return draw_image(0,sprite,opacity);
48264     }
48265 
48266     //! Draw a masked image.
48267     /**
48268        \param sprite Sprite image.
48269        \param mask Mask image.
48270        \param x0 X-coordinate of the sprite position in the image instance.
48271        \param y0 Y-coordinate of the sprite position in the image instance.
48272        \param z0 Z-coordinate of the sprite position in the image instance.
48273        \param c0 C-coordinate of the sprite position in the image instance.
48274        \param mask_max_value Maximum pixel value of the mask image \c mask.
48275        \param opacity Drawing opacity.
48276        \note
48277        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
48278        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
48279     **/
48280     template<typename ti, typename tm>
48281     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48282                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48283                         const float mask_max_value=1) {
48284       if (is_empty() || !sprite || !mask) return *this;
48285       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
48286       if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
48287       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
48288         throw CImgArgumentException(_cimg_instance
48289                                     "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
48290                                     "incompatible dimensions.",
48291                                     cimg_instance,
48292                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
48293                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
48294 
48295       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48296       const int
48297         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48298         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48299         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48300         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48301         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48302         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48303       const ulongT msize = mask.size();
48304 
48305       if (lx>0 && ly>0 && lz>0 && lc>0) {
48306         for (int c = 0; c<lc; ++c)
48307           for (int z = 0; z<lz; ++z)
48308             for (int y = 0; y<ly; ++y) {
48309               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48310               const ti *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48311               const tm *ptrm = mask._data + (mask.offset(sx0,sy0 + y,sz0 + z,sc0 + c)%msize);
48312               for (int x = 0; x<lx; ++x) {
48313                 const float mopacity = (float)(*(ptrm++)*opacity),
48314                   nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f);
48315                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
48316                 ++ptrd;
48317               }
48318             }
48319       }
48320       return *this;
48321     }
48322 
48323     //! Draw a masked image \overloading.
48324     template<typename ti, typename tm>
48325     CImg<T>& draw_image(const int x0, const int y0, const int z0,
48326                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48327                         const float mask_max_value=1) {
48328       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
48329     }
48330 
48331     //! Draw a image \overloading.
48332     template<typename ti, typename tm>
48333     CImg<T>& draw_image(const int x0, const int y0,
48334                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48335                         const float mask_max_value=1) {
48336       return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
48337     }
48338 
48339     //! Draw a image \overloading.
48340     template<typename ti, typename tm>
48341     CImg<T>& draw_image(const int x0,
48342                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48343                         const float mask_max_value=1) {
48344       return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
48345     }
48346 
48347     //! Draw an image.
48348     template<typename ti, typename tm>
48349     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48350                         const float mask_max_value=1) {
48351       return draw_image(0,sprite,mask,opacity,mask_max_value);
48352     }
48353 
48354     //! Draw a text string.
48355     /**
48356        \param x0 X-coordinate of the text in the image instance.
48357        \param y0 Y-coordinate of the text in the image instance.
48358        \param text Format of the text ('printf'-style format string).
48359        \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
48360        \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
48361        \param opacity Drawing opacity.
48362        \param font Font used for drawing text.
48363     **/
48364     template<typename tc1, typename tc2, typename t>
48365     CImg<T>& draw_text(const int x0, const int y0,
48366                        const char *const text,
48367                        const tc1 *const foreground_color, const tc2 *const background_color,
48368                        const float opacity, const CImgList<t>& font, ...) {
48369       if (!font) return *this;
48370       CImg<charT> tmp(2048);
48371       std::va_list ap; va_start(ap,font);
48372       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48373       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
48374     }
48375 
48376     //! Draw a text string \overloading.
48377     /**
48378        \note A transparent background is used for the text.
48379     **/
48380     template<typename tc, typename t>
48381     CImg<T>& draw_text(const int x0, const int y0,
48382                        const char *const text,
48383                        const tc *const foreground_color, const int,
48384                        const float opacity, const CImgList<t>& font, ...) {
48385       if (!font) return *this;
48386       CImg<charT> tmp(2048);
48387       std::va_list ap; va_start(ap,font);
48388       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48389       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
48390     }
48391 
48392     //! Draw a text string \overloading.
48393     /**
48394        \note A transparent foreground is used for the text.
48395     **/
48396     template<typename tc, typename t>
48397     CImg<T>& draw_text(const int x0, const int y0,
48398                        const char *const text,
48399                        const int, const tc *const background_color,
48400                        const float opacity, const CImgList<t>& font, ...) {
48401       if (!font) return *this;
48402       CImg<charT> tmp(2048);
48403       std::va_list ap; va_start(ap,font);
48404       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48405       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
48406     }
48407 
48408     //! Draw a text string \overloading.
48409     /**
48410        \param x0 X-coordinate of the text in the image instance.
48411        \param y0 Y-coordinate of the text in the image instance.
48412        \param text Format of the text ('printf'-style format string).
48413        \param foreground_color Array of spectrum() values of type \c T,
48414          defining the foreground color (0 means 'transparent').
48415        \param background_color Array of spectrum() values of type \c T,
48416          defining the background color (0 means 'transparent').
48417        \param opacity Drawing opacity.
48418        \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise).
48419     **/
48420     template<typename tc1, typename tc2>
48421     CImg<T>& draw_text(const int x0, const int y0,
48422                        const char *const text,
48423                        const tc1 *const foreground_color, const tc2 *const background_color,
48424                        const float opacity=1, const unsigned int font_height=13, ...) {
48425       if (!font_height) return *this;
48426       CImg<charT> tmp(2048);
48427       std::va_list ap; va_start(ap,font_height);
48428       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48429       const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
48430       _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
48431       return *this;
48432     }
48433 
48434     //! Draw a text string \overloading.
48435     template<typename tc>
48436     CImg<T>& draw_text(const int x0, const int y0,
48437                        const char *const text,
48438                        const tc *const foreground_color, const int background_color=0,
48439                        const float opacity=1, const unsigned int font_height=13, ...) {
48440       if (!font_height) return *this;
48441       cimg::unused(background_color);
48442       CImg<charT> tmp(2048);
48443       std::va_list ap; va_start(ap,font_height);
48444       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48445       return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
48446     }
48447 
48448     //! Draw a text string \overloading.
48449     template<typename tc>
48450     CImg<T>& draw_text(const int x0, const int y0,
48451                        const char *const text,
48452                        const int, const tc *const background_color,
48453                        const float opacity=1, const unsigned int font_height=13, ...) {
48454       if (!font_height) return *this;
48455       CImg<charT> tmp(2048);
48456       std::va_list ap; va_start(ap,font_height);
48457       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48458       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
48459     }
48460 
48461     template<typename tc1, typename tc2, typename t>
48462     CImg<T>& _draw_text(const int x0, const int y0,
48463                         const char *const text,
48464                         const tc1 *const foreground_color, const tc2 *const background_color,
48465                         const float opacity, const CImgList<t>& font,
48466                         const bool is_native_font) {
48467       if (!text) return *this;
48468       if (!font)
48469         throw CImgArgumentException(_cimg_instance
48470                                     "draw_text(): Empty specified font.",
48471                                     cimg_instance);
48472 
48473       const unsigned int text_length = (unsigned int)std::strlen(text);
48474       const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
48475       unsigned char o_ch, ch = 0;
48476       int x, y, w;
48477       CImg<intT> left_paddings(text_length,1,1,1,0);
48478       const CImg<t> empty = CImg<t>::empty();
48479 
48480       if (is_empty() || is_native_font) {
48481         // Pre-compute necessary size of the image as well as left paddings of each character.
48482         x = y = w = 0;
48483         o_ch = 0;
48484         for (unsigned int i = 0; i<text_length; ++i) {
48485           ch = (unsigned char)text[i];
48486           switch (ch) {
48487           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
48488           case '\t' : x+=4*font[(int)' ']._width; break;
48489           case ' ' : x+=font[(int)' ']._width; break;
48490           default : if (ch<font._width) {
48491               int left_padding = 0;
48492               if (is_native_font && font[0]._height<128) {
48493                 // Determine left padding from various rules.
48494                 if (ch==':' || ch=='!' || ch=='.' || ch==';')
48495                   left_padding = 2*padding_x;
48496                 else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
48497                   left_padding = 4*padding_x;
48498                 else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
48499                           ((ch>='0' && ch<='9') ||
48500                            (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
48501                            (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
48502                          o_ch=='.' || o_ch=='\'' || ch=='\'')
48503                   left_padding = padding_x;
48504                 else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
48505                   const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
48506                   if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
48507                     const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
48508                     if (o_mask._height>13) {
48509                       const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
48510                       left_padding = -10;
48511                       cimg_forY(mask,k) {
48512                         const int
48513                           lpad = o_mask(w1,k)>=8?0:
48514                                  o_mask._width<=2 || o_mask(w2,k)>=8?-1:
48515                                  o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
48516                           rpad = mask(0,k)>=8?0:
48517                                  mask._width<=2 || mask(1,k)>=8?-1:
48518                                  mask._width<=3 || mask(2,k)>=8?-2:-3;
48519                         left_padding = std::max(left_padding,lpad + rpad);
48520                       }
48521                     }
48522                   }
48523                 }
48524                 left_paddings[i] = left_padding;
48525               }
48526               x+=left_padding + font[ch]._width + padding_x;
48527               o_ch = ch;
48528             }
48529           }
48530         }
48531         if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
48532         if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
48533       }
48534 
48535       // Draw font characters on image.
48536       x = x0; y = y0;
48537       for (unsigned int i = 0; i<text_length; ++i) {
48538         ch = (unsigned char)text[i];
48539         switch (ch) {
48540         case '\n' : y+=font[0]._height; x = x0; break;
48541         case '\t' :
48542         case ' ' : {
48543           const unsigned int lw = (ch=='\t'?4:1)*font[(int)' ']._width, lh = font[(int)' ']._height;
48544           if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
48545           x+=lw;
48546         } break;
48547         default : if (ch<font._width) {
48548             CImg<T> letter = font[ch];
48549             if (letter) {
48550               const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
48551               const int posx = x + left_paddings[i] + padding_x;
48552               if (is_native_font && _spectrum>letter._spectrum)
48553                 letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false);
48554               const unsigned int cmin = std::min(_spectrum,letter._spectrum);
48555               if (foreground_color)
48556                 for (unsigned int c = 0; c<cmin; ++c)
48557                   if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
48558               if (mask) { // Letter has mask
48559                 if (background_color)
48560                   for (unsigned int c = 0; c<cmin; ++c)
48561                     draw_rectangle(x,y,0,c,posx + letter._width - 1,y + letter._height - 1,0,c,
48562                                    background_color[c],opacity);
48563                 draw_image(posx,y,letter,font[ch + 256],opacity,255.f);
48564               } else draw_image(posx,y,letter,opacity); // Letter has no mask
48565               x = posx + letter._width;
48566             }
48567           }
48568         }
48569       }
48570       return *this;
48571     }
48572 
48573     // [internal] Version used to display text in interactive viewers.
48574     CImg<T>& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) {
48575       CImg<charT> tmp(2048);
48576       std::va_list ap;
48577       va_start(ap,is_down);
48578       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48579       CImg<ucharT> a_label, a_labelmask;
48580       const unsigned char a_labelcolor = 255;
48581       unsigned int ofs = font_size, fs = ofs;
48582       do { // Determine best font size
48583         a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data);
48584         if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) {
48585           font_size = fs; break;
48586         } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) {
48587           ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f));
48588         } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) {
48589           ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f));
48590         } else { font_size = fs; break; }
48591       } while (true);
48592       a_label.normalize(0,255);
48593       a_label+=(255 - a_label.get_dilate(3)).normalize(0,80);
48594       a_label.resize(-100,-100,1,3,1);
48595       return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f);
48596     }
48597 
48598     //! Draw a 2D vector field.
48599     /**
48600        \param flow Image of 2D vectors used as input data.
48601        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48602        \param opacity Drawing opacity.
48603        \param sampling Length (in pixels) between each arrow.
48604        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
48605        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
48606        \param pattern Used pattern to draw lines.
48607        \note Clipping is supported.
48608     **/
48609     template<typename t1, typename t2>
48610     CImg<T>& draw_quiver(const CImg<t1>& flow,
48611                          const t2 *const color, const float opacity=1,
48612                          const unsigned int sampling=25, const float factor=-20,
48613                          const bool is_arrow=true, const unsigned int pattern=~0U) {
48614       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
48615     }
48616 
48617     //! Draw a 2D vector field, using a field of colors.
48618     /**
48619        \param flow Image of 2D vectors used as input data.
48620        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
48621        \param opacity Opacity of the drawing.
48622        \param sampling Length (in pixels) between each arrow.
48623        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
48624        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
48625        \param pattern Used pattern to draw lines.
48626        \note Clipping is supported.
48627     **/
48628     template<typename t1, typename t2>
48629     CImg<T>& draw_quiver(const CImg<t1>& flow,
48630                          const CImg<t2>& color, const float opacity=1,
48631                          const unsigned int sampling=25, const float factor=-20,
48632                          const bool is_arrow=true, const unsigned int pattern=~0U) {
48633       if (is_empty()) return *this;
48634       if (!flow || flow._spectrum!=2)
48635         throw CImgArgumentException(_cimg_instance
48636                                     "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
48637                                     cimg_instance,
48638                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
48639       if (sampling<=0)
48640         throw CImgArgumentException(_cimg_instance
48641                                     "draw_quiver(): Invalid sampling value %g "
48642                                     "(should be >0)",
48643                                     cimg_instance,
48644                                     sampling);
48645       const bool colorfield = (color._width==flow._width && color._height==flow._height &&
48646                                color._depth==1 && color._spectrum==_spectrum);
48647       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
48648       float vmax,fact;
48649       if (factor<=0) {
48650         float m, M = (float)flow.get_norm(2).max_min(m);
48651         vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
48652         if (!vmax) vmax = 1;
48653         fact = -factor;
48654       } else { fact = factor; vmax = 1; }
48655 
48656       for (unsigned int y = sampling/2; y<_height; y+=sampling)
48657         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
48658           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
48659           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
48660           if (is_arrow) {
48661             const int xx = (int)(x + u), yy = (int)(y + v);
48662             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern);
48663             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern);
48664           } else {
48665             if (colorfield)
48666               draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
48667                         color.get_vector_at(X,Y)._data,opacity,pattern);
48668             else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
48669                            color._data,opacity,pattern);
48670           }
48671         }
48672       return *this;
48673     }
48674 
48675     //! Draw a labeled horizontal axis.
48676     /**
48677        \param values_x Values along the horizontal axis.
48678        \param y Y-coordinate of the horizontal axis in the image instance.
48679        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48680        \param opacity Drawing opacity.
48681        \param pattern Drawing pattern.
48682        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48683        \param allow_zero Enable/disable the drawing of label '0' if found.
48684     **/
48685     template<typename t, typename tc>
48686     CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
48687                        const tc *const color, const float opacity=1,
48688                        const unsigned int pattern=~0U, const unsigned int font_height=13,
48689                        const bool allow_zero=true, const float round_x=0) {
48690       if (is_empty()) return *this;
48691       const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
48692       const int siz = (int)values_x.size() - 1;
48693       CImg<charT> txt(32);
48694       CImg<T> a_label;
48695       if (siz<=0) { // Degenerated case
48696         draw_line(0,y,_width - 1,y,color,opacity,pattern);
48697         if (!siz) {
48698           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x);
48699           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48700           const int
48701             _xt = (width() - a_label.width())/2,
48702             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
48703           draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
48704           if (allow_zero || *txt!='0' || txt[1]!=0)
48705             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48706         }
48707       } else { // Regular case
48708         if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
48709         else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
48710         cimg_foroff(values_x,x) {
48711           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)values_x(x),round_x):(double)values_x(x));
48712           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48713           const int
48714             xi = (int)(x*(_width - 1)/siz),
48715             _xt = xi - a_label.width()/2,
48716             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
48717           draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
48718           if (allow_zero || *txt!='0' || txt[1]!=0)
48719             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48720         }
48721       }
48722       return *this;
48723     }
48724 
48725     //! Draw a labeled vertical axis.
48726     /**
48727        \param x X-coordinate of the vertical axis in the image instance.
48728        \param values_y Values along the Y-axis.
48729        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48730        \param opacity Drawing opacity.
48731        \param pattern Drawing pattern.
48732        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48733        \param allow_zero Enable/disable the drawing of label '0' if found.
48734     **/
48735     template<typename t, typename tc>
48736     CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
48737                        const tc *const color, const float opacity=1,
48738                        const unsigned int pattern=~0U, const unsigned int font_height=13,
48739                        const bool allow_zero=true, const float round_y=0) {
48740       if (is_empty()) return *this;
48741       int siz = (int)values_y.size() - 1;
48742       CImg<charT> txt(32);
48743       CImg<T> a_label;
48744       if (siz<=0) { // Degenerated case
48745         draw_line(x,0,x,_height - 1,color,opacity,pattern);
48746         if (!siz) {
48747           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y);
48748           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48749           const int
48750             _yt = (height() - a_label.height())/2,
48751             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
48752             _xt = x - 2 - a_label.width(),
48753             xt = _xt>=0?_xt:x + 3;
48754           draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
48755           if (allow_zero || *txt!='0' || txt[1]!=0)
48756             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48757         }
48758       } else { // Regular case
48759         if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
48760         else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
48761         cimg_foroff(values_y,y) {
48762           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)values_y(y),round_y):(double)values_y(y));
48763           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48764           const int
48765             yi = (int)(y*(_height - 1)/siz),
48766             _yt = yi - a_label.height()/2,
48767             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
48768             _xt = x - 2 - a_label.width(),
48769             xt = _xt>=0?_xt:x + 3;
48770           draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
48771           if (allow_zero || *txt!='0' || txt[1]!=0)
48772             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48773         }
48774       }
48775       return *this;
48776     }
48777 
48778     //! Draw labeled horizontal and vertical axes.
48779     /**
48780        \param values_x Values along the X-axis.
48781        \param values_y Values along the Y-axis.
48782        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48783        \param opacity Drawing opacity.
48784        \param pattern_x Drawing pattern for the X-axis.
48785        \param pattern_y Drawing pattern for the Y-axis.
48786        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48787        \param allow_zero Enable/disable the drawing of label '0' if found.
48788     **/
48789     template<typename tx, typename ty, typename tc>
48790     CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
48791                        const tc *const color, const float opacity=1,
48792                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
48793                        const unsigned int font_height=13, const bool allow_zero=true,
48794                        const float round_x=0, const float round_y=0) {
48795       if (is_empty()) return *this;
48796       const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
48797       const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
48798       if (sizx>=0) {
48799         float ox = (float)*nvalues_x;
48800         for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
48801           const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
48802           if (nx*ox<=0) {
48803             draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y);
48804             break;
48805           }
48806           ox = nx;
48807         }
48808       }
48809       const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
48810       const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
48811       if (sizy>0) {
48812         float oy = (float)nvalues_y[0];
48813         for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
48814           const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
48815           if (ny*oy<=0) {
48816             draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x);
48817             break;
48818           }
48819           oy = ny;
48820         }
48821       }
48822       return *this;
48823     }
48824 
48825     //! Draw labeled horizontal and vertical axes \overloading.
48826     template<typename tc>
48827     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
48828                        const tc *const color, const float opacity=1,
48829                        const int subdivisionx=-60, const int subdivisiony=-60,
48830                        const float precisionx=0, const float precisiony=0,
48831                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
48832                        const unsigned int font_height=13) {
48833       if (is_empty()) return *this;
48834       const bool allow_zero = (x0*x1>0) || (y0*y1>0);
48835       const float
48836         dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
48837         px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx,
48838         py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony;
48839       if (x0!=x1 && y0!=y1)
48840         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),
48841                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
48842                   color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py);
48843       else if (x0==x1 && y0!=y1)
48844         draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
48845                   color,opacity,pattern_y,font_height,py);
48846       else if (x0!=x1 && y0==y1)
48847         draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0,
48848                   color,opacity,pattern_x,font_height,px);
48849       return *this;
48850     }
48851 
48852     //! Draw 2D grid.
48853     /**
48854        \param values_x X-coordinates of the vertical lines.
48855        \param values_y Y-coordinates of the horizontal lines.
48856        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48857        \param opacity Drawing opacity.
48858        \param pattern_x Drawing pattern for vertical lines.
48859        \param pattern_y Drawing pattern for horizontal lines.
48860     **/
48861     template<typename tx, typename ty, typename tc>
48862     CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
48863                        const tc *const color, const float opacity=1,
48864                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
48865       if (is_empty()) return *this;
48866       if (values_x) cimg_foroff(values_x,x) {
48867           const int xi = (int)values_x[x];
48868           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
48869         }
48870       if (values_y) cimg_foroff(values_y,y) {
48871           const int yi = (int)values_y[y];
48872           if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
48873         }
48874       return *this;
48875     }
48876 
48877     //! Draw 2D grid \simplification.
48878     template<typename tc>
48879     CImg<T>& draw_grid(const float delta_x,  const float delta_y,
48880                        const float offsetx, const float offsety,
48881                        const bool invertx, const bool inverty,
48882                        const tc *const color, const float opacity=1,
48883                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
48884       if (is_empty()) return *this;
48885       CImg<uintT> seqx, seqy;
48886       if (delta_x!=0) {
48887         const float dx = delta_x>0?delta_x:_width*-delta_x/100;
48888         const unsigned int nx = (unsigned int)(_width/dx);
48889         seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
48890         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
48891         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
48892       }
48893       if (delta_y!=0) {
48894         const float dy = delta_y>0?delta_y:_height*-delta_y/100;
48895         const unsigned int ny = (unsigned int)(_height/dy);
48896         seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
48897         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
48898         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
48899      }
48900       return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
48901     }
48902 
48903     //! Draw 1D graph.
48904     /**
48905        \param data Image containing the graph values I = f(x).
48906        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48907        \param opacity Drawing opacity.
48908 
48909        \param plot_type Define the type of the plot:
48910                       - 0 = No plot.
48911                       - 1 = Plot using segments.
48912                       - 2 = Plot using cubic splines.
48913                       - 3 = Plot with bars.
48914        \param vertex_type Define the type of points:
48915                       - 0 = No points.
48916                       - 1 = Point.
48917                       - 2 = Straight cross.
48918                       - 3 = Diagonal cross.
48919                       - 4 = Filled circle.
48920                       - 5 = Outlined circle.
48921                       - 6 = Square.
48922                       - 7 = Diamond.
48923        \param ymin Lower bound of the y-range.
48924        \param ymax Upper bound of the y-range.
48925        \param pattern Drawing pattern.
48926        \note
48927          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
48928     **/
48929     template<typename t, typename tc>
48930     CImg<T>& draw_graph(const CImg<t>& data,
48931                         const tc *const color, const float opacity=1,
48932                         const unsigned int plot_type=1, const int vertex_type=1,
48933                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
48934       if (is_empty() || _height<=1) return *this;
48935       if (!color)
48936         throw CImgArgumentException(_cimg_instance
48937                                     "draw_graph(): Specified color is (null).",
48938                                     cimg_instance);
48939 
48940       // Create shaded colors for displaying bar plots.
48941       CImg<tc> color1, color2;
48942       if (plot_type==3) {
48943         color1.assign(_spectrum); color2.assign(_spectrum);
48944         cimg_forC(*this,c) {
48945           color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
48946           color2[c] = (tc)(color[c]*0.4f);
48947         }
48948       }
48949 
48950       // Compute min/max and normalization factors.
48951       const ulongT
48952         siz = data.size(),
48953         _siz1 = siz - (plot_type!=3),
48954         siz1 = _siz1?_siz1:1;
48955       const unsigned int
48956         _width1 = _width - (plot_type!=3),
48957         width1 = _width1?_width1:1;
48958       double m = ymin, M = ymax;
48959       if (ymin==ymax) m = (double)data.max_min(M);
48960       if (m==M) { --m; ++M; }
48961       const float ca = (float)(M-m)/(_height - 1);
48962       bool init_hatch = true;
48963 
48964       // Draw graph edges
48965       switch (plot_type%4) {
48966       case 1 : { // Segments
48967         int oX = 0, oY = (int)cimg::round((data[0] - m)/ca);
48968         if (siz==1) {
48969           const int Y = (int)cimg::round((*data - m)/ca);
48970           draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
48971         } else {
48972           const float fx = (float)_width/siz1;
48973           for (ulongT off = 1; off<siz; ++off) {
48974             const int
48975               X = (int)cimg::round(off*fx) - 1,
48976               Y = (int)cimg::round((data[off]-m)/ca);
48977             draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
48978             oX = X; oY = Y;
48979             init_hatch = false;
48980           }
48981         }
48982       } break;
48983       case 2 : { // Spline
48984         const CImg<t> ndata(data._data,siz,1,1,1,true);
48985         int oY = (int)cimg::round((data[0] - m)/ca);
48986         cimg_forX(*this,x) {
48987           const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
48988           if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
48989           init_hatch = false;
48990           oY = Y;
48991         }
48992       } break;
48993       case 3 : { // Bars
48994         const int Y0 = (int)cimg::round(-m/ca);
48995         const float fx = (float)_width/siz1;
48996         int oX = 0;
48997         cimg_foroff(data,off) {
48998           const int
48999             X = (int)cimg::round((off + 1)*fx) - 1,
49000             Y = (int)cimg::round((data[off] - m)/ca);
49001           draw_rectangle(oX,Y0,X,Y,color,opacity).
49002             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
49003             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
49004             draw_line(X,Y,X,Y0,color1.data(),opacity).
49005             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
49006           oX = X + 1;
49007         }
49008       } break;
49009       default : break; // No edges
49010       }
49011 
49012       // Draw graph points
49013       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
49014       const float fx = (float)_width1/siz1;
49015       switch (vertex_type%8) {
49016       case 1 : { // Point
49017         cimg_foroff(data,off) {
49018           const int
49019             X = (int)cimg::round(off*fx + wb2),
49020             Y = (int)cimg::round((data[off]-m)/ca);
49021           draw_point(X,Y,color,opacity);
49022         }
49023       } break;
49024       case 2 : { // Straight Cross
49025         cimg_foroff(data,off) {
49026           const int
49027             X = (int)cimg::round(off*fx + wb2),
49028             Y = (int)cimg::round((data[off]-m)/ca);
49029           draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
49030         }
49031       } break;
49032       case 3 : { // Diagonal Cross
49033         cimg_foroff(data,off) {
49034           const int
49035             X = (int)cimg::round(off*fx + wb2),
49036             Y = (int)cimg::round((data[off]-m)/ca);
49037           draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
49038         }
49039       } break;
49040       case 4 : { // Filled Circle
49041         cimg_foroff(data,off) {
49042           const int
49043             X = (int)cimg::round(off*fx + wb2),
49044             Y = (int)cimg::round((data[off]-m)/ca);
49045           draw_circle(X,Y,3,color,opacity);
49046         }
49047       } break;
49048       case 5 : { // Outlined circle
49049         cimg_foroff(data,off) {
49050           const int
49051             X = (int)cimg::round(off*fx + wb2),
49052             Y = (int)cimg::round((data[off]-m)/ca);
49053           draw_circle(X,Y,3,color,opacity,~0U);
49054         }
49055       } break;
49056       case 6 : { // Square
49057         cimg_foroff(data,off) {
49058           const int
49059             X = (int)cimg::round(off*fx + wb2),
49060             Y = (int)cimg::round((data[off]-m)/ca);
49061           draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
49062         }
49063       } break;
49064       case 7 : { // Diamond
49065         cimg_foroff(data,off) {
49066           const int
49067             X = (int)cimg::round(off*fx + wb2),
49068             Y = (int)cimg::round((data[off]-m)/ca);
49069           draw_line(X,Y - 4,X + 4,Y,color,opacity).
49070             draw_line(X + 4,Y,X,Y + 4,color,opacity).
49071             draw_line(X,Y + 4,X - 4,Y,color,opacity).
49072             draw_line(X - 4,Y,X,Y - 4,color,opacity);
49073         }
49074       } break;
49075       default : break; // No points
49076       }
49077       return *this;
49078     }
49079 
49080     bool _draw_fill(const int x, const int y, const int z,
49081                     const CImg<T>& ref, const float tolerance2) const {
49082       const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
49083       const ulongT off = _width*_height*_depth;
49084       float diff = 0;
49085       cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
49086       return diff<=tolerance2;
49087     }
49088 
49089     //! Draw filled 3D region with the flood fill algorithm.
49090     /**
49091        \param x0 X-coordinate of the starting point of the region to fill.
49092        \param y0 Y-coordinate of the starting point of the region to fill.
49093        \param z0 Z-coordinate of the starting point of the region to fill.
49094        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49095        \param[out] region Image that will contain the mask of the filled region mask, as an output.
49096        \param tolerance Tolerance concerning neighborhood values.
49097        \param opacity Opacity of the drawing.
49098        \param is_high_connectivity Tells if 8-connexity must be used.
49099        \return \c region is initialized with the binary mask of the filled region.
49100     **/
49101     template<typename tc, typename t>
49102     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
49103                         const tc *const color, const float opacity,
49104                         CImg<t> &region,
49105                         const float tolerance = 0,
49106                         const bool is_high_connectivity = false) {
49107 #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
49108                                stack[N] = x; stack(N,1) = y; stack(N++,2) = z
49109 #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
49110 #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
49111 
49112       if (!containsXYZC(x0,y0,z0,0)) return *this;
49113       const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f);
49114       const float tolerance2 = cimg::sqr(tolerance);
49115       const CImg<T> ref = get_vector_at(x0,y0,z0);
49116       CImg<uintT> stack(256,1,1,3);
49117       CImg<ucharT> _region(_width,_height,_depth,1,0);
49118       unsigned int N = 0;
49119       int x, y, z;
49120 
49121       _draw_fill_push(x0,y0,z0);
49122       while (N>0) {
49123         _draw_fill_pop(x,y,z);
49124         if (!_region(x,y,z)) {
49125           const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
49126           int xl = x, xr = x;
49127 
49128           // Using these booleans reduces the number of pushes drastically.
49129           bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
49130           for (int step = -1; step<2; step+=2) {
49131             while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
49132               if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
49133                 if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
49134               } else is_yp = false;
49135               if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
49136                 if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
49137               } else is_yn = false;
49138               if (depth()>1) {
49139                 if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
49140                   if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
49141                 } else is_zp = false;
49142                 if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
49143                   if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
49144                 } else is_zn = false;
49145               }
49146               if (is_high_connectivity) {
49147                 const int xp = x - 1, xn = x + 1;
49148                 if (yp>=0 && !is_yp) {
49149                   if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
49150                     _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
49151                   }
49152                   if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
49153                     _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
49154                   }
49155                 }
49156                 if (yn<height() && !is_yn) {
49157                   if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
49158                     _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
49159                   }
49160                   if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
49161                     _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
49162                   }
49163                 }
49164                 if (depth()>1) {
49165                   if (zp>=0 && !is_zp) {
49166                     if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
49167                       _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
49168                     }
49169                     if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
49170                       _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
49171                     }
49172 
49173                     if (yp>=0 && !is_yp) {
49174                       if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
49175                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
49176                       if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
49177                     }
49178                     if (yn<height() && !is_yn) {
49179                       if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
49180                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
49181                       if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
49182                     }
49183                   }
49184 
49185                   if (zn<depth() && !is_zn) {
49186                     if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
49187                       _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
49188                     }
49189                     if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
49190                       _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
49191                     }
49192 
49193                     if (yp>=0 && !is_yp) {
49194                       if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
49195                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
49196                       if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
49197                     }
49198                     if (yn<height() && !is_yn) {
49199                       if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
49200                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
49201                       if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
49202                     }
49203                   }
49204                 }
49205               }
49206               x+=step;
49207             }
49208             if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
49209             else xr = --x;
49210           }
49211           std::memset(_region.data(xl,y,z),1,xr - xl + 1);
49212           if (opacity==1) {
49213             if (sizeof(T)==1) {
49214               const int dx = xr - xl + 1;
49215               cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
49216             } else cimg_forC(*this,c) {
49217                 const T val = (T)color[c];
49218                 T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
49219               }
49220           } else cimg_forC(*this,c) {
49221               const T val = (T)(color[c]*nopacity);
49222               T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
49223             }
49224         }
49225       }
49226       _region.move_to(region);
49227       return *this;
49228     }
49229 
49230     //! Draw filled 3D region with the flood fill algorithm \simplification.
49231     template<typename tc>
49232     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
49233                        const tc *const color, const float opacity=1,
49234                        const float tolerance=0, const bool is_high_connexity=false) {
49235       CImg<ucharT> tmp;
49236       return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
49237     }
49238 
49239     //! Draw filled 2D region with the flood fill algorithm \simplification.
49240     template<typename tc>
49241     CImg<T>& draw_fill(const int x0, const int y0,
49242                        const tc *const color, const float opacity=1,
49243                        const float tolerance=0, const bool is_high_connexity=false) {
49244       CImg<ucharT> tmp;
49245       return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
49246     }
49247 
49248     //! Draw a random plasma texture.
49249     /**
49250        \param alpha Alpha-parameter.
49251        \param beta Beta-parameter.
49252        \param scale Scale-parameter.
49253        \note Use the mid-point algorithm to render.
49254     **/
49255     CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
49256       if (is_empty()) return *this;
49257       const int w = width(), h = height();
49258       const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
49259       cimg_uint64 rng = (cimg::_rand(),cimg::rng());
49260       cimg_forZC(*this,z,c) {
49261         CImg<T> ref = get_shared_slice(z,c);
49262         for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
49263           const int delta2 = delta>>1;
49264           const float r = alpha*delta + beta;
49265 
49266           // Square step.
49267           for (int y0 = 0; y0<h; y0+=delta)
49268             for (int x0 = 0; x0<w; x0+=delta) {
49269               const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
49270               const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
49271                                           r*cimg::rand(-1,1,&rng));
49272               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49273             }
49274 
49275           // Diamond steps.
49276           for (int y = -delta2; y<h; y+=delta)
49277             for (int x0=0; x0<w; x0+=delta) {
49278               const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
49279                 xc = (x0 + delta2)%w, yc = (y + delta2)%h;
49280               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49281                                           r*cimg::rand(-1,1,&rng));
49282               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49283             }
49284           for (int y0 = 0; y0<h; y0+=delta)
49285             for (int x = -delta2; x<w; x+=delta) {
49286               const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
49287                 xc = (x + delta2)%w, yc = (y0 + delta2)%h;
49288               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49289                                           r*cimg::rand(-1,1,&rng));
49290               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49291             }
49292           for (int y = -delta2; y<h; y+=delta)
49293             for (int x = -delta2; x<w; x+=delta) {
49294               const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
49295                 xc = (x + delta2)%w, yc = (y + delta2)%h;
49296               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49297                                           r*cimg::rand(-1,1,&rng));
49298                 ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49299             }
49300         }
49301       }
49302       cimg::srand(rng);
49303       return *this;
49304     }
49305 
49306     //! Draw a quadratic Mandelbrot or Julia 2D fractal.
49307     /**
49308        \param x0 X-coordinate of the upper-left pixel.
49309        \param y0 Y-coordinate of the upper-left pixel.
49310        \param x1 X-coordinate of the lower-right pixel.
49311        \param y1 Y-coordinate of the lower-right pixel.
49312        \param colormap Colormap.
49313        \param opacity Drawing opacity.
49314        \param z0r Real part of the upper-left fractal vertex.
49315        \param z0i Imaginary part of the upper-left fractal vertex.
49316        \param z1r Real part of the lower-right fractal vertex.
49317        \param z1i Imaginary part of the lower-right fractal vertex.
49318        \param iteration_max Maximum number of iterations for each estimated point.
49319        \param is_normalized_iteration Tells if iterations are normalized.
49320        \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
49321        \param param_r Real part of the Julia set parameter.
49322        \param param_i Imaginary part of the Julia set parameter.
49323        \note Fractal rendering is done by the Escape Time Algorithm.
49324     **/
49325     template<typename tc>
49326     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
49327                              const CImg<tc>& colormap, const float opacity=1,
49328                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
49329                              const unsigned int iteration_max=255,
49330                              const bool is_normalized_iteration=false,
49331                              const bool is_julia_set=false,
49332                              const double param_r=0, const double param_i=0) {
49333       if (is_empty()) return *this;
49334       CImg<tc> palette;
49335       if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
49336       if (palette && palette._spectrum!=_spectrum)
49337         throw CImgArgumentException(_cimg_instance
49338                                     "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
49339                                     "incompatible dimensions.",
49340                                     cimg_instance,
49341                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
49342 
49343       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.);
49344       const int
49345         _x0 = cimg::cut(x0,0,width() - 1),
49346         _y0 = cimg::cut(y0,0,height() - 1),
49347         _x1 = cimg::cut(x1,0,width() - 1),
49348         _y1 = cimg::cut(y1,0,height() - 1);
49349 
49350       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
49351                          cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048))
49352       for (int q = _y0; q<=_y1; ++q)
49353         for (int p = _x0; p<=_x1; ++p) {
49354           unsigned int iteration = 0;
49355           const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
49356           double zr, zi, cr, ci;
49357           if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
49358           else { zr = param_r; zi = param_i; cr = x; ci = y; }
49359           for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
49360             const double temp = zr*zr - zi*zi + cr;
49361             zi = 2*zr*zi + ci;
49362             zr = temp;
49363           }
49364           if (iteration>iteration_max) {
49365             if (palette) {
49366               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
49367               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
49368             } else {
49369               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
49370               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
49371             }
49372           } else if (is_normalized_iteration) {
49373             const float
49374               normz = (float)cimg::abs(zr*zr + zi*zi),
49375               niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
49376             if (palette) {
49377               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
49378               else cimg_forC(*this,c)
49379                      (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
49380             } else {
49381               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
49382               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
49383             }
49384           } else {
49385             if (palette) {
49386               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
49387               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
49388             } else {
49389               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
49390               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
49391             }
49392           }
49393         }
49394       return *this;
49395     }
49396 
49397     //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading.
49398     template<typename tc>
49399     CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
49400                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
49401                              const unsigned int iteration_max=255,
49402                              const bool is_normalized_iteration=false,
49403                              const bool is_julia_set=false,
49404                              const double param_r=0, const double param_i=0) {
49405       return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
49406                              z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
49407     }
49408 
49409     //! Draw a 1D gaussian function.
49410     /**
49411        \param xc X-coordinate of the gaussian center.
49412        \param sigma Standard variation of the gaussian distribution.
49413        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49414        \param opacity Drawing opacity.
49415     **/
49416     template<typename tc>
49417     CImg<T>& draw_gaussian(const float xc, const float sigma,
49418                            const tc *const color, const float opacity=1) {
49419       if (is_empty()) return *this;
49420       if (!color)
49421         throw CImgArgumentException(_cimg_instance
49422                                     "draw_gaussian(): Specified color is (null).",
49423                                     cimg_instance);
49424       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
49425       const ulongT whd = (ulongT)_width*_height*_depth;
49426       const tc *col = color;
49427       cimg_forX(*this,x) {
49428         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
49429         T *ptrd = data(x,0,0,0);
49430         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
49431         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
49432         col-=_spectrum;
49433       }
49434       return *this;
49435     }
49436 
49437     //! Draw a 2D gaussian function.
49438     /**
49439        \param xc X-coordinate of the gaussian center.
49440        \param yc Y-coordinate of the gaussian center.
49441        \param tensor Covariance matrix (must be 2x2).
49442        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49443        \param opacity Drawing opacity.
49444     **/
49445     template<typename t, typename tc>
49446     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
49447                            const tc *const color, const float opacity=1) {
49448       if (is_empty()) return *this;
49449       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
49450         throw CImgArgumentException(_cimg_instance
49451                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
49452                                     cimg_instance,
49453                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
49454       if (!color)
49455         throw CImgArgumentException(_cimg_instance
49456                                     "draw_gaussian(): Specified color is (null).",
49457                                     cimg_instance);
49458       typedef typename CImg<t>::Tfloat tfloat;
49459       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
49460       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
49461       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
49462       const ulongT whd = (ulongT)_width*_height*_depth;
49463       const tc *col = color;
49464       float dy = -yc;
49465       cimg_forY(*this,y) {
49466         float dx = -xc;
49467         cimg_forX(*this,x) {
49468           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
49469           T *ptrd = data(x,y,0,0);
49470           if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
49471           else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
49472           col-=_spectrum;
49473           ++dx;
49474         }
49475         ++dy;
49476       }
49477       return *this;
49478     }
49479 
49480     //! Draw a 2D gaussian function \overloading.
49481     template<typename tc>
49482     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
49483                            const tc *const color, const float opacity=1) {
49484       const double
49485         a = r1*ru*ru + r2*rv*rv,
49486         b = (r1-r2)*ru*rv,
49487         c = r1*rv*rv + r2*ru*ru;
49488       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
49489       return draw_gaussian(xc,yc,tensor,color,opacity);
49490     }
49491 
49492     //! Draw a 2D gaussian function \overloading.
49493     template<typename tc>
49494     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
49495                            const tc *const color, const float opacity=1) {
49496       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
49497     }
49498 
49499     //! Draw a 3D gaussian function \overloading.
49500     template<typename t, typename tc>
49501     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
49502                            const tc *const color, const float opacity=1) {
49503       if (is_empty()) return *this;
49504       typedef typename CImg<t>::Tfloat tfloat;
49505       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
49506         throw CImgArgumentException(_cimg_instance
49507                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
49508                                     cimg_instance,
49509                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
49510 
49511       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
49512       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);
49513       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
49514       const ulongT whd = (ulongT)_width*_height*_depth;
49515       const tc *col = color;
49516       cimg_forXYZ(*this,x,y,z) {
49517         const float
49518           dx = (x - xc), dy = (y - yc), dz = (z - zc),
49519           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
49520         T *ptrd = data(x,y,z,0);
49521         if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
49522         else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
49523         col-=_spectrum;
49524       }
49525       return *this;
49526     }
49527 
49528     //! Draw a 3D gaussian function \overloading.
49529     template<typename tc>
49530     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
49531                            const tc *const color, const float opacity=1) {
49532       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
49533     }
49534 
49535     //! Draw a 3D object.
49536     /**
49537        \param x0 X-coordinate of the 3D object position
49538        \param y0 Y-coordinate of the 3D object position
49539        \param z0 Z-coordinate of the 3D object position
49540        \param vertices Image Nx3 describing 3D point coordinates
49541        \param primitives List of P primitives
49542        \param colors List of P color (or textures)
49543        \param opacities Image or list of P opacities
49544        \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
49545        \param is_double_sided Tells if object faces have two sides or are oriented.
49546        \param focale length of the focale (0 for parallel projection)
49547        \param lightx X-coordinate of the light
49548        \param lighty Y-coordinate of the light
49549        \param lightz Z-coordinate of the light
49550        \param specular_lightness Amount of specular light.
49551        \param specular_shininess Shininess of the object
49552        \param g_opacity Global opacity of the object.
49553     **/
49554     template<typename tp, typename tf, typename tc, typename to>
49555     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49556                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49557                            const CImgList<tc>& colors, const CImg<to>& opacities,
49558                            const unsigned int render_type=4,
49559                            const bool is_double_sided=false, const float focale=700,
49560                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49561                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49562                            const float g_opacity=1) {
49563       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49564                            is_double_sided,focale,lightx,lighty,lightz,
49565                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49566     }
49567 
49568     //! Draw a 3D object \simplification.
49569     template<typename tp, typename tf, typename tc, typename to, typename tz>
49570     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49571                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49572                            const CImgList<tc>& colors, const CImg<to>& opacities,
49573                            const unsigned int render_type,
49574                            const bool is_double_sided, const float focale,
49575                            const float lightx, const float lighty, const float lightz,
49576                            const float specular_lightness, const float specular_shininess,
49577                            const float g_opacity, CImg<tz>& zbuffer) {
49578       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49579                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49580                             specular_lightness,specular_shininess,g_opacity,1);
49581     }
49582 
49583 #ifdef cimg_use_board
49584     template<typename tp, typename tf, typename tc, typename to>
49585     CImg<T>& draw_object3d(LibBoard::Board& board,
49586                            const float x0, const float y0, const float z0,
49587                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49588                            const CImgList<tc>& colors, const CImg<to>& opacities,
49589                            const unsigned int render_type=4,
49590                            const bool is_double_sided=false, const float focale=700,
49591                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49592                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49593                            const float g_opacity=1) {
49594       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49595                            is_double_sided,focale,lightx,lighty,lightz,
49596                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49597     }
49598 
49599     template<typename tp, typename tf, typename tc, typename to, typename tz>
49600     CImg<T>& draw_object3d(LibBoard::Board& board,
49601                            const float x0, const float y0, const float z0,
49602                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49603                            const CImgList<tc>& colors, const CImg<to>& opacities,
49604                            const unsigned int render_type,
49605                            const bool is_double_sided, const float focale,
49606                            const float lightx, const float lighty, const float lightz,
49607                            const float specular_lightness, const float specular_shininess,
49608                            const float g_opacity, CImg<tz>& zbuffer) {
49609       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49610                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49611                             specular_lightness,specular_shininess,g_opacity,1);
49612     }
49613 #endif
49614 
49615     //! Draw a 3D object \simplification.
49616     template<typename tp, typename tf, typename tc, typename to>
49617     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49618                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49619                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49620                            const unsigned int render_type=4,
49621                            const bool is_double_sided=false, const float focale=700,
49622                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49623                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49624                            const float g_opacity=1) {
49625       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49626                            is_double_sided,focale,lightx,lighty,lightz,
49627                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49628     }
49629 
49630     //! Draw a 3D object \simplification.
49631     template<typename tp, typename tf, typename tc, typename to, typename tz>
49632     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49633                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49634                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49635                            const unsigned int render_type,
49636                            const bool is_double_sided, const float focale,
49637                            const float lightx, const float lighty, const float lightz,
49638                            const float specular_lightness, const float specular_shininess,
49639                            const float g_opacity, CImg<tz>& zbuffer) {
49640       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49641                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49642                             specular_lightness,specular_shininess,g_opacity,1);
49643     }
49644 
49645 #ifdef cimg_use_board
49646     template<typename tp, typename tf, typename tc, typename to>
49647     CImg<T>& draw_object3d(LibBoard::Board& board,
49648                            const float x0, const float y0, const float z0,
49649                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49650                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49651                            const unsigned int render_type=4,
49652                            const bool is_double_sided=false, const float focale=700,
49653                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49654                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49655                            const float g_opacity=1) {
49656       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49657                            is_double_sided,focale,lightx,lighty,lightz,
49658                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49659     }
49660 
49661     template<typename tp, typename tf, typename tc, typename to, typename tz>
49662     CImg<T>& draw_object3d(LibBoard::Board& board,
49663                            const float x0, const float y0, const float z0,
49664                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49665                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49666                            const unsigned int render_type,
49667                            const bool is_double_sided, const float focale,
49668                            const float lightx, const float lighty, const float lightz,
49669                            const float specular_lightness, const float specular_shininess,
49670                            const float g_opacity, CImg<tz>& zbuffer) {
49671       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49672                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49673                             specular_lightness,specular_shininess,g_opacity,1);
49674     }
49675 #endif
49676 
49677     //! Draw a 3D object \simplification.
49678     template<typename tp, typename tf, typename tc>
49679     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49680                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49681                            const CImgList<tc>& colors,
49682                            const unsigned int render_type=4,
49683                            const bool is_double_sided=false, const float focale=700,
49684                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49685                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49686                            const float g_opacity=1) {
49687       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49688                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49689                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49690     }
49691 
49692     //! Draw a 3D object \simplification.
49693     template<typename tp, typename tf, typename tc, typename tz>
49694     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49695                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49696                            const CImgList<tc>& colors,
49697                            const unsigned int render_type,
49698                            const bool is_double_sided, const float focale,
49699                            const float lightx, const float lighty, const float lightz,
49700                            const float specular_lightness, const float specular_shininess,
49701                            const float g_opacity, CImg<tz>& zbuffer) {
49702       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49703                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49704                            specular_lightness,specular_shininess,g_opacity,zbuffer);
49705     }
49706 
49707 #ifdef cimg_use_board
49708     template<typename tp, typename tf, typename tc, typename to>
49709     CImg<T>& draw_object3d(LibBoard::Board& board,
49710                            const float x0, const float y0, const float z0,
49711                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49712                            const CImgList<tc>& colors,
49713                            const unsigned int render_type=4,
49714                            const bool is_double_sided=false, const float focale=700,
49715                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49716                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49717                            const float g_opacity=1) {
49718       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49719                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49720                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49721     }
49722 
49723     template<typename tp, typename tf, typename tc, typename to, typename tz>
49724     CImg<T>& draw_object3d(LibBoard::Board& board,
49725                            const float x0, const float y0, const float z0,
49726                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49727                            const CImgList<tc>& colors,
49728                            const unsigned int render_type,
49729                            const bool is_double_sided, const float focale,
49730                            const float lightx, const float lighty, const float lightz,
49731                            const float specular_lightness, const float specular_shininess,
49732                            const float g_opacity, CImg<tz>& zbuffer) {
49733       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49734                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49735                            specular_lightness,specular_shininess,g_opacity,zbuffer);
49736     }
49737 #endif
49738 
49739     template<typename t, typename to>
49740     static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
49741       if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
49742       if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
49743       opacity.assign(opacities[n_primitive],true);
49744       return 1.f;
49745     }
49746 
49747     template<typename t, typename to>
49748     static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
49749       opacity.assign();
49750       return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive];
49751     }
49752 
49753     template<typename t>
49754     static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
49755       return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f;
49756     }
49757 
49758     template<typename t>
49759     static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
49760       return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f;
49761     }
49762 
49763     template<typename tz, typename tp, typename tf, typename tc, typename to>
49764     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
49765                             const float X, const float Y, const float Z,
49766                             const CImg<tp>& vertices,
49767                             const CImgList<tf>& primitives,
49768                             const CImgList<tc>& colors,
49769                             const to& opacities,
49770                             const unsigned int render_type,
49771                             const bool is_double_sided, const float focale,
49772                             const float lightx, const float lighty, const float lightz,
49773                             const float specular_lightness, const float specular_shininess,
49774                             const float g_opacity, const float sprite_scale) {
49775       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
49776       typedef typename to::value_type _to;
49777       if (is_empty() || !vertices || !primitives) return *this;
49778       CImg<char> error_message(1024);
49779       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
49780         throw CImgArgumentException(_cimg_instance
49781                                     "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).",
49782                                     cimg_instance,vertices._width,primitives._width,error_message.data());
49783 #ifndef cimg_use_board
49784       if (pboard) return *this;
49785 #endif
49786       if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety
49787 
49788       const float
49789         nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)),
49790         nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess),
49791         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
49792         nsl2 = 1 - 2*nsl1*nspec,
49793         nsl3 = nspec2 - nsl1 - nsl2;
49794 
49795       // Create light texture for phong-like rendering.
49796       CImg<floatT> light_texture;
49797       if (render_type==5) {
49798         if (colors._width>primitives._width) {
49799           static CImg<floatT> default_light_texture;
49800           static const tc *lptr = 0;
49801           static tc ref_values[64] = { 0 };
49802           const CImg<tc>& img = colors.back();
49803           bool is_same_texture = (lptr==img._data);
49804           if (is_same_texture)
49805             for (unsigned int r = 0, j = 0; j<8; ++j)
49806               for (unsigned int i = 0; i<8; ++i)
49807                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
49808                   is_same_texture = false; break;
49809                 }
49810           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
49811             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
49812             lptr = colors.back().data();
49813             for (unsigned int r = 0, j = 0; j<8; ++j)
49814               for (unsigned int i = 0; i<8; ++i)
49815                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
49816           }
49817           light_texture.assign(default_light_texture,true);
49818         } else {
49819           static CImg<floatT> default_light_texture;
49820           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
49821           if (!default_light_texture ||
49822               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
49823               specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
49824             default_light_texture.assign(512,512);
49825             const float
49826               dlx = lightx - X,
49827               dly = lighty - Y,
49828               dlz = lightz - Z,
49829               nl = cimg::hypot(dlx,dly,dlz),
49830               nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
49831               nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
49832               white[] = { 1 };
49833             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white);
49834             cimg_forXY(default_light_texture,x,y) {
49835               const float factor = default_light_texture(x,y);
49836               if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3);
49837             }
49838             default_light_texture.resize(-100,-100,1,_spectrum);
49839             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
49840           }
49841           light_texture.assign(default_light_texture,true);
49842         }
49843       }
49844 
49845       // Compute 3D to 2D projection.
49846       CImg<tpfloat> projections(vertices._width,2);
49847       tpfloat parallzmin = cimg::type<tpfloat>::max();
49848       const float absfocale = focale?cimg::abs(focale):0;
49849       if (absfocale) {
49850         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
49851         cimg_forX(projections,l) { // Perspective projection
49852           const tpfloat
49853             x = (tpfloat)vertices(l,0),
49854             y = (tpfloat)vertices(l,1),
49855             z = (tpfloat)vertices(l,2);
49856           const tpfloat projectedz = z + Z + absfocale;
49857           projections(l,1) = Y + absfocale*y/projectedz;
49858           projections(l,0) = X + absfocale*x/projectedz;
49859         }
49860       } else {
49861         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
49862         cimg_forX(projections,l) { // Parallel projection
49863           const tpfloat
49864             x = (tpfloat)vertices(l,0),
49865             y = (tpfloat)vertices(l,1),
49866             z = (tpfloat)vertices(l,2);
49867           if (z<parallzmin) parallzmin = z;
49868           projections(l,1) = Y + y;
49869           projections(l,0) = X + x;
49870         }
49871       }
49872 
49873       const float _focale = absfocale?absfocale:(1e5f-parallzmin);
49874       float zmax = 0;
49875       if (zbuffer) zmax = vertices.get_shared_row(2).max();
49876 
49877       // Compute visible primitives.
49878       CImg<uintT> visibles(primitives._width,1,1,1,~0U);
49879       CImg<tpfloat> zrange(primitives._width);
49880       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
49881       bool is_forward = zbuffer?true:false;
49882 
49883       cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096))
49884       cimglist_for(primitives,l) {
49885         const CImg<tf>& primitive = primitives[l];
49886         switch (primitive.size()) {
49887         case 1 : { // Point
49888           CImg<_to> _opacity;
49889           __draw_object3d(opacities,l,_opacity);
49890           if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
49891           const unsigned int i0 = (unsigned int)primitive(0);
49892           const tpfloat z0 = Z + vertices(i0,2);
49893           if (z0>zmin) {
49894             visibles(l) = (unsigned int)l;
49895             zrange(l) = z0;
49896           }
49897         } break;
49898         case 5 : { // Sphere
49899           const unsigned int
49900             i0 = (unsigned int)primitive(0),
49901             i1 = (unsigned int)primitive(1);
49902           const tpfloat
49903             Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
49904             Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
49905             Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
49906             _zc = Z + Zc,
49907             zc = _zc + _focale,
49908             xc = X + Xc*(absfocale?absfocale/zc:1),
49909             yc = Y + Yc*(absfocale?absfocale/zc:1),
49910             radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
49911                                       vertices(i1,1) - vertices(i0,1),
49912                                       vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
49913             xm = xc - radius,
49914             ym = yc - radius,
49915             xM = xc + radius,
49916             yM = yc + radius;
49917           if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
49918             visibles(l) = (unsigned int)l;
49919             zrange(l) = _zc;
49920           }
49921           is_forward = false;
49922         } break;
49923         case 2 : case 6 : { // Segment
49924           const unsigned int
49925             i0 = (unsigned int)primitive(0),
49926             i1 = (unsigned int)primitive(1);
49927           const tpfloat
49928             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49929             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
49930           tpfloat xm, xM, ym, yM;
49931           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49932           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49933           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
49934             visibles(l) = (unsigned int)l;
49935             zrange(l) = (z0 + z1)/2;
49936           }
49937         } break;
49938         case 3 : case 9 : { // Triangle
49939           const unsigned int
49940             i0 = (unsigned int)primitive(0),
49941             i1 = (unsigned int)primitive(1),
49942             i2 = (unsigned int)primitive(2);
49943           const tpfloat
49944             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49945             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
49946             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
49947           tpfloat xm, xM, ym, yM;
49948           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49949           if (x2<xm) xm = x2;
49950           if (x2>xM) xM = x2;
49951           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49952           if (y2<ym) ym = y2;
49953           if (y2>yM) yM = y2;
49954           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
49955             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
49956             if (is_double_sided || d<0) {
49957               visibles(l) = (unsigned int)l;
49958               zrange(l) = (z0 + z1 + z2)/3;
49959             }
49960           }
49961         } break;
49962         case 4 : case 12 : { // Quadrangle
49963           const unsigned int
49964             i0 = (unsigned int)primitive(0),
49965             i1 = (unsigned int)primitive(1),
49966             i2 = (unsigned int)primitive(2),
49967             i3 = (unsigned int)primitive(3);
49968           const tpfloat
49969             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49970             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
49971             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
49972             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
49973           tpfloat xm, xM, ym, yM;
49974           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49975           if (x2<xm) xm = x2;
49976           if (x2>xM) xM = x2;
49977           if (x3<xm) xm = x3;
49978           if (x3>xM) xM = x3;
49979           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49980           if (y2<ym) ym = y2;
49981           if (y2>yM) yM = y2;
49982           if (y3<ym) ym = y3;
49983           if (y3>yM) yM = y3;
49984           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
49985             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
49986             if (is_double_sided || d<0) {
49987               visibles(l) = (unsigned int)l;
49988               zrange(l) = (z0 + z1 + z2 + z3)/4;
49989             }
49990           }
49991         } break;
49992         default :
49993           if (render_type==5) cimg::mutex(10,0);
49994           throw CImgArgumentException(_cimg_instance
49995                                       "draw_object3d(): Invalid primitive[%u] with size %u "
49996                                       "(should have size 1,2,3,4,5,6,9 or 12).",
49997                                       cimg_instance,
49998                                       l,primitive.size());
49999         }
50000       }
50001 
50002       // Force transparent primitives to be drawn last when zbuffer is activated
50003       // (and if object contains no spheres or sprites).
50004       if (is_forward)
50005         cimglist_for(primitives,l)
50006           if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
50007 
50008       // Sort only visibles primitives.
50009       unsigned int *p_visibles = visibles._data;
50010       tpfloat *p_zrange = zrange._data;
50011       const tpfloat *ptrz = p_zrange;
50012       cimg_for(visibles,ptr,unsigned int) {
50013         if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
50014         ++ptrz;
50015       }
50016       const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
50017       if (!nb_visibles) {
50018         if (render_type==5) cimg::mutex(10,0);
50019         return *this;
50020       }
50021       CImg<uintT> permutations;
50022       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
50023 
50024       // Compute light properties
50025       CImg<floatT> lightprops;
50026       switch (render_type) {
50027       case 3 : { // Flat Shading
50028         lightprops.assign(nb_visibles);
50029         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50030         cimg_forX(lightprops,l) {
50031           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
50032           const unsigned int psize = (unsigned int)primitive.size();
50033           if (psize==3 || psize==4 || psize==9 || psize==12) {
50034             const unsigned int
50035               i0 = (unsigned int)primitive(0),
50036               i1 = (unsigned int)primitive(1),
50037               i2 = (unsigned int)primitive(2);
50038             const tpfloat
50039               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
50040               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
50041               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
50042               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
50043               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
50044               nx = dy1*dz2 - dz1*dy2,
50045               ny = dz1*dx2 - dx1*dz2,
50046               nz = dx1*dy2 - dy1*dx2,
50047               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50048               lx = X + (x0 + x1 + x2)/3 - lightx,
50049               ly = Y + (y0 + y1 + y2)/3 - lighty,
50050               lz = Z + (z0 + z1 + z2)/3 - lightz,
50051               nl = 1e-5f + cimg::hypot(lx,ly,lz),
50052               factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
50053             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
50054           } else lightprops[l] = 1;
50055         }
50056       } break;
50057 
50058       case 4 : // Gouraud Shading
50059       case 5 : { // Phong-Shading
50060         CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
50061         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50062         for (int l = 0; l<(int)nb_visibles; ++l) {
50063           const CImg<tf>& primitive = primitives[visibles(l)];
50064           const unsigned int psize = (unsigned int)primitive.size();
50065           const bool
50066             triangle_flag = (psize==3) || (psize==9),
50067             quadrangle_flag = (psize==4) || (psize==12);
50068           if (triangle_flag || quadrangle_flag) {
50069             const unsigned int
50070               i0 = (unsigned int)primitive(0),
50071               i1 = (unsigned int)primitive(1),
50072               i2 = (unsigned int)primitive(2),
50073               i3 = quadrangle_flag?(unsigned int)primitive(3):0;
50074             const tpfloat
50075               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
50076               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
50077               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
50078               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
50079               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
50080               nnx = dy1*dz2 - dz1*dy2,
50081               nny = dz1*dx2 - dx1*dz2,
50082               nnz = dx1*dy2 - dy1*dx2,
50083               norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
50084               nx = nnx/norm,
50085               ny = nny/norm,
50086               nz = nnz/norm;
50087             unsigned int ix = 0, iy = 1, iz = 2;
50088             if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
50089             vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
50090             vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
50091             vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
50092             if (quadrangle_flag) {
50093               vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
50094             }
50095           }
50096         }
50097 
50098         if (is_double_sided) cimg_forX(vertices_normals,p) {
50099             const float
50100               nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
50101               nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
50102               n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
50103             if (n1>n0) {
50104               vertices_normals(p,0) = -nx1;
50105               vertices_normals(p,1) = -ny1;
50106               vertices_normals(p,2) = -nz1;
50107             }
50108           }
50109 
50110         if (render_type==4) {
50111           lightprops.assign(vertices._width);
50112           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50113           cimg_forX(lightprops,l) {
50114             const tpfloat
50115               nx = vertices_normals(l,0),
50116               ny = vertices_normals(l,1),
50117               nz = vertices_normals(l,2),
50118               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50119               lx = X + vertices(l,0) - lightx,
50120               ly = Y + vertices(l,1) - lighty,
50121               lz = Z + vertices(l,2) - lightz,
50122               nl = 1e-5f + cimg::hypot(lx,ly,lz),
50123               factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
50124             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
50125           }
50126         } else {
50127           const unsigned int
50128             lw2 = light_texture._width/2 - 1,
50129             lh2 = light_texture._height/2 - 1;
50130           lightprops.assign(vertices._width,2);
50131           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50132           cimg_forX(lightprops,l) {
50133             const tpfloat
50134               nx = vertices_normals(l,0),
50135               ny = vertices_normals(l,1),
50136               nz = vertices_normals(l,2),
50137               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50138               nnx = nx/norm,
50139               nny = ny/norm;
50140             lightprops(l,0) = lw2*(1 + nnx);
50141             lightprops(l,1) = lh2*(1 + nny);
50142           }
50143         }
50144       } break;
50145       }
50146 
50147       // Draw visible primitives
50148       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
50149       CImg<_to> _opacity;
50150 
50151       for (unsigned int l = 0; l<nb_visibles; ++l) {
50152         const unsigned int n_primitive = visibles(permutations(l));
50153         const CImg<tf>& primitive = primitives[n_primitive];
50154         const CImg<tc>
50155           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
50156           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
50157             __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
50158           &color = _color?_color:(__color?__color:default_color);
50159         const tc *const pcolor = color._data;
50160         float opacity = __draw_object3d(opacities,n_primitive,_opacity);
50161         if (_opacity.is_empty()) opacity*=g_opacity;
50162 
50163 #ifdef cimg_use_board
50164         LibBoard::Board &board = *(LibBoard::Board*)pboard;
50165 #endif
50166 
50167         switch (primitive.size()) {
50168         case 1 : { // Colored point or sprite
50169           const unsigned int n0 = (unsigned int)primitive[0];
50170           const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1));
50171 
50172           if (_opacity.is_empty()) { // Scalar opacity
50173 
50174             if (color.size()==_spectrum) { // Colored point
50175               draw_point(x0,y0,pcolor,opacity);
50176 
50177 #ifdef cimg_use_board
50178               if (pboard) {
50179                 board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50180                 board.drawDot((float)x0,height()-(float)y0);
50181               }
50182 #endif
50183             } else { // Sprite
50184               const tpfloat z = Z + vertices(n0,2);
50185               const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
50186               const unsigned int
50187                 _sw = (unsigned int)(color._width*factor),
50188                 _sh = (unsigned int)(color._height*factor),
50189                 sw = _sw?_sw:1, sh = _sh?_sh:1;
50190               const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
50191               if (sw<=3*_width/2 && sh<=3*_height/2 &&
50192                   (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
50193                 const CImg<tc>
50194                   _sprite = (sw!=color._width || sh!=color._height)?
50195                     color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
50196                   &sprite = _sprite?_sprite:color;
50197                 draw_image(nx0,ny0,sprite,opacity);
50198 
50199 #ifdef cimg_use_board
50200                 if (pboard) {
50201                   board.setPenColorRGBi(128,128,128);
50202                   board.setFillColor(LibBoard::Color::Null);
50203                   board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
50204                 }
50205 #endif
50206               }
50207             }
50208           } else { // Opacity mask
50209             const tpfloat z = Z + vertices(n0,2);
50210             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
50211             const unsigned int
50212               _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
50213               _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
50214               sw = _sw?_sw:1, sh = _sh?_sh:1;
50215             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
50216             if (sw<=3*_width/2 && sh<=3*_height/2 &&
50217                 (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
50218               const CImg<tc>
50219                 _sprite = (sw!=color._width || sh!=color._height)?
50220                   color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
50221                 &sprite = _sprite?_sprite:color;
50222               const CImg<_to>
50223                 _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
50224                   _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
50225                 &nopacity = _nopacity?_nopacity:_opacity;
50226               draw_image(nx0,ny0,sprite,nopacity,g_opacity);
50227 
50228 #ifdef cimg_use_board
50229               if (pboard) {
50230                 board.setPenColorRGBi(128,128,128);
50231                 board.setFillColor(LibBoard::Color::Null);
50232                 board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
50233               }
50234 #endif
50235             }
50236           }
50237         } break;
50238         case 2 : { // Colored line
50239           const unsigned int
50240             n0 = (unsigned int)primitive[0],
50241             n1 = (unsigned int)primitive[1];
50242           const int
50243             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50244             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
50245           const float
50246             z0 = vertices(n0,2) + Z + _focale,
50247             z1 = vertices(n1,2) + Z + _focale;
50248           if (render_type) {
50249             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
50250             else draw_line(x0,y0,x1,y1,pcolor,opacity);
50251 
50252 #ifdef cimg_use_board
50253             if (pboard) {
50254               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50255               board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
50256             }
50257 #endif
50258           } else {
50259             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
50260 
50261 #ifdef cimg_use_board
50262             if (pboard) {
50263               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50264               board.drawDot((float)x0,height() - (float)y0);
50265               board.drawDot((float)x1,height() - (float)y1);
50266             }
50267 #endif
50268           }
50269         } break;
50270         case 5 : { // Colored sphere
50271           const unsigned int
50272             n0 = (unsigned int)primitive[0],
50273             n1 = (unsigned int)primitive[1],
50274             is_wireframe = (unsigned int)primitive[2],
50275             is_radius = (unsigned int)primitive[3];
50276           float Xc,Yc,Zc,radius;
50277           if (is_radius) {
50278             Xc = (float)vertices(n0,0);
50279             Yc = (float)vertices(n0,1);
50280             Zc = (float)vertices(n0,2);
50281             radius = cimg::hypot(vertices(n1,0) - vertices(n0,0),
50282                                  vertices(n1,1) - vertices(n0,1),
50283                                  vertices(n1,2) - vertices(n0,2));
50284           } else {
50285             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0));
50286             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1));
50287             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2));
50288             radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
50289                                       vertices(n1,1) - vertices(n0,1),
50290                                       vertices(n1,2) - vertices(n0,2));
50291           }
50292           const float
50293             zc = Z + Zc + _focale,
50294             af = absfocale?absfocale/zc:1,
50295             xc = X + Xc*af,
50296             yc = Y + Yc*af;
50297           radius*=af;
50298 
50299           switch (render_type) {
50300           case 0 :
50301             draw_point((int)xc,(int)yc,pcolor,opacity);
50302 
50303 #ifdef cimg_use_board
50304             if (pboard) {
50305               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50306               board.drawDot(xc,height() - yc);
50307             }
50308 #endif
50309             break;
50310           case 1 :
50311             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
50312 
50313 #ifdef cimg_use_board
50314             if (pboard) {
50315               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50316               board.setFillColor(LibBoard::Color::Null);
50317               board.drawCircle(xc,height() - yc,radius);
50318             }
50319 #endif
50320             break;
50321           default :
50322             if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
50323             else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
50324 
50325 #ifdef cimg_use_board
50326             if (pboard) {
50327               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50328               if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
50329               else {
50330                 board.setFillColor(LibBoard::Color::Null);
50331                 board.drawCircle(xc,height() - yc,radius);
50332               }
50333             }
50334 #endif
50335             break;
50336           }
50337         } break;
50338         case 6 : { // Textured line
50339           if (!__color) {
50340             if (render_type==5) cimg::mutex(10,0);
50341             throw CImgArgumentException(_cimg_instance
50342                                         "draw_object3d(): Undefined texture for line primitive [%u].",
50343                                         cimg_instance,n_primitive);
50344           }
50345           const unsigned int
50346             n0 = (unsigned int)primitive[0],
50347             n1 = (unsigned int)primitive[1];
50348           const int
50349             tx0 = (int)primitive[2], ty0 = (int)primitive[3],
50350             tx1 = (int)primitive[4], ty1 = (int)primitive[5],
50351             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50352             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
50353           const float
50354             z0 = vertices(n0,2) + Z + _focale,
50355             z1 = vertices(n1,2) + Z + _focale;
50356           if (render_type) {
50357             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
50358             else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
50359 
50360 #ifdef cimg_use_board
50361             if (pboard) {
50362               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50363               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50364             }
50365 #endif
50366           } else {
50367             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50368                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50369               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50370                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
50371 
50372 #ifdef cimg_use_board
50373             if (pboard) {
50374               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50375               board.drawDot((float)x0,height() - (float)y0);
50376               board.drawDot((float)x1,height() - (float)y1);
50377             }
50378 #endif
50379           }
50380         } break;
50381         case 3 : { // Colored triangle
50382           const unsigned int
50383             n0 = (unsigned int)primitive[0],
50384             n1 = (unsigned int)primitive[1],
50385             n2 = (unsigned int)primitive[2];
50386           const int
50387             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50388             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50389             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
50390           const float
50391             z0 = vertices(n0,2) + Z + _focale,
50392             z1 = vertices(n1,2) + Z + _focale,
50393             z2 = vertices(n2,2) + Z + _focale;
50394           switch (render_type) {
50395           case 0 :
50396             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
50397 
50398 #ifdef cimg_use_board
50399             if (pboard) {
50400               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50401               board.drawDot((float)x0,height() - (float)y0);
50402               board.drawDot((float)x1,height() - (float)y1);
50403               board.drawDot((float)x2,height() - (float)y2);
50404             }
50405 #endif
50406             break;
50407           case 1 :
50408             if (zbuffer)
50409               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
50410                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
50411             else
50412               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
50413                 draw_line(x1,y1,x2,y2,pcolor,opacity);
50414 
50415 #ifdef cimg_use_board
50416             if (pboard) {
50417               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50418               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50419               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
50420               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50421             }
50422 #endif
50423             break;
50424           case 2 :
50425             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
50426             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
50427 
50428 #ifdef cimg_use_board
50429             if (pboard) {
50430               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50431               board.fillTriangle((float)x0,height() - (float)y0,
50432                                  (float)x1,height() - (float)y1,
50433                                  (float)x2,height() - (float)y2);
50434             }
50435 #endif
50436             break;
50437           case 3 :
50438             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
50439             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
50440 
50441 #ifdef cimg_use_board
50442             if (pboard) {
50443               const float lp = std::min(lightprops(l),1.f);
50444               board.setPenColorRGBi((unsigned char)(color[0]*lp),
50445                                      (unsigned char)(color[1]*lp),
50446                                      (unsigned char)(color[2]*lp),
50447                                      (unsigned char)(opacity*255));
50448               board.fillTriangle((float)x0,height() - (float)y0,
50449                                  (float)x1,height() - (float)y1,
50450                                  (float)x2,height() - (float)y2);
50451             }
50452 #endif
50453             break;
50454           case 4 :
50455             if (zbuffer)
50456               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
50457                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50458             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50459 
50460 #ifdef cimg_use_board
50461             if (pboard) {
50462               board.setPenColorRGBi((unsigned char)(color[0]),
50463                                      (unsigned char)(color[1]),
50464                                      (unsigned char)(color[2]),
50465                                      (unsigned char)(opacity*255));
50466               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
50467                                          (float)x1,height() - (float)y1,lightprops(n1),
50468                                          (float)x2,height() - (float)y2,lightprops(n2));
50469             }
50470 #endif
50471             break;
50472           case 5 : {
50473             const unsigned int
50474               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
50475               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
50476               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1));
50477             if (zbuffer)
50478               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
50479             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
50480 
50481 #ifdef cimg_use_board
50482             if (pboard) {
50483               const float
50484                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
50485                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
50486                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
50487                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
50488                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
50489                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
50490               board.setPenColorRGBi((unsigned char)(color[0]),
50491                                      (unsigned char)(color[1]),
50492                                      (unsigned char)(color[2]),
50493                                      (unsigned char)(opacity*255));
50494               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50495                                          (float)x1,height() - (float)y1,l1,
50496                                          (float)x2,height() - (float)y2,l2);
50497             }
50498 #endif
50499           } break;
50500           }
50501         } break;
50502         case 4 : { // Colored quadrangle
50503           const unsigned int
50504             n0 = (unsigned int)primitive[0],
50505             n1 = (unsigned int)primitive[1],
50506             n2 = (unsigned int)primitive[2],
50507             n3 = (unsigned int)primitive[3];
50508           const int
50509             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50510             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50511             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
50512             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)),
50513             xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
50514           const float
50515             z0 = vertices(n0,2) + Z + _focale,
50516             z1 = vertices(n1,2) + Z + _focale,
50517             z2 = vertices(n2,2) + Z + _focale,
50518             z3 = vertices(n3,2) + Z + _focale,
50519             zc = (z0 + z1 + z2 + z3)/4;
50520 
50521           switch (render_type) {
50522           case 0 :
50523             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
50524               draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
50525 
50526 #ifdef cimg_use_board
50527             if (pboard) {
50528               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50529               board.drawDot((float)x0,height() - (float)y0);
50530               board.drawDot((float)x1,height() - (float)y1);
50531               board.drawDot((float)x2,height() - (float)y2);
50532               board.drawDot((float)x3,height() - (float)y3);
50533             }
50534 #endif
50535             break;
50536           case 1 :
50537             if (zbuffer)
50538               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
50539                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
50540             else
50541               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
50542                 draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
50543 
50544 #ifdef cimg_use_board
50545             if (pboard) {
50546               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50547               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50548               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50549               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
50550               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
50551             }
50552 #endif
50553             break;
50554           case 2 :
50555             if (zbuffer)
50556               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
50557                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
50558             else
50559               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
50560 
50561 #ifdef cimg_use_board
50562             if (pboard) {
50563               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50564               board.fillTriangle((float)x0,height() - (float)y0,
50565                                  (float)x1,height() - (float)y1,
50566                                  (float)x2,height() - (float)y2);
50567               board.fillTriangle((float)x0,height() - (float)y0,
50568                                  (float)x2,height() - (float)y2,
50569                                  (float)x3,height() - (float)y3);
50570             }
50571 #endif
50572             break;
50573           case 3 :
50574             if (zbuffer)
50575               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
50576                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
50577             else
50578               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
50579                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
50580 
50581 #ifdef cimg_use_board
50582             if (pboard) {
50583               const float lp = std::min(lightprops(l),1.f);
50584               board.setPenColorRGBi((unsigned char)(color[0]*lp),
50585                                      (unsigned char)(color[1]*lp),
50586                                      (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
50587               board.fillTriangle((float)x0,height() - (float)y0,
50588                                  (float)x1,height() - (float)y1,
50589                                  (float)x2,height() - (float)y2);
50590               board.fillTriangle((float)x0,height() - (float)y0,
50591                                  (float)x2,height() - (float)y2,
50592                                  (float)x3,height() - (float)y3);
50593             }
50594 #endif
50595             break;
50596           case 4 : {
50597             const float
50598               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
50599               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
50600               lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
50601             if (zbuffer)
50602               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
50603               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
50604               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
50605                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
50606             else
50607               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
50608               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
50609               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
50610                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
50611 
50612 #ifdef cimg_use_board
50613             if (pboard) {
50614               board.setPenColorRGBi((unsigned char)(color[0]),
50615                                      (unsigned char)(color[1]),
50616                                      (unsigned char)(color[2]),
50617                                      (unsigned char)(opacity*255));
50618               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50619                                          (float)x1,height() - (float)y1,lightprop1,
50620                                          (float)x2,height() - (float)y2,lightprop2);
50621               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50622                                          (float)x2,height() - (float)y2,lightprop2,
50623                                          (float)x3,height() - (float)y3,lightprop3);
50624             }
50625 #endif
50626           } break;
50627           case 5 : {
50628             const unsigned int
50629               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
50630               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
50631               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
50632               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)),
50633               lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
50634             if (zbuffer)
50635               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
50636               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
50637               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
50638                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
50639             else
50640               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
50641               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
50642               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
50643                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
50644 
50645 #ifdef cimg_use_board
50646             if (pboard) {
50647               const float
50648                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
50649                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
50650                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
50651                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
50652               board.setPenColorRGBi((unsigned char)(color[0]),
50653                                      (unsigned char)(color[1]),
50654                                      (unsigned char)(color[2]),
50655                                      (unsigned char)(opacity*255));
50656               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50657                                          (float)x1,height() - (float)y1,l1,
50658                                          (float)x2,height() - (float)y2,l2);
50659               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50660                                          (float)x2,height() - (float)y2,l2,
50661                                          (float)x3,height() - (float)y3,l3);
50662             }
50663 #endif
50664           } break;
50665           }
50666         } break;
50667         case 9 : { // Textured triangle
50668           if (!__color) {
50669             if (render_type==5) cimg::mutex(10,0);
50670             throw CImgArgumentException(_cimg_instance
50671                                         "draw_object3d(): Undefined texture for triangle primitive [%u].",
50672                                         cimg_instance,n_primitive);
50673           }
50674           const unsigned int
50675             n0 = (unsigned int)primitive[0],
50676             n1 = (unsigned int)primitive[1],
50677             n2 = (unsigned int)primitive[2];
50678           const int
50679             tx0 = (int)primitive[3], ty0 = (int)primitive[4],
50680             tx1 = (int)primitive[5], ty1 = (int)primitive[6],
50681             tx2 = (int)primitive[7], ty2 = (int)primitive[8],
50682             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50683             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50684             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
50685           const float
50686             z0 = vertices(n0,2) + Z + _focale,
50687             z1 = vertices(n1,2) + Z + _focale,
50688             z2 = vertices(n2,2) + Z + _focale;
50689           switch (render_type) {
50690           case 0 :
50691             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50692                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50693               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50694                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
50695               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
50696                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
50697 #ifdef cimg_use_board
50698             if (pboard) {
50699               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50700               board.drawDot((float)x0,height() - (float)y0);
50701               board.drawDot((float)x1,height() - (float)y1);
50702               board.drawDot((float)x2,height() - (float)y2);
50703             }
50704 #endif
50705             break;
50706           case 1 :
50707             if (zbuffer)
50708               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50709                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
50710                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
50711             else
50712               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50713                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
50714                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
50715 
50716 #ifdef cimg_use_board
50717             if (pboard) {
50718               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50719               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50720               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
50721               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50722             }
50723 #endif
50724             break;
50725           case 2 :
50726             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
50727             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
50728 
50729 #ifdef cimg_use_board
50730             if (pboard) {
50731               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50732               board.fillTriangle((float)x0,height() - (float)y0,
50733                                  (float)x1,height() - (float)y1,
50734                                  (float)x2,height() - (float)y2);
50735             }
50736 #endif
50737             break;
50738           case 3 :
50739             if (zbuffer)
50740               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
50741             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
50742 
50743 #ifdef cimg_use_board
50744             if (pboard) {
50745               const float lp = std::min(lightprops(l),1.f);
50746               board.setPenColorRGBi((unsigned char)(128*lp),
50747                                     (unsigned char)(128*lp),
50748                                     (unsigned char)(128*lp),
50749                                     (unsigned char)(opacity*255));
50750               board.fillTriangle((float)x0,height() - (float)y0,
50751                                  (float)x1,height() - (float)y1,
50752                                  (float)x2,height() - (float)y2);
50753             }
50754 #endif
50755             break;
50756           case 4 :
50757             if (zbuffer)
50758               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50759                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50760             else
50761               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50762                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50763 
50764 #ifdef cimg_use_board
50765             if (pboard) {
50766               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50767               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
50768                                         (float)x1,height() - (float)y1,lightprops(n1),
50769                                         (float)x2,height() - (float)y2,lightprops(n2));
50770             }
50771 #endif
50772             break;
50773           case 5 :
50774             if (zbuffer)
50775               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
50776                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
50777                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
50778                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
50779                             opacity);
50780             else
50781               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
50782                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
50783                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
50784                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
50785                             opacity);
50786 
50787 #ifdef cimg_use_board
50788             if (pboard) {
50789               const float
50790                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
50791                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
50792                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
50793                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
50794                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
50795                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
50796               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50797               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50798                                         (float)x1,height() - (float)y1,l1,
50799                                         (float)x2,height() - (float)y2,l2);
50800             }
50801 #endif
50802             break;
50803           }
50804         } break;
50805         case 12 : { // Textured quadrangle
50806           if (!__color) {
50807             if (render_type==5) cimg::mutex(10,0);
50808             throw CImgArgumentException(_cimg_instance
50809                                         "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
50810                                         cimg_instance,n_primitive);
50811           }
50812           const unsigned int
50813             n0 = (unsigned int)primitive[0],
50814             n1 = (unsigned int)primitive[1],
50815             n2 = (unsigned int)primitive[2],
50816             n3 = (unsigned int)primitive[3];
50817           const int
50818             tx0 = (int)primitive[4], ty0 = (int)primitive[5],
50819             tx1 = (int)primitive[6], ty1 = (int)primitive[7],
50820             tx2 = (int)primitive[8], ty2 = (int)primitive[9],
50821             tx3 = (int)primitive[10], ty3 = (int)primitive[11],
50822             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50823             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50824             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
50825             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1));
50826           const float
50827             z0 = vertices(n0,2) + Z + _focale,
50828             z1 = vertices(n1,2) + Z + _focale,
50829             z2 = vertices(n2,2) + Z + _focale,
50830             z3 = vertices(n3,2) + Z + _focale;
50831 
50832           switch (render_type) {
50833           case 0 :
50834             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50835                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50836               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50837                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
50838               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
50839                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
50840               draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
50841                                                    ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
50842 
50843 #ifdef cimg_use_board
50844             if (pboard) {
50845               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50846               board.drawDot((float)x0,height() - (float)y0);
50847               board.drawDot((float)x1,height() - (float)y1);
50848               board.drawDot((float)x2,height() - (float)y2);
50849               board.drawDot((float)x3,height() - (float)y3);
50850             }
50851 #endif
50852             break;
50853           case 1 :
50854             if (zbuffer)
50855               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50856                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
50857                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
50858                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
50859             else
50860               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50861                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
50862                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
50863                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
50864 
50865 #ifdef cimg_use_board
50866             if (pboard) {
50867               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50868               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50869               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50870               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
50871               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
50872             }
50873 #endif
50874             break;
50875           case 2 :
50876             if (zbuffer)
50877               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
50878                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
50879             else
50880               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
50881                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
50882 
50883 #ifdef cimg_use_board
50884             if (pboard) {
50885               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50886               board.fillTriangle((float)x0,height() - (float)y0,
50887                                  (float)x1,height() - (float)y1,
50888                                  (float)x2,height() - (float)y2);
50889               board.fillTriangle((float)x0,height() - (float)y0,
50890                                  (float)x2,height() - (float)y2,
50891                                  (float)x3,height() - (float)y3);
50892             }
50893 #endif
50894             break;
50895           case 3 :
50896             if (zbuffer)
50897               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
50898                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
50899             else
50900               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
50901                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
50902 
50903 #ifdef cimg_use_board
50904             if (pboard) {
50905               const float lp = std::min(lightprops(l),1.f);
50906               board.setPenColorRGBi((unsigned char)(128*lp),
50907                                      (unsigned char)(128*lp),
50908                                      (unsigned char)(128*lp),
50909                                      (unsigned char)(opacity*255));
50910               board.fillTriangle((float)x0,height() - (float)y0,
50911                                  (float)x1,height() - (float)y1,
50912                                  (float)x2,height() - (float)y2);
50913               board.fillTriangle((float)x0,height() - (float)y0,
50914                                  (float)x2,height() - (float)y2,
50915                                  (float)x3,height() - (float)y3);
50916             }
50917 #endif
50918             break;
50919           case 4 : {
50920             const float
50921               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
50922               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
50923             if (zbuffer)
50924               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50925                             lightprop0,lightprop1,lightprop2,opacity).
50926                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50927                               lightprop0,lightprop2,lightprop3,opacity);
50928             else
50929               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50930                             lightprop0,lightprop1,lightprop2,opacity).
50931                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50932                               lightprop0,lightprop2,lightprop3,opacity);
50933 
50934 #ifdef cimg_use_board
50935             if (pboard) {
50936               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50937               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50938                                          (float)x1,height() - (float)y1,lightprop1,
50939                                          (float)x2,height() - (float)y2,lightprop2);
50940               board.fillGouraudTriangle((float)x0,height()  -(float)y0,lightprop0,
50941                                          (float)x2,height() - (float)y2,lightprop2,
50942                                          (float)x3,height() - (float)y3,lightprop3);
50943             }
50944 #endif
50945           } break;
50946           case 5 : {
50947             const unsigned int
50948               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
50949               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
50950               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
50951               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1));
50952             if (zbuffer)
50953               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50954                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
50955                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50956                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
50957             else
50958               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50959                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
50960                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50961                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
50962 #ifdef cimg_use_board
50963             if (pboard) {
50964               const float
50965                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
50966                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
50967                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
50968                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
50969               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50970               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50971                                          (float)x1,height() - (float)y1,l1,
50972                                          (float)x2,height() - (float)y2,l2);
50973               board.fillGouraudTriangle((float)x0,height()  -(float)y0,l0,
50974                                          (float)x2,height() - (float)y2,l2,
50975                                          (float)x3,height() - (float)y3,l3);
50976             }
50977 #endif
50978           } break;
50979           }
50980         } break;
50981         }
50982       }
50983       if (render_type==5) cimg::mutex(10,0);
50984       return *this;
50985     }
50986 
50987     //@}
50988     //---------------------------
50989     //
50990     //! \name Data Input
50991     //@{
50992     //---------------------------
50993 
50994     //! Launch simple interface to select a shape from an image.
50995     /**
50996        \param disp Display window to use.
50997        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
50998        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
50999        \param exit_on_anykey Exit function when any key is pressed.
51000     **/
51001     CImg<T>& select(CImgDisplay &disp,
51002                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
51003                     const bool exit_on_anykey=false,
51004                     const bool is_deep_selection_default=false) {
51005       return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
51006     }
51007 
51008     //! Simple interface to select a shape from an image \overloading.
51009     CImg<T>& select(const char *const title,
51010                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
51011                     const bool exit_on_anykey=false,
51012                     const bool is_deep_selection_default=false) {
51013       return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
51014     }
51015 
51016     //! Simple interface to select a shape from an image \newinstance.
51017     CImg<intT> get_select(CImgDisplay &disp,
51018                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
51019                           const bool exit_on_anykey=false,
51020                           const bool is_deep_selection_default=false) const {
51021       return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
51022     }
51023 
51024     //! Simple interface to select a shape from an image \newinstance.
51025     CImg<intT> get_select(const char *const title,
51026                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
51027                           const bool exit_on_anykey=false,
51028                           const bool is_deep_selection_default=false) const {
51029       CImgDisplay disp;
51030       return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
51031     }
51032 
51033     CImg<intT> _select(CImgDisplay &disp, const char *const title,
51034                        const unsigned int feature_type, unsigned int *const XYZ,
51035                        const int origX, const int origY, const int origZ,
51036                        const bool exit_on_anykey,
51037                        const bool reset_view3d,
51038                        const bool force_display_z_coord,
51039                        const bool is_deep_selection_default) const {
51040       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
51041       if (!disp) {
51042         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
51043         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
51044       } else {
51045         if (title) disp.set_title("%s",title);
51046         disp.move_inside_screen();
51047       }
51048 
51049       CImg<T> thumb;
51050       if (width()>disp.screen_width() || height()>disp.screen_height())
51051         get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
51052 
51053       const unsigned int old_normalization = disp.normalization();
51054       bool old_is_resized = disp.is_resized();
51055       disp._normalization = 0;
51056       disp.show().set_key(0).set_wheel().show_mouse();
51057 
51058       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
51059       int area = 0, area_started = 0, area_clicked = 0, phase = 0,
51060         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width),
51061         Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height),
51062         Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
51063         X1 =-1, Y1 = -1, Z1 = -1,
51064         X3d = -1, Y3d = -1,
51065         oX3d = X3d, oY3d = -1,
51066         omx = -1, omy = -1;
51067       float X = -1, Y = -1, Z = -1;
51068       unsigned int key = 0, font_size = 32;
51069 
51070       bool is_deep_selection = is_deep_selection_default,
51071         shape_selected = false, text_down = false, visible_cursor = true;
51072       static CImg<floatT> pose3d;
51073       static bool is_view3d = false, is_axes = true;
51074       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
51075       CImg<floatT> points3d, opacities3d, sel_opacities3d;
51076       CImgList<uintT> primitives3d, sel_primitives3d;
51077       CImgList<ucharT> colors3d, sel_colors3d;
51078       CImg<ucharT> visu, visu0, view3d;
51079       CImg<charT> text(1024); *text = 0;
51080 
51081       while (!key && !disp.is_closed() && !shape_selected) {
51082 
51083         // Handle mouse motion and selection
51084         int
51085           mx = disp.mouse_x(),
51086           my = disp.mouse_y();
51087 
51088         const float
51089           mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
51090           mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
51091 
51092         area = 0;
51093         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
51094         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
51095         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
51096         if (mX>=width() && mY>=height()) area = 4;
51097         if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
51098 
51099         CImg<charT> filename(32);
51100 
51101         switch (key = disp.key()) {
51102 #if cimg_OS!=2
51103         case cimg::keyCTRLRIGHT :
51104 #endif
51105         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
51106         case cimg::keyPAGEUP :
51107           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
51108         case cimg::keyPAGEDOWN :
51109           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
51110         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51111             is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
51112           } break;
51113         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51114             disp.set_fullscreen(false).
51115               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51116                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
51117               _is_resized = true;
51118             disp.set_key(key,false); key = 0; visu0.assign();
51119           } break;
51120         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51121             disp.set_fullscreen(false).
51122               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
51123             disp.set_key(key,false); key = 0; visu0.assign();
51124           } break;
51125         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51126             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
51127             disp.set_key(key,false); key = 0; visu0.assign();
51128           } break;
51129         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51130             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
51131             disp.set_key(key,false); key = 0; visu0.assign();
51132           } break;
51133         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51134             is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
51135           } break;
51136         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51137             static unsigned int snap_number = 0;
51138             std::FILE *file;
51139             do {
51140               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
51141               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51142             } while (file);
51143             if (visu0) {
51144               (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp);
51145               visu0.save(filename);
51146               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
51147             }
51148             disp.set_key(key,false); key = 0;
51149           } break;
51150         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51151             static unsigned int snap_number = 0;
51152             std::FILE *file;
51153             do {
51154 
51155 #ifdef cimg_use_zlib
51156               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
51157 #else
51158               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
51159 #endif
51160               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51161             } while (file);
51162             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
51163             save(filename);
51164             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
51165             disp.set_key(key,false); key = 0;
51166           } break;
51167         }
51168 
51169         switch (area) {
51170 
51171         case 0 : // When mouse is out of image range
51172           mx = my = -1; X = Y = Z = -1;
51173           break;
51174 
51175         case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections
51176           const unsigned int but = disp.button();
51177           const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
51178 
51179           if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step)
51180             if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
51181             X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
51182           }
51183           if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes)
51184             switch (area_started) {
51185             case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
51186             case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
51187             case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
51188             }
51189           }
51190           if (b2 && area_clicked==area) { // When moving through the image/volume
51191             if (phase) {
51192               if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
51193               X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
51194             } else {
51195               if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
51196               X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
51197             }
51198           }
51199           if (b3) { // Reset selection
51200             X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
51201             visu0.assign();
51202           }
51203           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel)
51204             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
51205                 !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
51206               switch (area) {
51207               case 1 :
51208                 if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
51209                 visu0.assign(); break;
51210               case 2 :
51211                 if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
51212                 visu0.assign(); break;
51213               case 3 :
51214                 if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
51215                 visu0.assign(); break;
51216               }
51217               disp.set_wheel();
51218             } else key = ~0U;
51219           }
51220 
51221           if ((phase==0 && b1) ||
51222               (phase==1 && !b1) ||
51223               (phase==2 && b1)) switch (phase) { // Detect change of phase
51224             case 0 :
51225               if (area==area_clicked) {
51226                 X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
51227               } break;
51228             case 1 :
51229               if (area==area_started) {
51230                 X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
51231                 if (_depth>1) {
51232                   if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
51233                   if (is_deep_selection) ++phase;
51234                 }
51235               } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
51236               break;
51237             case 2 : ++phase; break;
51238             }
51239         } break;
51240 
51241         case 4 : // When mouse is over the 3D view
51242           if (is_view3d && points3d) {
51243             X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
51244             Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
51245             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
51246             // Left + right buttons: reset.
51247             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
51248             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate
51249               const float
51250                 R = 0.45f*std::min(view3d._width,view3d._height),
51251                 R2 = R*R,
51252                 u0 = (float)(oX3d - view3d.width()/2),
51253                 v0 = (float)(oY3d - view3d.height()/2),
51254                 u1 = (float)(X3d - view3d.width()/2),
51255                 v1 = (float)(Y3d - view3d.height()/2),
51256                 n0 = cimg::hypot(u0,v0),
51257                 n1 = cimg::hypot(u1,v1),
51258                 nu0 = n0>R?(u0*R/n0):u0,
51259                 nv0 = n0>R?(v0*R/n0):v0,
51260                 nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
51261                 nu1 = n1>R?(u1*R/n1):u1,
51262                 nv1 = n1>R?(v1*R/n1):v1,
51263                 nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
51264                 u = nv0*nw1 - nw0*nv1,
51265                 v = nw0*nu1 - nu0*nw1,
51266                 w = nv0*nu1 - nu0*nv1,
51267                 n = cimg::hypot(u,v,w),
51268                 alpha = (float)std::asin(n/R2)*180/cimg::PI;
51269               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
51270               view3d.assign();
51271             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button: zoom
51272               pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
51273             }
51274             if (disp.wheel()) { // Wheel: zoom
51275               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
51276             }
51277             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift
51278               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
51279             }
51280             oX3d = X3d; oY3d = Y3d;
51281           }
51282           mx = my = -1; X = Y = Z = -1;
51283           break;
51284         }
51285 
51286         if (phase) {
51287           if (!feature_type) shape_selected = phase?true:false;
51288           else {
51289             if (_depth>1) shape_selected = (phase==3)?true:false;
51290             else shape_selected = (phase==2)?true:false;
51291           }
51292         }
51293 
51294         if (X0<0) X0 = 0;
51295         if (X0>=width()) X0 = width() - 1;
51296         if (Y0<0) Y0 = 0;
51297         if (Y0>=height()) Y0 = height() - 1;
51298         if (Z0<0) Z0 = 0;
51299         if (Z0>=depth()) Z0 = depth() - 1;
51300         if (X1<1) X1 = 0;
51301         if (X1>=width()) X1 = width() - 1;
51302         if (Y1<0) Y1 = 0;
51303         if (Y1>=height()) Y1 = height() - 1;
51304         if (Z1<0) Z1 = 0;
51305         if (Z1>=depth()) Z1 = depth() - 1;
51306 
51307         // Draw visualization image on the display
51308         if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
51309 
51310           if (!visu0) { // Create image of projected planes
51311             if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
51312             else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
51313             visu0.resize(disp);
51314             view3d.assign();
51315             points3d.assign();
51316           }
51317 
51318           if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images
51319             const unsigned int
51320               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
51321               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
51322               x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
51323               y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
51324             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
51325               move_to(view3d);
51326             if (!points3d) {
51327               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
51328               points3d.append(CImg<floatT>(8,3,1,1,
51329                                            0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
51330                                            0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
51331                                            0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
51332               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
51333               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
51334               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
51335               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
51336               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
51337               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
51338               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
51339               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
51340               if (!phase) {
51341                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
51342                 sel_primitives3d.assign();
51343                 sel_colors3d.assign();
51344                 sel_opacities3d.assign();
51345               } else {
51346                 if (feature_type==2) {
51347                   points3d.append(CImg<floatT>(8,3,1,1,
51348                                                X0,X1,X1,X0,X0,X1,X1,X0,
51349                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
51350                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
51351                   sel_primitives3d.assign();
51352                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
51353                   CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
51354                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
51355                   CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
51356                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
51357                   CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
51358                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
51359                   CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
51360                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
51361                   CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
51362                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
51363                   CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
51364                 } else {
51365                   points3d.append(CImg<floatT>(2,3,1,1,
51366                                                X0,X1,
51367                                                Y0,Y1,
51368                                                Z0,Z1),'x');
51369                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
51370                 }
51371                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
51372                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
51373               }
51374               points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
51375               points3d*=0.75f*std::min(view3d._width,view3d._height);
51376             }
51377 
51378             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
51379             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
51380             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
51381             if (sel_primitives3d)
51382               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
51383                                    pose3d(3,1) + 0.5f*view3d._height,
51384                                    pose3d(3,2),
51385                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
51386                                    2,true,500,0,0,0,0,0,1,zbuffer3d);
51387             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
51388                                  pose3d(3,1) + 0.5f*view3d._height,
51389                                  pose3d(3,2),
51390                                  rotated_points3d,primitives3d,colors3d,opacities3d,
51391                                  2,true,500,0,0,0,0,0,1,zbuffer3d);
51392             visu0.draw_image(x3d,y3d,view3d);
51393           }
51394           visu = visu0;
51395 
51396           if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
51397           else {
51398             if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
51399             else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
51400             const int d = (depth()>1)?depth():0;
51401             int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
51402             if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
51403             int
51404               w = disp.width(), W = width() + d,
51405               h = disp.height(), H = height() + d,
51406               _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
51407               _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
51408               _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1),
51409               _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1),
51410               _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
51411               _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
51412               _zxn = (int)((_vZ + width() + 1.f)*w/W - 1),
51413                        zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1),
51414               _zyn = (int)((_vZ + height() + 1.f)*h/H - 1),
51415                        zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1),
51416               _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()),
51417               _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()),
51418               xc = (xp + xn)/2,
51419               yc = (yp + yn)/2,
51420               zxc = (zxp + zxn)/2,
51421               zyc = (zyp + zyn)/2,
51422               xf = (int)(X*w/W),
51423               yf = (int)(Y*h/H),
51424               zxf = (int)((Z + width())*w/W),
51425               zyf = (int)((Z + height())*h/H);
51426 
51427             if (is_axes) { // Draw axes
51428               visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
51429                 draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
51430                 draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
51431                 draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
51432               if (_depth>1)
51433                 visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
51434                   draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
51435                   draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
51436                   draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
51437             }
51438 
51439             // Draw box cursor.
51440             if (xn - xp>=4 && yn - yp>=4)
51441               visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
51442                 draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
51443                 draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
51444             if (_depth>1) {
51445               if (yn - yp>=4 && zxn - zxp>=4)
51446                 visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
51447                                               draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
51448                                               draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
51449               if (xn - xp>=4 && zyn - zyp>=4)
51450                 visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
51451                           draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
51452                           draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
51453             }
51454 
51455             // Draw selection.
51456             if (phase && (phase!=1 || area_started==area)) {
51457               const int
51458                 _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
51459                 _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
51460                 _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1),
51461                 _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1),
51462                 _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
51463                 _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
51464                 _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1),
51465                 zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1),
51466                 _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1),
51467                 zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1),
51468                 xc0 = (xp0 + xn0)/2,
51469                 yc0 = (yp0 + yn0)/2,
51470                 zxc0 = (zxp0 + zxn0)/2,
51471                 zyc0 = (zyp0 + zyn0)/2;
51472 
51473               switch (feature_type) {
51474               case 1 : { // Vector
51475                 visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333).
51476                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC);
51477                 if (d) {
51478                   visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333).
51479                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC).
51480                     draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333).
51481                     draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC);
51482                 }
51483               } break;
51484               case 2 : { // Box
51485                 visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
51486                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
51487                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA);
51488                 if (xc0!=xc && yc0!=yc)
51489                   visu.draw_line(xc0,yc0,xc,yc,background_color,0.9f,0x33333333).
51490                     draw_line(xc0,yc0,xc,yc,foreground_color,0.9f,0xCCCCCCCC);
51491                 if (d) {
51492                   visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
51493                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
51494                                    background_color,0.9f,0x55555555).
51495                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
51496                                    foreground_color,0.9f,0xAAAAAAAA);
51497                   if (zxc0!=zxc && yc0!=yc)
51498                     visu.draw_line(zxc0,yc0,zxc,yc,background_color,0.9f,0x33333333).
51499                       draw_line(zxc0,yc0,zxc,yc,foreground_color,0.9f,0xCCCCCCCC);
51500                   visu.draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
51501                                       background_color,0.2f).
51502                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
51503                                    background_color,0.9f,0x55555555).
51504                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
51505                                    foreground_color,0.9f,0xAAAAAAAA);
51506                   if (xp0!=xn && zyp0!=zyn)
51507                     visu.draw_line(xp0,zyp0,xn,zyn,background_color,0.9f,0x33333333).
51508                       draw_line(xp0,zyp0,xn,zyn,foreground_color,0.9f,0xCCCCCCCC);
51509                 }
51510               } break;
51511               case 3 : { // Ellipse
51512                 visu.draw_ellipse(xc0,yc0,
51513                                   (float)cimg::abs(xc - xc0),
51514                                   (float)cimg::abs(yc - yc0),0,background_color,0.2f).
51515                   draw_ellipse(xc0,yc0,
51516                                (float)cimg::abs(xc - xc0),
51517                                (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
51518                   draw_point(xc0,yc0,foreground_color,0.9f);
51519                 if (d) {
51520                   visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
51521                                     background_color,0.2f).
51522                     draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
51523                                  foreground_color,0.9f,~0U).
51524                     draw_point(zxc0,yc0,foreground_color,0.9f).
51525                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
51526                                  background_color,0.2f).
51527                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
51528                                  foreground_color,0.9f,~0U).
51529                     draw_point(xc0,zyc0,foreground_color,0.9f);
51530                 }
51531               } break;
51532               }
51533             }
51534 
51535             // Draw text info.
51536             if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
51537             if (!feature_type || !phase) {
51538               if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
51539                 if (_depth>1 || force_display_z_coord)
51540                   cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
51541                 else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
51542                 CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
51543                 const bool is_large_spectrum = values._height>8;
51544                 if (is_large_spectrum)
51545                   values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0);
51546                 char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
51547                 for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
51548                   cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
51549                                 cimg::type<T>::format(values[c]));
51550                   ctext += std::strlen(ctext);
51551                   if (c==3 && is_large_spectrum) {
51552                     cimg_snprintf(ctext,24," ...");
51553                     ctext += std::strlen(ctext);
51554                   }
51555                   *(ctext++) = ' '; *ctext = 0;
51556                 }
51557                 std::strcpy(text._data + std::strlen(text),"] ");
51558               }
51559             } else switch (feature_type) {
51560               case 1 : {
51561                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
51562                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
51563                 if (_depth>1 || force_display_z_coord)
51564                   cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
51565                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
51566                 else if (_width!=1 && _height!=1)
51567                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
51568                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length,
51569                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
51570                 else
51571                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
51572                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length);
51573               } break;
51574               case 2 : {
51575                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
51576                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
51577                 if (_depth>1 || force_display_z_coord)
51578                   cimg_snprintf(text,text._width,
51579                                 " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ",
51580                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
51581                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
51582                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
51583                 else if (_width!=1 && _height!=1)
51584                   cimg_snprintf(text,text._width,
51585                                 " Box ( %d,%d ) - ( %d,%d )\n Size = ( %d,%d ), Length = %g \n Angle = %g\260 ",
51586                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
51587                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
51588                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
51589                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
51590                 else
51591                   cimg_snprintf(text,text._width,
51592                                 " Box ( %d,%d ) - ( %d,%d )\n Size = (%d,%d), Length = %g ",
51593                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
51594                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
51595                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
51596               } break;
51597               default :
51598                 if (_depth>1 || force_display_z_coord)
51599                   cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ",
51600                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
51601                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
51602                 else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ",
51603                                    origX + X0,origY + Y0,origX + X1,origY + Y1,
51604                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
51605               }
51606             if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data);
51607           }
51608 
51609           disp.display(visu);
51610         }
51611         if (!shape_selected) disp.wait();
51612         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
51613         omx = mx; omy = my;
51614         if (!exit_on_anykey && key && key!=cimg::keyESC &&
51615             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51616           key = 0;
51617         }
51618       }
51619 
51620       // Return result.
51621       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
51622       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
51623       if (shape_selected) {
51624         if (feature_type==2) {
51625           if (is_deep_selection) switch (area_started) {
51626             case 1 : Z0 = 0; Z1 = _depth - 1; break;
51627             case 2 : Y0 = 0; Y1 = _height - 1; break;
51628             case 3 : X0 = 0; X1 = _width - 1; break;
51629           }
51630           if (X0>X1) cimg::swap(X0,X1);
51631           if (Y0>Y1) cimg::swap(Y0,Y1);
51632           if (Z0>Z1) cimg::swap(Z0,Z1);
51633         }
51634         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
51635         switch (feature_type) {
51636         case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
51637         case 3 :
51638           res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
51639           res[0] = X0; res[1] = Y0; res[2] = Z0;
51640           break;
51641         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
51642         }
51643       }
51644       if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
51645       if (!visible_cursor) disp.show_mouse();
51646       disp._normalization = old_normalization;
51647       disp._is_resized = old_is_resized;
51648       if (key!=~0U) disp.set_key(key);
51649       return res;
51650     }
51651 
51652     // Return a visualizable uchar8 image for display routines.
51653     CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
51654                              const int x, const int y, const int z) const {
51655       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
51656       const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
51657       CImg<Tuchar> img2d;
51658       if (_depth>1) {
51659         const int mdisp = std::min(disp.screen_width(),disp.screen_height());
51660         if (depth()>mdisp) {
51661           crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
51662           img2d.projections2d(x,y,z*img2d._depth/_depth);
51663         } else crop.get_projections2d(x,y,z).move_to(img2d);
51664       } else CImg<Tuchar>(crop,false).move_to(img2d);
51665 
51666       // Check for inf and NaN values.
51667       if (cimg::type<T>::is_float() && normalization) {
51668         bool is_inf = false, is_nan = false;
51669         cimg_for(img2d,ptr,Tuchar)
51670           if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
51671           else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
51672         if (is_inf || is_nan) {
51673           Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
51674           if (!normalization) { m0 = 0; M0 = 255; }
51675           else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
51676           else {
51677             cimg_for(img2d,ptr,Tuchar)
51678               if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
51679                 if (*ptr<(Tuchar)m0) m0 = *ptr;
51680                 if (*ptr>(Tuchar)M0) M0 = *ptr;
51681               }
51682           }
51683           const T
51684             val_minf = (T)(normalization==1 || normalization==3?m0 - cimg::abs(m0):m0),
51685             val_pinf = (T)(normalization==1 || normalization==3?M0 + cimg::abs(M0):M0);
51686           if (is_nan)
51687             cimg_for(img2d,ptr,Tuchar)
51688               if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values
51689           if (is_inf)
51690             cimg_for(img2d,ptr,Tuchar)
51691               if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values
51692         }
51693       }
51694 
51695       switch (normalization) {
51696       case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
51697       case 2 : {
51698         const float m = disp._min, M = disp._max;
51699         (img2d-=m)*=255.f/(M - m>0?M - m:1);
51700       } break;
51701       case 3 :
51702         if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
51703         else {
51704           const float
51705             m = (float)cimg::type<T>::min(),
51706             M = (float)cimg::type<T>::max();
51707           (img2d-=m)*=255.f/(M - m>0?M - m:1);
51708         } break;
51709       }
51710       if (img2d.spectrum()==2) img2d.channels(0,2);
51711       return img2d;
51712     }
51713 
51714     //! Select sub-graph in a graph.
51715     CImg<intT> get_select_graph(CImgDisplay &disp,
51716                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
51717                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
51718                                 const char *const labely=0, const double ymin=0, const double ymax=0,
51719                                 const bool exit_on_anykey=false) const {
51720       if (is_empty())
51721         throw CImgInstanceException(_cimg_instance
51722                                     "select_graph(): Empty instance.",
51723                                     cimg_instance);
51724       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
51725                    set_title("CImg<%s>",pixel_type());
51726       const ulongT siz = (ulongT)_width*_height*_depth;
51727       const unsigned int old_normalization = disp.normalization();
51728       disp.show().set_button().set_wheel()._normalization = 0;
51729 
51730       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
51731       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
51732       if (nymin==nymax) { --nymin; ++nymax; }
51733       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }
51734 
51735       static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
51736       static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
51737 
51738       CImg<ucharT> colormap(3,_spectrum);
51739       if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
51740       else {
51741         colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
51742         if (_spectrum>1) { colormap(0,1) = 10;  colormap(1,1) = 220; colormap(2,1) = 10;  }
51743         if (_spectrum>2) { colormap(0,2) = 10;  colormap(1,2) = 10;  colormap(2,2) = 220; }
51744         if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10;  }
51745         if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10;  colormap(2,4) = 220; }
51746         if (_spectrum>5) { colormap(0,5) = 10;  colormap(1,5) = 220; colormap(2,5) = 220; }
51747         if (_spectrum>6) {
51748           cimg_uint64 rng = 10;
51749           cimg_for_inY(colormap,6,colormap.height()-1,k) {
51750             colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51751             colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51752             colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51753           }
51754         }
51755       }
51756 
51757       CImg<ucharT> visu0, visu, graph, text, axes;
51758       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
51759       const unsigned int one = plot_type==3?0U:1U;
51760       unsigned int okey = 0, obutton = 0, font_size = 32;
51761       CImg<charT> message(1024);
51762       CImg_3x3(I,unsigned char);
51763 
51764       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
51765         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
51766         const unsigned int key = disp.key(), button = disp.button();
51767 
51768         // Generate graph representation.
51769         if (!visu0) {
51770           visu0.assign(disp.width(),disp.height(),1,3,220);
51771           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
51772           if (gdimx>0 && gdimy>0) {
51773             graph.assign(gdimx,gdimy,1,3,255);
51774             if (siz<32) {
51775               if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
51776                                          false,true,black,0.2f,0x33333333,0x33333333);
51777             } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
51778             cimg_forC(*this,c)
51779               graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
51780                                plot_type,vertex_type,nymax,nymin);
51781 
51782             axes.assign(gdimx,gdimy,1,1,0);
51783             const float
51784               dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
51785               px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
51786               py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
51787             const CImg<Tdouble>
51788               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
51789                 CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
51790               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);
51791 
51792             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
51793             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
51794             if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
51795             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
51796             if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
51797             if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);
51798 
51799             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
51800               if (Icc) {
51801                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
51802                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
51803               }
51804               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
51805                 cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
51806 
51807             visu0.draw_image(16,16,graph);
51808             visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
51809               draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
51810           } else graph.assign();
51811           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
51812           visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
51813           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
51814           visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
51815           visu.assign();
51816         }
51817 
51818         // Generate and display current view.
51819         if (!visu) {
51820           visu.assign(visu0);
51821           if (graph && x0>=0 && x1>=0) {
51822             const int
51823               nx0 = x0<=x1?x0:x1,
51824               nx1 = x0<=x1?x1:x0,
51825               ny0 = y0<=y1?y0:y1,
51826               ny1 = y0<=y1?y1:y0,
51827               sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
51828               sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
51829               sy0 = 16 + ny0,
51830               sy1 = 16 + ny1;
51831             if (y0>=0 && y1>=0)
51832               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
51833             else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
51834                    draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
51835                    draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
51836           }
51837           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
51838             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
51839             const unsigned int
51840               x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
51841             const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
51842             if (_spectrum>=7)
51843               cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
51844                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
51845                             (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
51846                             (double)(*this)(x,0,0,_spectrum - 1));
51847             else {
51848               cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
51849               cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
51850               cimg_sprintf(message._data + std::strlen(message),")");
51851             }
51852             if (x0>=0 && x1>=0) {
51853               const unsigned int
51854                 nx0 = (unsigned int)(x0<=x1?x0:x1),
51855                 nx1 = (unsigned int)(x0<=x1?x1:x0),
51856                 ny0 = (unsigned int)(y0<=y1?y0:y1),
51857                 ny1 = (unsigned int)(y0<=y1?y1:y0);
51858               const double
51859                 cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
51860                 cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
51861                 cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
51862                 cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
51863               if (y0>=0 && y1>=0)
51864                 cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
51865                              x0,cx0,cy0,x1 + one,cx1,cy1);
51866               else
51867                 cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
51868                              x0,cx0,x1 + one,cx1);
51869             }
51870             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
51871             visu.draw_image((visu.width() - text.width())/2,1,~text);
51872           }
51873           visu.display(disp);
51874         }
51875 
51876         // Test keys.
51877         CImg<charT> filename(32);
51878         switch (okey = key) {
51879 #if cimg_OS!=2
51880         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
51881 #endif
51882         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
51883         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51884           disp.set_fullscreen(false).
51885             resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51886                    CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
51887             _is_resized = true;
51888           disp.set_key(key,false); okey = 0;
51889         } break;
51890         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51891           disp.set_fullscreen(false).
51892             resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
51893           disp.set_key(key,false); okey = 0;
51894         } break;
51895         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51896             disp.set_fullscreen(false).
51897               resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
51898                                     CImgDisplay::screen_height()/2,1),false)._is_resized = true;
51899             disp.set_key(key,false); okey = 0;
51900           } break;
51901         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51902             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
51903             disp.set_key(key,false); okey = 0;
51904           } break;
51905         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51906             static unsigned int snap_number = 0;
51907             if (visu || visu0) {
51908               CImg<ucharT> &screen = visu?visu:visu0;
51909               std::FILE *file;
51910               do {
51911                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
51912                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51913               } while (file);
51914               (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
51915               screen.save(filename);
51916               (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
51917             }
51918             disp.set_key(key,false); okey = 0;
51919           } break;
51920         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51921             static unsigned int snap_number = 0;
51922             if (visu || visu0) {
51923               CImg<ucharT> &screen = visu?visu:visu0;
51924               std::FILE *file;
51925               do {
51926 
51927 #ifdef cimg_use_zlib
51928                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
51929 #else
51930                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
51931 #endif
51932                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51933               } while (file);
51934               (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp);
51935               save(filename);
51936               (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp);
51937             }
51938             disp.set_key(key,false); okey = 0;
51939           } break;
51940         }
51941 
51942         // Handle mouse motion and mouse buttons.
51943         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
51944           visu.assign();
51945           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
51946             const int
51947               mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
51948               cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
51949               my = mouse_y - 16,
51950               cy = cimg::cut(my,0,disp.height() - 32);
51951             if (button&1) {
51952               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
51953             }
51954             else if (button&2) {
51955               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
51956             }
51957             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
51958           } else if (!button && obutton) selected = true;
51959           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
51960         }
51961         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
51962         if (visu && visu0) disp.wait();
51963         if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
51964             (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51965           disp.set_key(key,false);
51966           okey = 0;
51967         }
51968       }
51969 
51970       disp._normalization = old_normalization;
51971       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
51972       if (y1<y0) cimg::swap(y0,y1);
51973       disp.set_key(okey);
51974       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
51975     }
51976 
51977     //! Load image from a file.
51978     /**
51979        \param filename Filename, as a C-string.
51980        \note The extension of \c filename defines the file format. If no filename
51981        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
51982     **/
51983     CImg<T>& load(const char *const filename) {
51984       if (!filename)
51985         throw CImgArgumentException(_cimg_instance
51986                                     "load(): Specified filename is (null).",
51987                                     cimg_instance);
51988 
51989       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
51990         CImg<charT> filename_local(256);
51991         load(cimg::load_network(filename,filename_local));
51992         std::remove(filename_local);
51993         return *this;
51994       }
51995 
51996       const char *const ext = cimg::split_filename(filename);
51997       const unsigned int omode = cimg::exception_mode();
51998       cimg::exception_mode(0);
51999       bool is_loaded = true;
52000       try {
52001 #ifdef cimg_load_plugin
52002         cimg_load_plugin(filename);
52003 #endif
52004 #ifdef cimg_load_plugin1
52005         cimg_load_plugin1(filename);
52006 #endif
52007 #ifdef cimg_load_plugin2
52008         cimg_load_plugin2(filename);
52009 #endif
52010 #ifdef cimg_load_plugin3
52011         cimg_load_plugin3(filename);
52012 #endif
52013 #ifdef cimg_load_plugin4
52014         cimg_load_plugin4(filename);
52015 #endif
52016 #ifdef cimg_load_plugin5
52017         cimg_load_plugin5(filename);
52018 #endif
52019 #ifdef cimg_load_plugin6
52020         cimg_load_plugin6(filename);
52021 #endif
52022 #ifdef cimg_load_plugin7
52023         cimg_load_plugin7(filename);
52024 #endif
52025 #ifdef cimg_load_plugin8
52026         cimg_load_plugin8(filename);
52027 #endif
52028         // Text formats
52029         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
52030         else if (!cimg::strcasecmp(ext,"csv") ||
52031                  !cimg::strcasecmp(ext,"dlm") ||
52032                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
52033         else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename);
52034 
52035         // 2D binary formats
52036         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
52037         else if (!cimg::strcasecmp(ext,"jpg") ||
52038                  !cimg::strcasecmp(ext,"jpeg") ||
52039                  !cimg::strcasecmp(ext,"jpe") ||
52040                  !cimg::strcasecmp(ext,"jfif") ||
52041                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
52042         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
52043         else if (!cimg::strcasecmp(ext,"ppm") ||
52044                  !cimg::strcasecmp(ext,"pgm") ||
52045                  !cimg::strcasecmp(ext,"pnm") ||
52046                  !cimg::strcasecmp(ext,"pbm") ||
52047                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
52048         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
52049         else if (!cimg::strcasecmp(ext,"tif") ||
52050                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
52051         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
52052         else if (!cimg::strcasecmp(ext,"cr2") ||
52053                  !cimg::strcasecmp(ext,"crw") ||
52054                  !cimg::strcasecmp(ext,"dcr") ||
52055                  !cimg::strcasecmp(ext,"mrw") ||
52056                  !cimg::strcasecmp(ext,"nef") ||
52057                  !cimg::strcasecmp(ext,"orf") ||
52058                  !cimg::strcasecmp(ext,"pix") ||
52059                  !cimg::strcasecmp(ext,"ptx") ||
52060                  !cimg::strcasecmp(ext,"raf") ||
52061                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
52062         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
52063         else if (!cimg::strcasecmp(ext,"heic") ||
52064                  !cimg::strcasecmp(ext,"avif")) load_heif(filename);
52065 
52066         // 3D binary formats
52067         else if (!cimg::strcasecmp(ext,"dcm") ||
52068                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
52069         else if (!cimg::strcasecmp(ext,"hdr") ||
52070                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
52071         else if (!cimg::strcasecmp(ext,"par") ||
52072                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
52073         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
52074         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
52075         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
52076         else if (!cimg::strcasecmp(ext,"cimg") ||
52077                  !cimg::strcasecmp(ext,"cimgz") ||
52078                  !*ext)  return load_cimg(filename);
52079 
52080         // Archive files
52081         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
52082 
52083         // Image sequences
52084         else if (!cimg::strcasecmp(ext,"avi") ||
52085                  !cimg::strcasecmp(ext,"mov") ||
52086                  !cimg::strcasecmp(ext,"asf") ||
52087                  !cimg::strcasecmp(ext,"divx") ||
52088                  !cimg::strcasecmp(ext,"flv") ||
52089                  !cimg::strcasecmp(ext,"mpg") ||
52090                  !cimg::strcasecmp(ext,"m1v") ||
52091                  !cimg::strcasecmp(ext,"m2v") ||
52092                  !cimg::strcasecmp(ext,"m4v") ||
52093                  !cimg::strcasecmp(ext,"mjp") ||
52094                  !cimg::strcasecmp(ext,"mp4") ||
52095                  !cimg::strcasecmp(ext,"mkv") ||
52096                  !cimg::strcasecmp(ext,"mpe") ||
52097                  !cimg::strcasecmp(ext,"movie") ||
52098                  !cimg::strcasecmp(ext,"ogm") ||
52099                  !cimg::strcasecmp(ext,"ogg") ||
52100                  !cimg::strcasecmp(ext,"ogv") ||
52101                  !cimg::strcasecmp(ext,"qt") ||
52102                  !cimg::strcasecmp(ext,"rm") ||
52103                  !cimg::strcasecmp(ext,"vob") ||
52104                  !cimg::strcasecmp(ext,"webm") ||
52105                  !cimg::strcasecmp(ext,"wmv") ||
52106                  !cimg::strcasecmp(ext,"xvid") ||
52107                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
52108         else is_loaded = false;
52109       } catch (CImgIOException&) { is_loaded = false; }
52110 
52111       // If nothing loaded, try to guess file format from magic number in file.
52112       if (!is_loaded) {
52113         std::FILE *file = cimg::std_fopen(filename,"rb");
52114         if (!file) {
52115           cimg::exception_mode(omode);
52116           throw CImgIOException(_cimg_instance
52117                                 "load(): Failed to open file '%s'.",
52118                                 cimg_instance,
52119                                 filename);
52120         }
52121 
52122         const char *const f_type = cimg::ftype(file,filename);
52123         cimg::fclose(file);
52124         is_loaded = true;
52125         try {
52126           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
52127           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
52128           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
52129           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
52130           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
52131           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
52132           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
52133           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
52134           else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
52135           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
52136           else is_loaded = false;
52137         } catch (CImgIOException&) { is_loaded = false; }
52138       }
52139 
52140       // If nothing loaded, try to load file with other means.
52141       if (!is_loaded) {
52142         try {
52143           load_other(filename);
52144         } catch (CImgIOException&) {
52145           cimg::exception_mode(omode);
52146           throw CImgIOException(_cimg_instance
52147                                 "load(): Failed to recognize format of file '%s'.",
52148                                 cimg_instance,
52149                                 filename);
52150         }
52151       }
52152       cimg::exception_mode(omode);
52153       return *this;
52154     }
52155 
52156     //! Load image from a file \newinstance.
52157     static CImg<T> get_load(const char *const filename) {
52158       return CImg<T>().load(filename);
52159     }
52160 
52161     //! Load image from an ascii file.
52162     /**
52163        \param filename Filename, as a C -string.
52164     **/
52165     CImg<T>& load_ascii(const char *const filename) {
52166       return _load_ascii(0,filename);
52167     }
52168 
52169     //! Load image from an ascii file \inplace.
52170     static CImg<T> get_load_ascii(const char *const filename) {
52171       return CImg<T>().load_ascii(filename);
52172     }
52173 
52174     //! Load image from an ascii file \overloading.
52175     CImg<T>& load_ascii(std::FILE *const file) {
52176       return _load_ascii(file,0);
52177     }
52178 
52179     //! Loadimage from an ascii file \newinstance.
52180     static CImg<T> get_load_ascii(std::FILE *const file) {
52181       return CImg<T>().load_ascii(file);
52182     }
52183 
52184     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
52185       if (!file && !filename)
52186         throw CImgArgumentException(_cimg_instance
52187                                     "load_ascii(): Specified filename is (null).",
52188                                     cimg_instance);
52189 
52190       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52191       CImg<charT> line(256); *line = 0;
52192       int err = std::fscanf(nfile,"%255[^\n]",line._data);
52193       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
52194       cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
52195       err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
52196       if (!dx || !dy || !dz || !dc) {
52197         if (!file) cimg::fclose(nfile);
52198         throw CImgIOException(_cimg_instance
52199                               "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
52200                               "to (%u,%u,%u,%u).",
52201                               cimg_instance,
52202                               filename?filename:"(FILE*)",dx,dy,dz,dc);
52203       }
52204       assign(dx,dy,dz,dc);
52205       const ulongT siz = size();
52206       ulongT off = 0;
52207       double val;
52208       T *ptr = _data;
52209       for (err = 1, off = 0; off<siz && err==1; ++off) {
52210         err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
52211         *(ptr++) = (T)val;
52212       }
52213       if (err!=1)
52214         cimg::warn(_cimg_instance
52215                    "load_ascii(): Only %lu/%lu values read from file '%s'.",
52216                    cimg_instance,
52217                    off - 1,siz,filename?filename:"(FILE*)");
52218 
52219       if (!file) cimg::fclose(nfile);
52220       return *this;
52221     }
52222 
52223     //! Load image from a DLM file.
52224     /**
52225       \param filename Filename, as a C-string.
52226     **/
52227     CImg<T>& load_dlm(const char *const filename) {
52228       return _load_dlm(0,filename);
52229     }
52230 
52231     //! Load image from a DLM file \newinstance.
52232     static CImg<T> get_load_dlm(const char *const filename) {
52233       return CImg<T>().load_dlm(filename);
52234     }
52235 
52236     //! Load image from a DLM file \overloading.
52237     CImg<T>& load_dlm(std::FILE *const file) {
52238       return _load_dlm(file,0);
52239     }
52240 
52241     //! Load image from a DLM file \newinstance.
52242     static CImg<T> get_load_dlm(std::FILE *const file) {
52243       return CImg<T>().load_dlm(file);
52244     }
52245 
52246     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
52247       if (!file && !filename)
52248         throw CImgArgumentException(_cimg_instance
52249                                     "load_dlm(): Specified filename is (null).",
52250                                     cimg_instance);
52251 
52252       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
52253       CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
52254       unsigned int cdx = 0, dx = 0, dy = 0;
52255       int err = 0;
52256       double val;
52257       assign(256,256,1,1,(T)0);
52258       while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
52259         if (err>0) (*this)(cdx++,dy) = (T)val;
52260         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
52261         char c = 0;
52262         if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
52263           dx = std::max(cdx,dx);
52264           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
52265           cdx = 0;
52266         }
52267       }
52268       if (cdx && err==1) { dx = cdx; ++dy; }
52269       if (!dx || !dy) {
52270         if (!file) cimg::fclose(nfile);
52271         throw CImgIOException(_cimg_instance
52272                               "load_dlm(): Invalid DLM file '%s'.",
52273                               cimg_instance,
52274                               filename?filename:"(FILE*)");
52275       }
52276       resize(dx,dy,1,1,0);
52277       if (!file) cimg::fclose(nfile);
52278       return *this;
52279     }
52280 
52281     //! Load image from a BMP file.
52282     /**
52283        \param filename Filename, as a C-string.
52284     **/
52285     CImg<T>& load_bmp(const char *const filename) {
52286       return _load_bmp(0,filename);
52287     }
52288 
52289     //! Load image from a BMP file \newinstance.
52290     static CImg<T> get_load_bmp(const char *const filename) {
52291       return CImg<T>().load_bmp(filename);
52292     }
52293 
52294     //! Load image from a BMP file \overloading.
52295     CImg<T>& load_bmp(std::FILE *const file) {
52296       return _load_bmp(file,0);
52297     }
52298 
52299     //! Load image from a BMP file \newinstance.
52300     static CImg<T> get_load_bmp(std::FILE *const file) {
52301       return CImg<T>().load_bmp(file);
52302     }
52303 
52304     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
52305       if (!file && !filename)
52306         throw CImgArgumentException(_cimg_instance
52307                                     "load_bmp(): Specified filename is (null).",
52308                                     cimg_instance);
52309 
52310       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52311       CImg<ucharT> header(54);
52312       cimg::fread(header._data,54,nfile);
52313       if (*header!='B' || header[1]!='M') {
52314         if (!file) cimg::fclose(nfile);
52315         throw CImgIOException(_cimg_instance
52316                               "load_bmp(): Invalid BMP file '%s'.",
52317                               cimg_instance,
52318                               filename?filename:"(FILE*)");
52319       }
52320 
52321       // Read header and pixel buffer
52322       int
52323         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
52324         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
52325         header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
52326         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
52327         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
52328         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
52329         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
52330         bpp = header[0x1C] + (header[0x1D]<<8);
52331 
52332       if (!file_size || file_size==offset) {
52333         cimg::fseek(nfile,0,SEEK_END);
52334         file_size = (int)cimg::ftell(nfile);
52335         cimg::fseek(nfile,54,SEEK_SET);
52336       }
52337       if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
52338 
52339       const int
52340         dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
52341         align_bytes = (4 - dx_bytes%4)%4;
52342       const ulongT
52343         cimg_iobuffer = (ulongT)24*1024*1024,
52344         buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes);
52345 
52346       CImg<intT> colormap;
52347       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
52348       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
52349       const int xoffset = offset - 14 - header_size - 4*nb_colors;
52350       if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
52351 
52352       CImg<ucharT> buffer;
52353       if (buf_size<cimg_iobuffer) {
52354         buffer.assign(buf_size,1,1,1,0);
52355         cimg::fread(buffer._data,buf_size,nfile);
52356       } else buffer.assign(dx_bytes + align_bytes);
52357       unsigned char *ptrs = buffer;
52358 
52359       // Decompress buffer (if necessary)
52360       if (compression==1 || compression==2) {
52361         if (file)
52362           throw CImgIOException(_cimg_instance
52363                                 "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
52364                                 cimg_instance);
52365         else {
52366           if (!file) cimg::fclose(nfile);
52367           return load_other(filename);
52368         }
52369       }
52370 
52371       // Read pixel data
52372       assign(dx,cimg::abs(dy),1,3,0);
52373       switch (bpp) {
52374       case 1 : { // Monochrome
52375         if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
52376           if (buf_size>=cimg_iobuffer) {
52377             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52378             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52379           }
52380           unsigned char mask = 0x80, val = 0;
52381           cimg_forX(*this,x) {
52382             if (mask==0x80) val = *(ptrs++);
52383             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
52384             (*this)(x,y,2) = (T)*(col++);
52385             (*this)(x,y,1) = (T)*(col++);
52386             (*this)(x,y,0) = (T)*(col++);
52387             mask = cimg::ror(mask);
52388           }
52389           ptrs+=align_bytes;
52390         }
52391       } break;
52392       case 4 : { // 16 colors
52393         if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
52394           if (buf_size>=cimg_iobuffer) {
52395             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52396             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52397           }
52398           unsigned char mask = 0xF0, val = 0;
52399           cimg_forX(*this,x) {
52400             if (mask==0xF0) val = *(ptrs++);
52401             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
52402             const unsigned char *col = (unsigned char*)(colormap._data + color);
52403             (*this)(x,y,2) = (T)*(col++);
52404             (*this)(x,y,1) = (T)*(col++);
52405             (*this)(x,y,0) = (T)*(col++);
52406             mask = cimg::ror(mask,4);
52407           }
52408           ptrs+=align_bytes;
52409         }
52410       } break;
52411       case 8 : { // 256 colors
52412         if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
52413           if (buf_size>=cimg_iobuffer) {
52414             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52415             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52416           }
52417           cimg_forX(*this,x) {
52418             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
52419             (*this)(x,y,2) = (T)*(col++);
52420             (*this)(x,y,1) = (T)*(col++);
52421             (*this)(x,y,0) = (T)*(col++);
52422           }
52423           ptrs+=align_bytes;
52424         }
52425       } break;
52426       case 16 : { // 16 bits colors (RGB565)
52427         for (int y = height() - 1; y>=0; --y) {
52428           if (buf_size>=cimg_iobuffer) {
52429             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52430             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52431           }
52432           cimg_forX(*this,x) {
52433             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
52434             const unsigned short col = (unsigned short)c2<<8 | c1;
52435             (*this)(x,y,2) = (T)((col&0x1F)<<3);
52436             (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3);
52437             (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3);
52438           }
52439           ptrs+=align_bytes;
52440         }
52441       } break;
52442       case 24 : { // 24 bits colors
52443         for (int y = height() - 1; y>=0; --y) {
52444           if (buf_size>=cimg_iobuffer) {
52445             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52446             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52447           }
52448           cimg_forX(*this,x) {
52449             (*this)(x,y,2) = (T)*(ptrs++);
52450             (*this)(x,y,1) = (T)*(ptrs++);
52451             (*this)(x,y,0) = (T)*(ptrs++);
52452           }
52453           ptrs+=align_bytes;
52454         }
52455       } break;
52456       case 32 : { // 32 bits colors
52457         for (int y = height() - 1; y>=0; --y) {
52458           if (buf_size>=cimg_iobuffer) {
52459             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52460             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52461           }
52462           cimg_forX(*this,x) {
52463             (*this)(x,y,2) = (T)*(ptrs++);
52464             (*this)(x,y,1) = (T)*(ptrs++);
52465             (*this)(x,y,0) = (T)*(ptrs++);
52466             ++ptrs;
52467           }
52468           ptrs+=align_bytes;
52469         }
52470       } break;
52471       }
52472       if (dy<0) mirror('y');
52473       if (!file) cimg::fclose(nfile);
52474       return *this;
52475     }
52476 
52477     //! Load image from a JPEG file.
52478     /**
52479        \param filename Filename, as a C-string.
52480     **/
52481     CImg<T>& load_jpeg(const char *const filename) {
52482       return _load_jpeg(0,filename);
52483     }
52484 
52485     //! Load image from a JPEG file \newinstance.
52486     static CImg<T> get_load_jpeg(const char *const filename) {
52487       return CImg<T>().load_jpeg(filename);
52488     }
52489 
52490     //! Load image from a JPEG file \overloading.
52491     CImg<T>& load_jpeg(std::FILE *const file) {
52492       return _load_jpeg(file,0);
52493     }
52494 
52495     //! Load image from a JPEG file \newinstance.
52496     static CImg<T> get_load_jpeg(std::FILE *const file) {
52497       return CImg<T>().load_jpeg(file);
52498     }
52499 
52500     // Custom error handler for libjpeg.
52501 #ifdef cimg_use_jpeg
52502     struct _cimg_error_mgr {
52503       struct jpeg_error_mgr original;
52504       jmp_buf setjmp_buffer;
52505       char message[JMSG_LENGTH_MAX];
52506     };
52507 
52508     typedef struct _cimg_error_mgr *_cimg_error_ptr;
52509 
52510     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
52511       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
52512       (*cinfo->err->format_message)(cinfo,c_err->message);
52513       jpeg_destroy(cinfo);  // Clean memory and temp files
52514       longjmp(c_err->setjmp_buffer,1);
52515     }
52516 #endif
52517 
52518     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
52519       if (!file && !filename)
52520         throw CImgArgumentException(_cimg_instance
52521                                     "load_jpeg(): Specified filename is (null).",
52522                                     cimg_instance);
52523 
52524 #ifndef cimg_use_jpeg
52525       if (file)
52526         throw CImgIOException(_cimg_instance
52527                               "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
52528                               cimg_instance);
52529       else return load_other(filename);
52530 #else
52531 
52532       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52533       struct jpeg_decompress_struct cinfo;
52534       struct _cimg_error_mgr jerr;
52535       cinfo.err = jpeg_std_error(&jerr.original);
52536       jerr.original.error_exit = _cimg_jpeg_error_exit;
52537       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
52538         if (!file) cimg::fclose(nfile);
52539         throw CImgIOException(_cimg_instance
52540                              "load_jpeg(): Error message returned by libjpeg: %s.",
52541                              cimg_instance,jerr.message);
52542       }
52543 
52544       jpeg_create_decompress(&cinfo);
52545       jpeg_stdio_src(&cinfo,nfile);
52546       jpeg_read_header(&cinfo,TRUE);
52547       jpeg_start_decompress(&cinfo);
52548 
52549       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
52550         if (!file) {
52551           cimg::fclose(nfile);
52552           return load_other(filename);
52553         } else
52554           throw CImgIOException(_cimg_instance
52555                                 "load_jpeg(): Failed to load JPEG data from file '%s'.",
52556                                 cimg_instance,filename?filename:"(FILE*)");
52557       }
52558       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
52559       JSAMPROW row_pointer[1];
52560       try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
52561       catch (...) { if (!file) cimg::fclose(nfile); throw; }
52562       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
52563         *ptr_a = _data + 3UL*_width*_height;
52564       while (cinfo.output_scanline<cinfo.output_height) {
52565         *row_pointer = buffer._data;
52566         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
52567           cimg::warn(_cimg_instance
52568                      "load_jpeg(): Incomplete data in file '%s'.",
52569                      cimg_instance,filename?filename:"(FILE*)");
52570           break;
52571         }
52572         const unsigned char *ptrs = buffer._data;
52573         switch (_spectrum) {
52574         case 1 : {
52575           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
52576         } break;
52577         case 3 : {
52578           cimg_forX(*this,x) {
52579             *(ptr_r++) = (T)*(ptrs++);
52580             *(ptr_g++) = (T)*(ptrs++);
52581             *(ptr_b++) = (T)*(ptrs++);
52582           }
52583         } break;
52584         case 4 : {
52585           cimg_forX(*this,x) {
52586             *(ptr_r++) = (T)*(ptrs++);
52587             *(ptr_g++) = (T)*(ptrs++);
52588             *(ptr_b++) = (T)*(ptrs++);
52589             *(ptr_a++) = (T)*(ptrs++);
52590           }
52591         } break;
52592         }
52593       }
52594       jpeg_finish_decompress(&cinfo);
52595       jpeg_destroy_decompress(&cinfo);
52596       if (!file) cimg::fclose(nfile);
52597       return *this;
52598 #endif
52599     }
52600 
52601     //! Load image from a file, using Magick++ library.
52602     /**
52603        \param filename Filename, as a C-string.
52604     **/
52605     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
52606     //   This is experimental code, not much tested, use with care.
52607     CImg<T>& load_magick(const char *const filename) {
52608       if (!filename)
52609         throw CImgArgumentException(_cimg_instance
52610                                     "load_magick(): Specified filename is (null).",
52611                                     cimg_instance);
52612 
52613 #ifdef cimg_use_magick
52614       Magick::Image image(filename);
52615       const unsigned int W = image.size().width(), H = image.size().height();
52616       switch (image.type()) {
52617       case Magick::PaletteMatteType :
52618       case Magick::TrueColorMatteType :
52619       case Magick::ColorSeparationType : {
52620         assign(W,H,1,4);
52621         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);
52622         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52623         for (ulongT off = (ulongT)W*H; off; --off) {
52624           *(ptr_r++) = (T)(pixels->red);
52625           *(ptr_g++) = (T)(pixels->green);
52626           *(ptr_b++) = (T)(pixels->blue);
52627           *(ptr_a++) = (T)(pixels->opacity);
52628           ++pixels;
52629         }
52630       } break;
52631       case Magick::PaletteType :
52632       case Magick::TrueColorType : {
52633         assign(W,H,1,3);
52634         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
52635         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52636         for (ulongT off = (ulongT)W*H; off; --off) {
52637           *(ptr_r++) = (T)(pixels->red);
52638           *(ptr_g++) = (T)(pixels->green);
52639           *(ptr_b++) = (T)(pixels->blue);
52640           ++pixels;
52641         }
52642       } break;
52643       case Magick::GrayscaleMatteType : {
52644         assign(W,H,1,2);
52645         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
52646         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52647         for (ulongT off = (ulongT)W*H; off; --off) {
52648           *(ptr_r++) = (T)(pixels->red);
52649           *(ptr_a++) = (T)(pixels->opacity);
52650           ++pixels;
52651         }
52652       } break;
52653       default : {
52654         assign(W,H,1,1);
52655         T *ptr_r = data(0,0,0,0);
52656         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52657         for (ulongT off = (ulongT)W*H; off; --off) {
52658           *(ptr_r++) = (T)(pixels->red);
52659           ++pixels;
52660         }
52661       }
52662       }
52663       return *this;
52664 #else
52665       throw CImgIOException(_cimg_instance
52666                             "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
52667                             cimg_instance,
52668                             filename);
52669 #endif
52670     }
52671 
52672     //! Load image from a file, using Magick++ library \newinstance.
52673     static CImg<T> get_load_magick(const char *const filename) {
52674       return CImg<T>().load_magick(filename);
52675     }
52676 
52677     //! Load image from a PNG file.
52678     /**
52679        \param filename Filename, as a C-string.
52680        \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
52681     **/
52682     CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_value=0) {
52683       return _load_png(0,filename,bits_per_value);
52684     }
52685 
52686     //! Load image from a PNG file \newinstance.
52687     static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_value=0) {
52688       return CImg<T>().load_png(filename,bits_per_value);
52689     }
52690 
52691     //! Load image from a PNG file \overloading.
52692     CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
52693       return _load_png(file,0,bits_per_value);
52694     }
52695 
52696     //! Load image from a PNG file \newinstance.
52697     static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
52698       return CImg<T>().load_png(file,bits_per_value);
52699     }
52700 
52701     // (Note: Most of this function has been written by Eric Fausett)
52702     CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) {
52703       if (!file && !filename)
52704         throw CImgArgumentException(_cimg_instance
52705                                     "load_png(): Specified filename is (null).",
52706                                     cimg_instance);
52707 
52708 #ifndef cimg_use_png
52709       cimg::unused(bits_per_value);
52710       if (file)
52711         throw CImgIOException(_cimg_instance
52712                               "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
52713                               cimg_instance);
52714 
52715       else return load_other(filename);
52716 #else
52717       // Open file and check for PNG validity
52718 #if defined __GNUC__
52719       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
52720       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
52721 #else
52722       const char *nfilename = filename;
52723       std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
52724 #endif
52725       unsigned char pngCheck[8] = { 0 };
52726       cimg::fread(pngCheck,8,(std::FILE*)nfile);
52727       if (png_sig_cmp(pngCheck,0,8)) {
52728         if (!file) cimg::fclose(nfile);
52729         throw CImgIOException(_cimg_instance
52730                               "load_png(): Invalid PNG file '%s'.",
52731                               cimg_instance,
52732                               nfilename?nfilename:"(FILE*)");
52733       }
52734 
52735       // Setup PNG structures for read
52736       png_voidp user_error_ptr = 0;
52737       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
52738       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
52739       if (!png_ptr) {
52740         if (!file) cimg::fclose(nfile);
52741         throw CImgIOException(_cimg_instance
52742                               "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
52743                               cimg_instance,
52744                               nfilename?nfilename:"(FILE*)");
52745       }
52746       png_infop info_ptr = png_create_info_struct(png_ptr);
52747       if (!info_ptr) {
52748         if (!file) cimg::fclose(nfile);
52749         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
52750         throw CImgIOException(_cimg_instance
52751                               "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
52752                               cimg_instance,
52753                               nfilename?nfilename:"(FILE*)");
52754       }
52755       png_infop end_info = png_create_info_struct(png_ptr);
52756       if (!end_info) {
52757         if (!file) cimg::fclose(nfile);
52758         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
52759         throw CImgIOException(_cimg_instance
52760                               "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
52761                               cimg_instance,
52762                               nfilename?nfilename:"(FILE*)");
52763       }
52764 
52765       // Error handling callback for png file reading
52766       if (setjmp(png_jmpbuf(png_ptr))) {
52767         if (!file) cimg::fclose((std::FILE*)nfile);
52768         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
52769         throw CImgIOException(_cimg_instance
52770                               "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
52771                               cimg_instance,
52772                               nfilename?nfilename:"(FILE*)");
52773       }
52774       png_init_io(png_ptr, nfile);
52775       png_set_sig_bytes(png_ptr, 8);
52776 
52777       // Get PNG Header Info up to data block
52778       png_read_info(png_ptr,info_ptr);
52779       png_uint_32 W, H;
52780       int bit_depth, color_type, interlace_type;
52781       bool is_gray = false;
52782       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
52783       if (bits_per_value) *bits_per_value = (unsigned int)bit_depth;
52784 
52785       // Transforms to unify image data
52786       if (color_type==PNG_COLOR_TYPE_PALETTE) {
52787         png_set_palette_to_rgb(png_ptr);
52788         color_type = PNG_COLOR_TYPE_RGB;
52789         bit_depth = 8;
52790       }
52791       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
52792         png_set_expand_gray_1_2_4_to_8(png_ptr);
52793         is_gray = true;
52794         bit_depth = 8;
52795       }
52796       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
52797         png_set_tRNS_to_alpha(png_ptr);
52798         color_type |= PNG_COLOR_MASK_ALPHA;
52799       }
52800       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
52801         png_set_gray_to_rgb(png_ptr);
52802         color_type |= PNG_COLOR_MASK_COLOR;
52803         is_gray = true;
52804       }
52805       if (color_type==PNG_COLOR_TYPE_RGB)
52806         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
52807 
52808       png_read_update_info(png_ptr,info_ptr);
52809       if (bit_depth!=8 && bit_depth!=16) {
52810         if (!file) cimg::fclose(nfile);
52811         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
52812         throw CImgIOException(_cimg_instance
52813                               "load_png(): Invalid bit depth %u in file '%s'.",
52814                               cimg_instance,
52815                               bit_depth,nfilename?nfilename:"(FILE*)");
52816       }
52817       const int byte_depth = bit_depth>>3;
52818 
52819       // Allocate memory for image reading
52820       png_bytep *const imgData = new png_bytep[H];
52821       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
52822       png_read_image(png_ptr,imgData);
52823       png_read_end(png_ptr,end_info);
52824 
52825       // Read pixel data
52826       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
52827         if (!file) cimg::fclose(nfile);
52828         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
52829         throw CImgIOException(_cimg_instance
52830                               "load_png(): Invalid color coding type %u in file '%s'.",
52831                               cimg_instance,
52832                               color_type,nfilename?nfilename:"(FILE*)");
52833       }
52834       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
52835       try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
52836       catch (...) { if (!file) cimg::fclose(nfile); throw; }
52837       T
52838         *ptr_r = data(0,0,0,0),
52839         *ptr_g = is_gray?0:data(0,0,0,1),
52840         *ptr_b = is_gray?0:data(0,0,0,2),
52841         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
52842       switch (bit_depth) {
52843       case 8 : {
52844         cimg_forY(*this,y) {
52845           const unsigned char *ptrs = (unsigned char*)imgData[y];
52846           cimg_forX(*this,x) {
52847             *(ptr_r++) = (T)*(ptrs++);
52848             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
52849             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
52850             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
52851           }
52852         }
52853       } break;
52854       case 16 : {
52855         cimg_forY(*this,y) {
52856           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
52857           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
52858           cimg_forX(*this,x) {
52859             *(ptr_r++) = (T)*(ptrs++);
52860             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
52861             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
52862             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
52863           }
52864         }
52865       } break;
52866       }
52867       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
52868 
52869       // Deallocate image read memory
52870       cimg_forY(*this,n) delete[] imgData[n];
52871       delete[] imgData;
52872       if (!file) cimg::fclose(nfile);
52873       return *this;
52874 #endif
52875     }
52876 
52877     //! Load image from a PNM file.
52878     /**
52879       \param filename Filename, as a C-string.
52880     **/
52881     CImg<T>& load_pnm(const char *const filename) {
52882       return _load_pnm(0,filename);
52883     }
52884 
52885     //! Load image from a PNM file \newinstance.
52886     static CImg<T> get_load_pnm(const char *const filename) {
52887       return CImg<T>().load_pnm(filename);
52888     }
52889 
52890     //! Load image from a PNM file \overloading.
52891     CImg<T>& load_pnm(std::FILE *const file) {
52892       return _load_pnm(file,0);
52893     }
52894 
52895     //! Load image from a PNM file \newinstance.
52896     static CImg<T> get_load_pnm(std::FILE *const file) {
52897       return CImg<T>().load_pnm(file);
52898     }
52899 
52900     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
52901       if (!file && !filename)
52902         throw CImgArgumentException(_cimg_instance
52903                                     "load_pnm(): Specified filename is (null).",
52904                                     cimg_instance);
52905 
52906       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52907       unsigned int ppm_type, W, H, D = 1, colormax = 255;
52908       CImg<charT> item(16384,1,1,1,0);
52909       int err, rval, gval, bval;
52910       const longT cimg_iobuffer = (longT)24*1024*1024;
52911       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52912       if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
52913         if (!file) cimg::fclose(nfile);
52914         throw CImgIOException(_cimg_instance
52915                               "load_pnm(): PNM header not found in file '%s'.",
52916                               cimg_instance,
52917                               filename?filename:"(FILE*)");
52918       }
52919       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52920       if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
52921         if (!file) cimg::fclose(nfile);
52922         throw CImgIOException(_cimg_instance
52923                               "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
52924                               cimg_instance,
52925                               filename?filename:"(FILE*)");
52926       }
52927       if (ppm_type!=1 && ppm_type!=4) {
52928         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
52929           while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52930           if (cimg_sscanf(item,"%u",&colormax)!=1)
52931             cimg::warn(_cimg_instance
52932                        "load_pnm(): COLORMAX field is undefined in file '%s'.",
52933                        cimg_instance,
52934                        filename?filename:"(FILE*)");
52935         } else { colormax = D; D = 1; }
52936       }
52937       std::fgetc(nfile);
52938 
52939       if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension
52940         const cimg_int64 siz = cimg::fsize(filename);
52941         if (W*H*D>siz)
52942           throw CImgIOException(_cimg_instance
52943                                 "load_pnm(): Specified image dimensions in file '%s' exceed file size.",
52944                                 cimg_instance,
52945                                 filename);
52946       }
52947 
52948       switch (ppm_type) {
52949       case 1 : { // 2D B&W ascii
52950         assign(W,H,1,1);
52951         T* ptrd = _data;
52952         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
52953       } break;
52954       case 2 : { // 2D grey ascii
52955         assign(W,H,1,1);
52956         T* ptrd = _data;
52957         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
52958       } break;
52959       case 3 : { // 2D color ascii
52960         assign(W,H,1,3);
52961         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
52962         cimg_forXY(*this,x,y) {
52963           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
52964             *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
52965           } else break;
52966         }
52967       } break;
52968       case 4 : { // 2D b&w binary (support 3D PINK extension)
52969         CImg<ucharT> raw;
52970         assign(W,H,D,1);
52971         T *ptrd = data(0,0,0,0);
52972         unsigned int w = 0, h = 0, d = 0;
52973         for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
52974           raw.assign(std::min(to_read,cimg_iobuffer));
52975           cimg::fread(raw._data,raw._width,nfile);
52976           to_read-=raw._width;
52977           const unsigned char *ptrs = raw._data;
52978           unsigned char mask = 0, val = 0;
52979           for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
52980             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
52981             *(ptrd++) = (T)((val&mask)?0:255);
52982             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
52983           }
52984         }
52985       } break;
52986       case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension)
52987         if (colormax<256) { // 8 bits
52988           CImg<ucharT> raw;
52989           assign(W,H,D,1);
52990           T *ptrd = data(0,0,0,0);
52991           for (longT to_read = (longT)size(); to_read>0; ) {
52992             raw.assign(std::min(to_read,cimg_iobuffer));
52993             cimg::fread(raw._data,raw._width,nfile);
52994             to_read-=raw._width;
52995             const unsigned char *ptrs = raw._data;
52996             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
52997           }
52998         } else { // 16 bits
52999           CImg<ushortT> raw;
53000           assign(W,H,D,1);
53001           T *ptrd = data(0,0,0,0);
53002           for (longT to_read = (longT)size(); to_read>0; ) {
53003             raw.assign(std::min(to_read,cimg_iobuffer/2));
53004             cimg::fread(raw._data,raw._width,nfile);
53005             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
53006             to_read-=raw._width;
53007             const unsigned short *ptrs = raw._data;
53008             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53009           }
53010         }
53011       } break;
53012       case 6 : { // 2D color binary
53013         if (colormax<256) { // 8 bits
53014           CImg<ucharT> raw;
53015           assign(W,H,1,3);
53016           T
53017             *ptr_r = data(0,0,0,0),
53018             *ptr_g = data(0,0,0,1),
53019             *ptr_b = data(0,0,0,2);
53020           for (longT to_read = (longT)size(); to_read>0; ) {
53021             raw.assign(std::min(to_read,cimg_iobuffer));
53022             cimg::fread(raw._data,raw._width,nfile);
53023             to_read-=raw._width;
53024             const unsigned char *ptrs = raw._data;
53025             for (ulongT off = (ulongT)raw._width/3; off; --off) {
53026               *(ptr_r++) = (T)*(ptrs++);
53027               *(ptr_g++) = (T)*(ptrs++);
53028               *(ptr_b++) = (T)*(ptrs++);
53029             }
53030           }
53031         } else { // 16 bits
53032           CImg<ushortT> raw;
53033           assign(W,H,1,3);
53034           T
53035             *ptr_r = data(0,0,0,0),
53036             *ptr_g = data(0,0,0,1),
53037             *ptr_b = data(0,0,0,2);
53038           for (longT to_read = (longT)size(); to_read>0; ) {
53039             raw.assign(std::min(to_read,cimg_iobuffer/2));
53040             cimg::fread(raw._data,raw._width,nfile);
53041             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
53042             to_read-=raw._width;
53043             const unsigned short *ptrs = raw._data;
53044             for (ulongT off = (ulongT)raw._width/3; off; --off) {
53045               *(ptr_r++) = (T)*(ptrs++);
53046               *(ptr_g++) = (T)*(ptrs++);
53047               *(ptr_b++) = (T)*(ptrs++);
53048             }
53049           }
53050         }
53051       } break;
53052       case 8 : { // 2D/3D grey binary with int32 integers (PINK extension)
53053         CImg<intT> raw;
53054         assign(W,H,D,1);
53055         T *ptrd = data(0,0,0,0);
53056         for (longT to_read = (longT)size(); to_read>0; ) {
53057           raw.assign(std::min(to_read,cimg_iobuffer));
53058           cimg::fread(raw._data,raw._width,nfile);
53059           to_read-=raw._width;
53060           const int *ptrs = raw._data;
53061           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53062         }
53063       } break;
53064       case 9 : { // 2D/3D grey binary with float values (PINK extension)
53065         CImg<floatT> raw;
53066         assign(W,H,D,1);
53067         T *ptrd = data(0,0,0,0);
53068         for (longT to_read = (longT)size(); to_read>0; ) {
53069           raw.assign(std::min(to_read,cimg_iobuffer));
53070           cimg::fread(raw._data,raw._width,nfile);
53071           to_read-=raw._width;
53072           const float *ptrs = raw._data;
53073           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53074         }
53075       } break;
53076       default :
53077         assign();
53078         if (!file) cimg::fclose(nfile);
53079         throw CImgIOException(_cimg_instance
53080                               "load_pnm(): PNM type 'P%d' found, but type is not supported.",
53081                               cimg_instance,
53082                               filename?filename:"(FILE*)",ppm_type);
53083       }
53084       if (!file) cimg::fclose(nfile);
53085       return *this;
53086     }
53087 
53088     //! Load image from a PFM file.
53089     /**
53090       \param filename Filename, as a C-string.
53091     **/
53092     CImg<T>& load_pfm(const char *const filename) {
53093       return _load_pfm(0,filename);
53094     }
53095 
53096     //! Load image from a PFM file \newinstance.
53097     static CImg<T> get_load_pfm(const char *const filename) {
53098       return CImg<T>().load_pfm(filename);
53099     }
53100 
53101     //! Load image from a PFM file \overloading.
53102     CImg<T>& load_pfm(std::FILE *const file) {
53103       return _load_pfm(file,0);
53104     }
53105 
53106     //! Load image from a PFM file \newinstance.
53107     static CImg<T> get_load_pfm(std::FILE *const file) {
53108       return CImg<T>().load_pfm(file);
53109     }
53110 
53111     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
53112       if (!file && !filename)
53113         throw CImgArgumentException(_cimg_instance
53114                                     "load_pfm(): Specified filename is (null).",
53115                                     cimg_instance);
53116 
53117       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53118       char pfm_type;
53119       CImg<charT> item(16384,1,1,1,0);
53120       int W = 0, H = 0, err = 0;
53121       double scale = 0;
53122       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53123       if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
53124         if (!file) cimg::fclose(nfile);
53125         throw CImgIOException(_cimg_instance
53126                               "load_pfm(): PFM header not found in file '%s'.",
53127                               cimg_instance,
53128                               filename?filename:"(FILE*)");
53129       }
53130       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53131       if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
53132         if (!file) cimg::fclose(nfile);
53133         throw CImgIOException(_cimg_instance
53134                               "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
53135                               cimg_instance,
53136                               filename?filename:"(FILE*)");
53137       } else if (W<=0 || H<=0) {
53138         if (!file) cimg::fclose(nfile);
53139         throw CImgIOException(_cimg_instance
53140                               "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
53141                               cimg_instance,W,H,
53142                               filename?filename:"(FILE*)");
53143       }
53144       if (err==2) {
53145         while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53146         if (cimg_sscanf(item,"%lf",&scale)!=1)
53147           cimg::warn(_cimg_instance
53148                      "load_pfm(): SCALE field is undefined in file '%s'.",
53149                      cimg_instance,
53150                      filename?filename:"(FILE*)");
53151       }
53152       std::fgetc(nfile);
53153       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
53154       if (is_color) {
53155         assign(W,H,1,3,(T)0);
53156         CImg<floatT> buf(3*W);
53157         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
53158         cimg_forY(*this,y) {
53159           cimg::fread(buf._data,3*W,nfile);
53160           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
53161           const float *ptrs = buf._data;
53162           cimg_forX(*this,x) {
53163             *(ptr_r++) = (T)*(ptrs++);
53164             *(ptr_g++) = (T)*(ptrs++);
53165             *(ptr_b++) = (T)*(ptrs++);
53166           }
53167         }
53168       } else {
53169         assign(W,H,1,1,(T)0);
53170         CImg<floatT> buf(W);
53171         T *ptrd = data(0,0,0,0);
53172         cimg_forY(*this,y) {
53173           cimg::fread(buf._data,W,nfile);
53174           if (is_inverted) cimg::invert_endianness(buf._data,W);
53175           const float *ptrs = buf._data;
53176           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
53177         }
53178       }
53179       if (!file) cimg::fclose(nfile);
53180       return mirror('y');  // Most of the .pfm files are flipped along the y-axis
53181     }
53182 
53183     //! Load image from a RGB file.
53184     /**
53185       \param filename Filename, as a C-string.
53186       \param dimw Width of the image buffer.
53187       \param dimh Height of the image buffer.
53188     **/
53189     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53190       return _load_rgb(0,filename,dimw,dimh);
53191     }
53192 
53193     //! Load image from a RGB file \newinstance.
53194     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53195       return CImg<T>().load_rgb(filename,dimw,dimh);
53196     }
53197 
53198     //! Load image from a RGB file \overloading.
53199     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53200       return _load_rgb(file,0,dimw,dimh);
53201     }
53202 
53203     //! Load image from a RGB file \newinstance.
53204     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53205       return CImg<T>().load_rgb(file,dimw,dimh);
53206     }
53207 
53208     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
53209                        const unsigned int dimw, const unsigned int dimh) {
53210       if (!file && !filename)
53211         throw CImgArgumentException(_cimg_instance
53212                                     "load_rgb(): Specified filename is (null).",
53213                                     cimg_instance);
53214 
53215       if (!dimw || !dimh) return assign();
53216       const longT cimg_iobuffer = (longT)24*1024*1024;
53217       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53218       CImg<ucharT> raw;
53219       assign(dimw,dimh,1,3);
53220       T
53221         *ptr_r = data(0,0,0,0),
53222         *ptr_g = data(0,0,0,1),
53223         *ptr_b = data(0,0,0,2);
53224       for (longT to_read = (longT)size(); to_read>0; ) {
53225         raw.assign(std::min(to_read,cimg_iobuffer));
53226         cimg::fread(raw._data,raw._width,nfile);
53227         to_read-=raw._width;
53228         const unsigned char *ptrs = raw._data;
53229         for (ulongT off = raw._width/3UL; off; --off) {
53230           *(ptr_r++) = (T)*(ptrs++);
53231           *(ptr_g++) = (T)*(ptrs++);
53232           *(ptr_b++) = (T)*(ptrs++);
53233         }
53234       }
53235       if (!file) cimg::fclose(nfile);
53236       return *this;
53237     }
53238 
53239     //! Load image from a RGBA file.
53240     /**
53241        \param filename Filename, as a C-string.
53242        \param dimw Width of the image buffer.
53243        \param dimh Height of the image buffer.
53244     **/
53245     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53246       return _load_rgba(0,filename,dimw,dimh);
53247     }
53248 
53249     //! Load image from a RGBA file \newinstance.
53250     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53251       return CImg<T>().load_rgba(filename,dimw,dimh);
53252     }
53253 
53254     //! Load image from a RGBA file \overloading.
53255     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53256       return _load_rgba(file,0,dimw,dimh);
53257     }
53258 
53259     //! Load image from a RGBA file \newinstance.
53260     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53261       return CImg<T>().load_rgba(file,dimw,dimh);
53262     }
53263 
53264     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
53265                         const unsigned int dimw, const unsigned int dimh) {
53266       if (!file && !filename)
53267         throw CImgArgumentException(_cimg_instance
53268                                     "load_rgba(): Specified filename is (null).",
53269                                     cimg_instance);
53270 
53271       if (!dimw || !dimh) return assign();
53272       const longT cimg_iobuffer = (longT)24*1024*1024;
53273       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53274       CImg<ucharT> raw;
53275       assign(dimw,dimh,1,4);
53276       T
53277         *ptr_r = data(0,0,0,0),
53278         *ptr_g = data(0,0,0,1),
53279         *ptr_b = data(0,0,0,2),
53280         *ptr_a = data(0,0,0,3);
53281       for (longT to_read = (longT)size(); to_read>0; ) {
53282         raw.assign(std::min(to_read,cimg_iobuffer));
53283         cimg::fread(raw._data,raw._width,nfile);
53284         to_read-=raw._width;
53285         const unsigned char *ptrs = raw._data;
53286         for (ulongT off = raw._width/4UL; off; --off) {
53287           *(ptr_r++) = (T)*(ptrs++);
53288           *(ptr_g++) = (T)*(ptrs++);
53289           *(ptr_b++) = (T)*(ptrs++);
53290           *(ptr_a++) = (T)*(ptrs++);
53291         }
53292       }
53293       if (!file) cimg::fclose(nfile);
53294       return *this;
53295     }
53296 
53297     //! Load image from a TIFF file.
53298     /**
53299        \param filename Filename, as a C-string.
53300        \param first_frame First frame to read (for multi-pages tiff).
53301        \param last_frame Last frame to read (for multi-pages tiff).
53302        \param step_frame Step value of frame reading.
53303        \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
53304        \param[out] voxel_size Voxel size, as stored in the filename.
53305        \param[out] description Description, as stored in the filename.
53306        \note
53307        - libtiff support is enabled by defining the precompilation
53308         directive \c cimg_use_tif.
53309        - When libtiff is enabled, 2D and 3D (multipage) several
53310         channel per pixel are supported for
53311         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
53312        - If \c cimg_use_tiff is not defined at compile time the
53313         function uses CImg<T>& load_other(const char*).
53314      **/
53315     CImg<T>& load_tiff(const char *const filename,
53316                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53317                        const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
53318                        float *const voxel_size=0, CImg<charT> *const description=0) {
53319       if (!filename)
53320         throw CImgArgumentException(_cimg_instance
53321                                     "load_tiff(): Specified filename is (null).",
53322                                     cimg_instance);
53323 
53324       const unsigned int
53325         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
53326         nstep_frame = step_frame?step_frame:1;
53327       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
53328 
53329 #ifndef cimg_use_tiff
53330       cimg::unused(bits_per_value,voxel_size,description);
53331       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
53332         throw CImgArgumentException(_cimg_instance
53333                                     "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
53334                                     cimg_instance,
53335                                     filename);
53336       return load_other(filename);
53337 #else
53338 #if cimg_verbosity<3
53339       TIFFSetWarningHandler(0);
53340       TIFFSetErrorHandler(0);
53341 #endif
53342       TIFF *tif = TIFFOpen(filename,"r");
53343       if (tif) {
53344         unsigned int nb_images = 0;
53345         do ++nb_images; while (TIFFReadDirectory(tif));
53346         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
53347           cimg::warn(_cimg_instance
53348                      "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
53349                      cimg_instance,
53350                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
53351 
53352         if (nfirst_frame>=nb_images) return assign();
53353         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
53354         TIFFSetDirectory(tif,0);
53355         CImg<T> frame;
53356         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
53357           frame._load_tiff(tif,l,bits_per_value,voxel_size,description);
53358           if (l==nfirst_frame)
53359             assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
53360           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
53361             resize(std::max(frame._width,_width),
53362                    std::max(frame._height,_height),-100,
53363                    std::max(frame._spectrum,_spectrum),0);
53364           draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
53365         }
53366         TIFFClose(tif);
53367       } else throw CImgIOException(_cimg_instance
53368                                    "load_tiff(): Failed to open file '%s'.",
53369                                    cimg_instance,
53370                                    filename);
53371       return *this;
53372 #endif
53373     }
53374 
53375     //! Load image from a TIFF file \newinstance.
53376     static CImg<T> get_load_tiff(const char *const filename,
53377                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53378                                  const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
53379                                  float *const voxel_size=0, CImg<charT> *const description=0) {
53380       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
53381     }
53382 
53383     // (Original contribution by Jerome Boulanger).
53384 #ifdef cimg_use_tiff
53385     template<typename t>
53386     void _load_tiff_tiled_contig(TIFF *const tif, const uint16_t samplesperpixel,
53387                                  const uint32_t nx, const uint32_t ny, const uint32_t tw, const uint32_t th) {
53388       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
53389       if (buf) {
53390         for (unsigned int row = 0; row<ny; row+=th)
53391           for (unsigned int col = 0; col<nx; col+=tw) {
53392             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
53393               _TIFFfree(buf); TIFFClose(tif);
53394               throw CImgIOException(_cimg_instance
53395                                     "load_tiff(): Invalid tile in file '%s'.",
53396                                     cimg_instance,
53397                                     TIFFFileName(tif));
53398             }
53399             const t *ptr = buf;
53400             for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
53401               for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
53402                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
53403                   (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
53404           }
53405         _TIFFfree(buf);
53406       }
53407     }
53408 
53409     template<typename t>
53410     void _load_tiff_tiled_separate(TIFF *const tif, const uint16_t samplesperpixel,
53411                                    const uint32_t nx, const uint32_t ny, const uint32_t tw, const uint32_t th) {
53412       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
53413       if (buf) {
53414         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
53415           for (unsigned int row = 0; row<ny; row+=th)
53416             for (unsigned int col = 0; col<nx; col+=tw) {
53417               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
53418                 _TIFFfree(buf); TIFFClose(tif);
53419                 throw CImgIOException(_cimg_instance
53420                                       "load_tiff(): Invalid tile in file '%s'.",
53421                                       cimg_instance,
53422                                       TIFFFileName(tif));
53423               }
53424               const t *ptr = buf;
53425               for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
53426                 for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
53427                   (*this)(cc,rr,vv) = (T)*(ptr++);
53428             }
53429         _TIFFfree(buf);
53430       }
53431     }
53432 
53433     template<typename t>
53434     void _load_tiff_contig(TIFF *const tif, const uint16_t samplesperpixel, const uint32_t nx, const uint32_t ny) {
53435       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
53436       if (buf) {
53437         uint32_t row, rowsperstrip = (uint32_t)-1;
53438         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
53439         for (row = 0; row<ny; row+= rowsperstrip) {
53440           uint32_t nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
53441           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
53442           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
53443             _TIFFfree(buf); TIFFClose(tif);
53444             throw CImgIOException(_cimg_instance
53445                                   "load_tiff(): Invalid strip in file '%s'.",
53446                                   cimg_instance,
53447                                   TIFFFileName(tif));
53448           }
53449           const t *ptr = buf;
53450           for (unsigned int rr = 0; rr<nrow; ++rr)
53451             for (unsigned int cc = 0; cc<nx; ++cc)
53452               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
53453         }
53454         _TIFFfree(buf);
53455       }
53456     }
53457 
53458     template<typename t>
53459     void _load_tiff_separate(TIFF *const tif, const uint16_t samplesperpixel, const uint32_t nx, const uint32_t ny) {
53460       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
53461       if (buf) {
53462         uint32_t row, rowsperstrip = (uint32_t)-1;
53463         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
53464         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
53465           for (row = 0; row<ny; row+= rowsperstrip) {
53466             uint32_t nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
53467             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
53468             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
53469               _TIFFfree(buf); TIFFClose(tif);
53470               throw CImgIOException(_cimg_instance
53471                                     "load_tiff(): Invalid strip in file '%s'.",
53472                                     cimg_instance,
53473                                     TIFFFileName(tif));
53474             }
53475             const t *ptr = buf;
53476             for (unsigned int rr = 0;rr<nrow; ++rr)
53477               for (unsigned int cc = 0; cc<nx; ++cc)
53478                 (*this)(cc,row + rr,vv) = (T)*(ptr++);
53479           }
53480         _TIFFfree(buf);
53481       }
53482     }
53483 
53484     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value,
53485                         float *const voxel_size, CImg<charT> *const description) {
53486       if (!TIFFSetDirectory(tif,directory)) return assign();
53487       uint16_t samplesperpixel = 1, bitspersample = 8, photo = 0;
53488       uint16_t sampleformat = 1;
53489       uint32_t nx = 1, ny = 1;
53490       const char *const filename = TIFFFileName(tif);
53491       const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
53492       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
53493       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
53494       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
53495       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
53496       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
53497       if (bits_per_value) *bits_per_value = (unsigned int)bitspersample;
53498       if (voxel_size) {
53499         const char *s_description = 0;
53500         float vx = 0, vy = 0, vz = 0;
53501         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
53502           const char *s_desc = std::strstr(s_description,"VX=");
53503           if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format
53504             voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
53505           }
53506           s_desc = std::strstr(s_description,"spacing=");
53507           if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format
53508             voxel_size[2] = vz;
53509           }
53510         }
53511         TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
53512         TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
53513         voxel_size[0] = 1.f/voxel_size[0];
53514         voxel_size[1] = 1.f/voxel_size[1];
53515       }
53516       if (description) {
53517         const char *s_description = 0;
53518         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
53519           CImg<charT>::string(s_description).move_to(*description);
53520       }
53521       const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
53522       assign(nx,ny,1,spectrum);
53523 
53524       if ((photo>=3 && sampleformat==1 &&
53525            (bitspersample==4 || bitspersample==8) &&
53526            (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
53527           (bitspersample==1 && samplesperpixel==1)) {
53528         // Special case for unsigned color images.
53529         uint32_t *const raster = (uint32_t*)_TIFFmalloc(nx*ny*sizeof(uint32_t));
53530         if (!raster) {
53531           _TIFFfree(raster); TIFFClose(tif);
53532           throw CImgException(_cimg_instance
53533                               "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
53534                               cimg_instance,
53535                               cimg::strbuffersize(nx*ny*sizeof(uint32_t)),filename);
53536         }
53537         TIFFReadRGBAImage(tif,nx,ny,raster,0);
53538         switch (spectrum) {
53539         case 1 :
53540           cimg_forXY(*this,x,y)
53541             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
53542           break;
53543         case 3 :
53544           cimg_forXY(*this,x,y) {
53545             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
53546             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
53547             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
53548           }
53549           break;
53550         case 4 :
53551           cimg_forXY(*this,x,y) {
53552             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
53553             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
53554             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
53555             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
53556           }
53557           break;
53558         }
53559         _TIFFfree(raster);
53560       } else { // Other cases
53561         uint16_t config;
53562         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
53563         if (TIFFIsTiled(tif)) {
53564           uint32_t tw = 1, th = 1;
53565           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
53566           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
53567           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
53568             case 8 :
53569               if (sampleformat==SAMPLEFORMAT_UINT)
53570                 _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
53571               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
53572               break;
53573             case 16 :
53574               if (sampleformat==SAMPLEFORMAT_UINT)
53575                 _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
53576               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
53577               break;
53578             case 32 :
53579               if (sampleformat==SAMPLEFORMAT_UINT)
53580                 _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
53581               else if (sampleformat==SAMPLEFORMAT_INT)
53582                 _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
53583               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
53584               break;
53585             case 64 :
53586               if (sampleformat==SAMPLEFORMAT_UINT)
53587                 _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
53588               else if (sampleformat==SAMPLEFORMAT_INT)
53589                 _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
53590               else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
53591               break;
53592             } else switch (bitspersample) {
53593             case 8 :
53594               if (sampleformat==SAMPLEFORMAT_UINT)
53595                 _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
53596               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
53597               break;
53598             case 16 :
53599               if (sampleformat==SAMPLEFORMAT_UINT)
53600                 _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
53601               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
53602               break;
53603             case 32 :
53604               if (sampleformat==SAMPLEFORMAT_UINT)
53605                 _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
53606               else if (sampleformat==SAMPLEFORMAT_INT)
53607                 _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
53608               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
53609               break;
53610             case 64 :
53611               if (sampleformat==SAMPLEFORMAT_UINT)
53612                 _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
53613               else if (sampleformat==SAMPLEFORMAT_INT)
53614                 _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
53615               else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
53616               break;
53617             }
53618         } else {
53619           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
53620             case 8 :
53621               if (sampleformat==SAMPLEFORMAT_UINT)
53622                 _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
53623               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
53624               break;
53625             case 16 :
53626               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
53627               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
53628               break;
53629             case 32 :
53630               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
53631               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
53632               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
53633               break;
53634             case 64 :
53635               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
53636               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
53637               else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
53638               break;
53639             } else switch (bitspersample) {
53640             case 8 :
53641               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
53642               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
53643               break;
53644             case 16 :
53645               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
53646               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
53647               break;
53648             case 32 :
53649               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
53650               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
53651               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
53652               break;
53653             case 64 :
53654               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
53655               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
53656               else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
53657               break;
53658             }
53659         }
53660       }
53661       return *this;
53662     }
53663 #endif
53664 
53665     //! Load image from a MINC2 file.
53666     /**
53667         \param filename Filename, as a C-string.
53668     **/
53669     // (Original code by Haz-Edine Assemlal).
53670     CImg<T>& load_minc2(const char *const filename) {
53671       if (!filename)
53672         throw CImgArgumentException(_cimg_instance
53673                                     "load_minc2(): Specified filename is (null).",
53674                                     cimg_instance);
53675 #ifndef cimg_use_minc2
53676       return load_other(filename);
53677 #else
53678       minc::minc_1_reader rdr;
53679       rdr.open(filename);
53680       assign(rdr.ndim(1)?rdr.ndim(1):1,
53681              rdr.ndim(2)?rdr.ndim(2):1,
53682              rdr.ndim(3)?rdr.ndim(3):1,
53683              rdr.ndim(4)?rdr.ndim(4):1);
53684       if (pixel_type()==cimg::type<unsigned char>::string())
53685         rdr.setup_read_byte();
53686       else if (pixel_type()==cimg::type<int>::string())
53687         rdr.setup_read_int();
53688       else if (pixel_type()==cimg::type<double>::string())
53689         rdr.setup_read_double();
53690       else
53691         rdr.setup_read_float();
53692       minc::load_standard_volume(rdr,this->_data);
53693       return *this;
53694 #endif
53695     }
53696 
53697     //! Load image from a MINC2 file \newinstance.
53698     static CImg<T> get_load_minc2(const char *const filename) {
53699       return CImg<T>().load_analyze(filename);
53700     }
53701 
53702     //! Load image from an ANALYZE7.5/NIFTI file.
53703     /**
53704        \param filename Filename, as a C-string.
53705        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
53706     **/
53707     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
53708       return _load_analyze(0,filename,voxel_size);
53709     }
53710 
53711     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
53712     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
53713       return CImg<T>().load_analyze(filename,voxel_size);
53714     }
53715 
53716     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
53717     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
53718       return _load_analyze(file,0,voxel_size);
53719     }
53720 
53721     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
53722     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
53723       return CImg<T>().load_analyze(file,voxel_size);
53724     }
53725 
53726     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
53727       if (!file && !filename)
53728         throw CImgArgumentException(_cimg_instance
53729                                     "load_analyze(): Specified filename is (null).",
53730                                     cimg_instance);
53731 
53732       std::FILE *nfile_header = 0, *nfile = 0;
53733       if (!file) {
53734         CImg<charT> body(1024);
53735         const char *const ext = cimg::split_filename(filename,body);
53736         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file
53737           nfile_header = cimg::fopen(filename,"rb");
53738           cimg_sprintf(body._data + std::strlen(body),".img");
53739           nfile = cimg::fopen(body,"rb");
53740         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file
53741           nfile = cimg::fopen(filename,"rb");
53742           cimg_sprintf(body._data + std::strlen(body),".hdr");
53743           nfile_header = cimg::fopen(body,"rb");
53744         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file
53745       } else nfile_header = nfile = file; // File is a Niftii file
53746       if (!nfile || !nfile_header)
53747         throw CImgIOException(_cimg_instance
53748                               "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
53749                               cimg_instance,
53750                               filename?filename:"(FILE*)");
53751 
53752       // Read header.
53753       bool endian = false;
53754       unsigned int header_size;
53755       cimg::fread(&header_size,1,nfile_header);
53756       if (!header_size)
53757         throw CImgIOException(_cimg_instance
53758                               "load_analyze(): Invalid zero-size header in file '%s'.",
53759                               cimg_instance,
53760                               filename?filename:"(FILE*)");
53761       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
53762 
53763       unsigned char *const header = new unsigned char[header_size];
53764       cimg::fread(header + 4,header_size - 4,nfile_header);
53765       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
53766       if (endian) {
53767         cimg::invert_endianness((short*)(header + 40),5);
53768         cimg::invert_endianness((short*)(header + 70),1);
53769         cimg::invert_endianness((short*)(header + 72),1);
53770         cimg::invert_endianness((float*)(header + 76),4);
53771         cimg::invert_endianness((float*)(header + 108),1);
53772         cimg::invert_endianness((float*)(header + 112),1);
53773       }
53774 
53775       if (nfile_header==nfile) {
53776         const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
53777         std::fseek(nfile,vox_offset,SEEK_SET);
53778       }
53779 
53780       unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
53781       if (!dim[0])
53782         cimg::warn(_cimg_instance
53783                    "load_analyze(): File '%s' defines an image with zero dimensions.",
53784                    cimg_instance,
53785                    filename?filename:"(FILE*)");
53786 
53787       if (dim[0]>4)
53788         cimg::warn(_cimg_instance
53789                    "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
53790                    cimg_instance,
53791                    filename?filename:"(FILE*)",dim[0]);
53792 
53793       if (dim[0]>=1) dimx = dim[1];
53794       if (dim[0]>=2) dimy = dim[2];
53795       if (dim[0]>=3) dimz = dim[3];
53796       if (dim[0]>=4) dimv = dim[4];
53797       float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
53798       const unsigned short datatype = *(unsigned short*)(header + 70);
53799       if (voxel_size) {
53800         const float *vsize = (float*)(header + 76);
53801         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
53802       }
53803       delete[] header;
53804 
53805       // Read pixel data.
53806       assign(dimx,dimy,dimz,dimv);
53807       const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
53808       switch (datatype) {
53809       case 2 : {
53810         unsigned char *const buffer = new unsigned char[pdim];
53811         cimg::fread(buffer,pdim,nfile);
53812         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53813         delete[] buffer;
53814       } break;
53815       case 4 : {
53816         short *const buffer = new short[pdim];
53817         cimg::fread(buffer,pdim,nfile);
53818         if (endian) cimg::invert_endianness(buffer,pdim);
53819         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53820         delete[] buffer;
53821       } break;
53822       case 8 : {
53823         int *const buffer = new int[pdim];
53824         cimg::fread(buffer,pdim,nfile);
53825         if (endian) cimg::invert_endianness(buffer,pdim);
53826         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53827         delete[] buffer;
53828       } break;
53829       case 16 : {
53830         float *const buffer = new float[pdim];
53831         cimg::fread(buffer,pdim,nfile);
53832         if (endian) cimg::invert_endianness(buffer,pdim);
53833         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53834         delete[] buffer;
53835       } break;
53836       case 64 : {
53837         double *const buffer = new double[pdim];
53838         cimg::fread(buffer,pdim,nfile);
53839         if (endian) cimg::invert_endianness(buffer,pdim);
53840         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53841         delete[] buffer;
53842       } break;
53843       default :
53844         if (!file) cimg::fclose(nfile);
53845         throw CImgIOException(_cimg_instance
53846                               "load_analyze(): Unable to load datatype %d in file '%s'",
53847                               cimg_instance,
53848                               datatype,filename?filename:"(FILE*)");
53849       }
53850       if (!file) cimg::fclose(nfile);
53851       return *this;
53852     }
53853 
53854     //! Load image from a .cimg[z] file.
53855     /**
53856       \param filename Filename, as a C-string.
53857       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53858       \param align Appending alignment.
53859     **/
53860     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
53861       CImgList<T> list;
53862       list.load_cimg(filename);
53863       if (list._width==1) return list[0].move_to(*this);
53864       return assign(list.get_append(axis,align));
53865     }
53866 
53867     //! Load image from a .cimg[z] file \newinstance
53868     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
53869       return CImg<T>().load_cimg(filename,axis,align);
53870     }
53871 
53872     //! Load image from a .cimg[z] file \overloading.
53873     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
53874       CImgList<T> list;
53875       list.load_cimg(file);
53876       if (list._width==1) return list[0].move_to(*this);
53877       return assign(list.get_append(axis,align));
53878     }
53879 
53880     //! Load image from a .cimg[z] file \newinstance
53881     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
53882       return CImg<T>().load_cimg(file,axis,align);
53883     }
53884 
53885     //! Load sub-images of a .cimg file.
53886     /**
53887       \param filename Filename, as a C-string.
53888       \param n0 Starting frame.
53889       \param n1 Ending frame (~0U for max).
53890       \param x0 X-coordinate of the starting sub-image vertex.
53891       \param y0 Y-coordinate of the starting sub-image vertex.
53892       \param z0 Z-coordinate of the starting sub-image vertex.
53893       \param c0 C-coordinate of the starting sub-image vertex.
53894       \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
53895       \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
53896       \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
53897       \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
53898       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53899       \param align Appending alignment.
53900     **/
53901     CImg<T>& load_cimg(const char *const filename,
53902                        const unsigned int n0, const unsigned int n1,
53903                        const unsigned int x0, const unsigned int y0,
53904                        const unsigned int z0, const unsigned int c0,
53905                        const unsigned int x1, const unsigned int y1,
53906                        const unsigned int z1, const unsigned int c1,
53907                        const char axis='z', const float align=0) {
53908       CImgList<T> list;
53909       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
53910       if (list._width==1) return list[0].move_to(*this);
53911       return assign(list.get_append(axis,align));
53912     }
53913 
53914     //! Load sub-images of a .cimg file \newinstance.
53915     static CImg<T> get_load_cimg(const char *const filename,
53916                                  const unsigned int n0, const unsigned int n1,
53917                                  const unsigned int x0, const unsigned int y0,
53918                                  const unsigned int z0, const unsigned int c0,
53919                                  const unsigned int x1, const unsigned int y1,
53920                                  const unsigned int z1, const unsigned int c1,
53921                                  const char axis='z', const float align=0) {
53922       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
53923     }
53924 
53925     //! Load sub-images of a .cimg file \overloading.
53926     CImg<T>& load_cimg(std::FILE *const file,
53927                        const unsigned int n0, const unsigned int n1,
53928                        const unsigned int x0, const unsigned int y0,
53929                        const unsigned int z0, const unsigned int c0,
53930                        const unsigned int x1, const unsigned int y1,
53931                        const unsigned int z1, const unsigned int c1,
53932                        const char axis='z', const float align=0) {
53933       CImgList<T> list;
53934       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
53935       if (list._width==1) return list[0].move_to(*this);
53936       return assign(list.get_append(axis,align));
53937     }
53938 
53939     //! Load sub-images of a .cimg file \newinstance.
53940     static CImg<T> get_load_cimg(std::FILE *const file,
53941                                  const unsigned int n0, const unsigned int n1,
53942                                  const unsigned int x0, const unsigned int y0,
53943                                  const unsigned int z0, const unsigned int c0,
53944                                  const unsigned int x1, const unsigned int y1,
53945                                  const unsigned int z1, const unsigned int c1,
53946                                  const char axis='z', const float align=0) {
53947       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
53948     }
53949 
53950     //! Load image from an INRIMAGE-4 file.
53951     /**
53952        \param filename Filename, as a C-string.
53953        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
53954     **/
53955     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
53956       return _load_inr(0,filename,voxel_size);
53957     }
53958 
53959     //! Load image from an INRIMAGE-4 file \newinstance.
53960     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
53961       return CImg<T>().load_inr(filename,voxel_size);
53962     }
53963 
53964     //! Load image from an INRIMAGE-4 file \overloading.
53965     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
53966       return _load_inr(file,0,voxel_size);
53967     }
53968 
53969     //! Load image from an INRIMAGE-4 file \newinstance.
53970     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
53971       return CImg<T>().load_inr(file,voxel_size);
53972     }
53973 
53974     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
53975       CImg<charT> item(1024), tmp1(64), tmp2(64);
53976       *item = *tmp1 = *tmp2 = 0;
53977       out[0] = std::fscanf(file,"%63s",item._data);
53978       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
53979       if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
53980         throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
53981                               pixel_type());
53982 
53983       while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
53984         cimg_sscanf(item," XDIM%*[^0-9]%d",out);
53985         cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
53986         cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
53987         cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
53988         cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
53989         if (voxel_size) {
53990           cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
53991           cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
53992           cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
53993         }
53994         if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
53995         switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
53996         case 0 : break;
53997         case 2 :
53998           out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
53999           std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
54000         case 1 :
54001           if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
54002           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
54003           if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
54004           if (out[4]>=0) break; // fallthrough
54005         default :
54006           throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
54007                                 pixel_type(),
54008                                 tmp2._data);
54009         }
54010       }
54011       if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
54012         throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
54013                               pixel_type(),
54014                               out[0],out[1],out[2],out[3]);
54015       if (out[4]<0 || out[5]<0)
54016         throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
54017                               pixel_type());
54018       if (out[6]<0)
54019         throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
54020                               pixel_type());
54021       if (out[7]<0)
54022         throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
54023                               pixel_type());
54024     }
54025 
54026     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
54027 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
54028      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
54029         Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
54030         cimg_forYZ(*this,y,z) { \
54031             cimg::fread(val,fopt[0]*fopt[3],nfile); \
54032             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
54033             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
54034           } \
54035         delete[] val; \
54036         loaded = true; \
54037       }
54038 
54039       if (!file && !filename)
54040         throw CImgArgumentException(_cimg_instance
54041                                     "load_inr(): Specified filename is (null).",
54042                                     cimg_instance);
54043 
54044       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
54045       int fopt[8], endian = cimg::endianness()?1:0;
54046       bool loaded = false;
54047       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
54048       _load_inr_header(nfile,fopt,voxel_size);
54049       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
54050       _cimg_load_inr_case(0,0,8,unsigned char);
54051       _cimg_load_inr_case(0,1,8,char);
54052       _cimg_load_inr_case(0,0,16,unsigned short);
54053       _cimg_load_inr_case(0,1,16,short);
54054       _cimg_load_inr_case(0,0,32,unsigned int);
54055       _cimg_load_inr_case(0,1,32,int);
54056       _cimg_load_inr_case(1,0,32,float);
54057       _cimg_load_inr_case(1,1,32,float);
54058       _cimg_load_inr_case(1,0,64,double);
54059       _cimg_load_inr_case(1,1,64,double);
54060       if (!loaded) {
54061         if (!file) cimg::fclose(nfile);
54062         throw CImgIOException(_cimg_instance
54063                               "load_inr(): Unknown pixel type defined in file '%s'.",
54064                               cimg_instance,
54065                               filename?filename:"(FILE*)");
54066       }
54067       if (!file) cimg::fclose(nfile);
54068       return *this;
54069     }
54070 
54071     //! Load image from a EXR file.
54072     /**
54073       \param filename Filename, as a C-string.
54074     **/
54075     CImg<T>& load_exr(const char *const filename) {
54076       if (!filename)
54077         throw CImgArgumentException(_cimg_instance
54078                                     "load_exr(): Specified filename is (null).",
54079                                     cimg_instance);
54080 #if defined(cimg_use_openexr)
54081       Imf::RgbaInputFile file(filename);
54082       Imath::Box2i dw = file.dataWindow();
54083       const int
54084         inwidth = dw.max.x - dw.min.x + 1,
54085         inheight = dw.max.y - dw.min.y + 1;
54086       Imf::Array2D<Imf::Rgba> pixels;
54087       pixels.resizeErase(inheight,inwidth);
54088       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
54089       file.readPixels(dw.min.y, dw.max.y);
54090       assign(inwidth,inheight,1,4);
54091       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);
54092       cimg_forXY(*this,x,y) {
54093         *(ptr_r++) = (T)pixels[y][x].r;
54094         *(ptr_g++) = (T)pixels[y][x].g;
54095         *(ptr_b++) = (T)pixels[y][x].b;
54096         *(ptr_a++) = (T)pixels[y][x].a;
54097       }
54098       return *this;
54099 #elif defined(cimg_use_tinyexr)
54100       float *res;
54101       const char *err = 0;
54102       int width = 0, height = 0;
54103       const int ret = LoadEXR(&res,&width,&height,filename,&err);
54104       if (ret) throw CImgIOException(_cimg_instance
54105                                      "load_exr(): Unable to load EXR file '%s'.",
54106                                      cimg_instance,filename);
54107       CImg<floatT>(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
54108       std::free(res);
54109       return *this;
54110 #else
54111       return load_other(filename);
54112 #endif
54113     }
54114 
54115     //! Load image from a EXR file \newinstance.
54116     static CImg<T> get_load_exr(const char *const filename) {
54117       return CImg<T>().load_exr(filename);
54118     }
54119 
54120     //! Load image from a PANDORE-5 file.
54121     /**
54122       \param filename Filename, as a C-string.
54123     **/
54124     CImg<T>& load_pandore(const char *const filename) {
54125       return _load_pandore(0,filename);
54126     }
54127 
54128     //! Load image from a PANDORE-5 file \newinstance.
54129     static CImg<T> get_load_pandore(const char *const filename) {
54130       return CImg<T>().load_pandore(filename);
54131     }
54132 
54133     //! Load image from a PANDORE-5 file \overloading.
54134     CImg<T>& load_pandore(std::FILE *const file) {
54135       return _load_pandore(file,0);
54136     }
54137 
54138     //! Load image from a PANDORE-5 file \newinstance.
54139     static CImg<T> get_load_pandore(std::FILE *const file) {
54140       return CImg<T>().load_pandore(file);
54141     }
54142 
54143     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
54144 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
54145         cimg::fread(dims,nbdim,nfile); \
54146         if (endian) cimg::invert_endianness(dims,nbdim); \
54147         assign(nwidth,nheight,ndepth,ndim); \
54148         const size_t siz = size(); \
54149         stype *buffer = new stype[siz]; \
54150         cimg::fread(buffer,siz,nfile); \
54151         if (endian) cimg::invert_endianness(buffer,siz); \
54152         T *ptrd = _data; \
54153         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
54154         buffer-=siz; \
54155         delete[] buffer
54156 
54157 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
54158         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
54159         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
54160         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
54161         else throw CImgIOException(_cimg_instance \
54162                                    "load_pandore(): Unknown pixel datatype in file '%s'.", \
54163                                    cimg_instance, \
54164                                    filename?filename:"(FILE*)"); }
54165       if (!file && !filename)
54166         throw CImgArgumentException(_cimg_instance
54167                                     "load_pandore(): Specified filename is (null).",
54168                                     cimg_instance);
54169 
54170       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
54171       CImg<charT> header(32);
54172       cimg::fread(header._data,12,nfile);
54173       if (cimg::strncasecmp("PANDORE",header,7)) {
54174         if (!file) cimg::fclose(nfile);
54175         throw CImgIOException(_cimg_instance
54176                               "load_pandore(): PANDORE header not found in file '%s'.",
54177                               cimg_instance,
54178                               filename?filename:"(FILE*)");
54179       }
54180       unsigned int imageid, dims[8] = { 0 };
54181       int ptbuf[4] = { 0 };
54182       cimg::fread(&imageid,1,nfile);
54183       const bool endian = imageid>255;
54184       if (endian) cimg::invert_endianness(imageid);
54185       cimg::fread(header._data,20,nfile);
54186 
54187       switch (imageid) {
54188       case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
54189       case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
54190       case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
54191       case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
54192       case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
54193       case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
54194       case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
54195       case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
54196       case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
54197       case 11 : { // Region 1D
54198         cimg::fread(dims,3,nfile);
54199         if (endian) cimg::invert_endianness(dims,3);
54200         assign(dims[1],1,1,1);
54201         const unsigned siz = size();
54202         if (dims[2]<256) {
54203           unsigned char *buffer = new unsigned char[siz];
54204           cimg::fread(buffer,siz,nfile);
54205           T *ptrd = _data;
54206           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54207           buffer-=siz;
54208           delete[] buffer;
54209         } else {
54210           if (dims[2]<65536) {
54211             unsigned short *buffer = new unsigned short[siz];
54212             cimg::fread(buffer,siz,nfile);
54213             if (endian) cimg::invert_endianness(buffer,siz);
54214             T *ptrd = _data;
54215             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54216             buffer-=siz;
54217             delete[] buffer;
54218           } else {
54219             unsigned int *buffer = new unsigned int[siz];
54220             cimg::fread(buffer,siz,nfile);
54221             if (endian) cimg::invert_endianness(buffer,siz);
54222             T *ptrd = _data;
54223             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54224             buffer-=siz;
54225             delete[] buffer;
54226           }
54227         }
54228       }
54229         break;
54230       case 12 : { // Region 2D
54231         cimg::fread(dims,4,nfile);
54232         if (endian) cimg::invert_endianness(dims,4);
54233         assign(dims[2],dims[1],1,1);
54234         const size_t siz = size();
54235         if (dims[3]<256) {
54236           unsigned char *buffer = new unsigned char[siz];
54237           cimg::fread(buffer,siz,nfile);
54238           T *ptrd = _data;
54239           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54240           buffer-=siz;
54241           delete[] buffer;
54242         } else {
54243           if (dims[3]<65536) {
54244             unsigned short *buffer = new unsigned short[siz];
54245             cimg::fread(buffer,siz,nfile);
54246             if (endian) cimg::invert_endianness(buffer,siz);
54247             T *ptrd = _data;
54248             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54249             buffer-=siz;
54250             delete[] buffer;
54251           } else {
54252             unsigned int *buffer = new unsigned int[siz];
54253             cimg::fread(buffer,siz,nfile);
54254             if (endian) cimg::invert_endianness(buffer,siz);
54255             T *ptrd = _data;
54256             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54257             buffer-=siz;
54258             delete[] buffer;
54259           }
54260         }
54261       }
54262         break;
54263       case 13 : { // Region 3D
54264         cimg::fread(dims,5,nfile);
54265         if (endian) cimg::invert_endianness(dims,5);
54266         assign(dims[3],dims[2],dims[1],1);
54267         const size_t siz = size();
54268         if (dims[4]<256) {
54269           unsigned char *buffer = new unsigned char[siz];
54270           cimg::fread(buffer,siz,nfile);
54271           T *ptrd = _data;
54272           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54273           buffer-=siz;
54274           delete[] buffer;
54275         } else {
54276           if (dims[4]<65536) {
54277             unsigned short *buffer = new unsigned short[siz];
54278             cimg::fread(buffer,siz,nfile);
54279             if (endian) cimg::invert_endianness(buffer,siz);
54280             T *ptrd = _data;
54281             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54282             buffer-=siz;
54283             delete[] buffer;
54284           } else {
54285             unsigned int *buffer = new unsigned int[siz];
54286             cimg::fread(buffer,siz,nfile);
54287             if (endian) cimg::invert_endianness(buffer,siz);
54288             T *ptrd = _data;
54289             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54290             buffer-=siz;
54291             delete[] buffer;
54292           }
54293         }
54294       }
54295         break;
54296       case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
54297       case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
54298       case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
54299       case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
54300       case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
54301       case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
54302       case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
54303       case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
54304       case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
54305       case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
54306       case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
54307       case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
54308       case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
54309       case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
54310       case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
54311         break;
54312       case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
54313       case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
54314         break;
54315       case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
54316       case 34 : { // Points 1D
54317         cimg::fread(ptbuf,1,nfile);
54318         if (endian) cimg::invert_endianness(ptbuf,1);
54319         assign(1); (*this)(0) = (T)ptbuf[0];
54320       } break;
54321       case 35 : { // Points 2D
54322         cimg::fread(ptbuf,2,nfile);
54323         if (endian) cimg::invert_endianness(ptbuf,2);
54324         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
54325       } break;
54326       case 36 : { // Points 3D
54327         cimg::fread(ptbuf,3,nfile);
54328         if (endian) cimg::invert_endianness(ptbuf,3);
54329         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
54330       } break;
54331       default :
54332         if (!file) cimg::fclose(nfile);
54333         throw CImgIOException(_cimg_instance
54334                               "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
54335                               cimg_instance,
54336                               imageid,filename?filename:"(FILE*)");
54337       }
54338       if (!file) cimg::fclose(nfile);
54339       return *this;
54340     }
54341 
54342     //! Load image from a PAR-REC (Philips) file.
54343     /**
54344       \param filename Filename, as a C-string.
54345       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54346       \param align Appending alignment.
54347     **/
54348     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
54349       CImgList<T> list;
54350       list.load_parrec(filename);
54351       if (list._width==1) return list[0].move_to(*this);
54352       return assign(list.get_append(axis,align));
54353     }
54354 
54355     //! Load image from a PAR-REC (Philips) file \newinstance.
54356     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
54357       return CImg<T>().load_parrec(filename,axis,align);
54358     }
54359 
54360     //! Load image from a raw binary file.
54361     /**
54362       \param filename Filename, as a C-string.
54363       \param size_x Width of the image buffer.
54364       \param size_y Height of the image buffer.
54365       \param size_z Depth of the image buffer.
54366       \param size_c Spectrum of the image buffer.
54367       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
54368       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
54369       \param offset Starting offset of the read in the specified file.
54370     **/
54371     CImg<T>& load_raw(const char *const filename,
54372                       const unsigned int size_x=0, const unsigned int size_y=1,
54373                       const unsigned int size_z=1, const unsigned int size_c=1,
54374                       const bool is_multiplexed=false, const bool invert_endianness=false,
54375                       const ulongT offset=0) {
54376       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54377     }
54378 
54379     //! Load image from a raw binary file \newinstance.
54380     static CImg<T> get_load_raw(const char *const filename,
54381                                 const unsigned int size_x=0, const unsigned int size_y=1,
54382                                 const unsigned int size_z=1, const unsigned int size_c=1,
54383                                 const bool is_multiplexed=false, const bool invert_endianness=false,
54384                                 const ulongT offset=0) {
54385       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54386     }
54387 
54388     //! Load image from a raw binary file \overloading.
54389     CImg<T>& load_raw(std::FILE *const file,
54390                       const unsigned int size_x=0, const unsigned int size_y=1,
54391                       const unsigned int size_z=1, const unsigned int size_c=1,
54392                       const bool is_multiplexed=false, const bool invert_endianness=false,
54393                       const ulongT offset=0) {
54394       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54395     }
54396 
54397     //! Load image from a raw binary file \newinstance.
54398     static CImg<T> get_load_raw(std::FILE *const file,
54399                                 const unsigned int size_x=0, const unsigned int size_y=1,
54400                                 const unsigned int size_z=1, const unsigned int size_c=1,
54401                                 const bool is_multiplexed=false, const bool invert_endianness=false,
54402                                 const ulongT offset=0) {
54403       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54404     }
54405 
54406     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
54407                        const unsigned int size_x, const unsigned int size_y,
54408                        const unsigned int size_z, const unsigned int size_c,
54409                        const bool is_multiplexed, const bool invert_endianness,
54410                        const ulongT offset) {
54411       if (!file && !filename)
54412         throw CImgArgumentException(_cimg_instance
54413                                     "load_raw(): Specified filename is (null).",
54414                                     cimg_instance);
54415       if (cimg::is_directory(filename))
54416         throw CImgArgumentException(_cimg_instance
54417                                     "load_raw(): Specified filename '%s' is a directory.",
54418                                     cimg_instance,filename);
54419       const bool is_bool = pixel_type()==cimg::type<bool>::string();
54420       ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
54421       unsigned int
54422         _size_x = size_x,
54423         _size_y = size_y,
54424         _size_z = size_z,
54425         _size_c = size_c;
54426       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
54427       if (!siz) {  // Retrieve file size
54428         const longT fpos = cimg::ftell(nfile);
54429         if (fpos<0) throw CImgArgumentException(_cimg_instance
54430                                                 "load_raw(): Cannot determine size of input file '%s'.",
54431                                                 cimg_instance,filename?filename:"(FILE*)");
54432         cimg::fseek(nfile,0,SEEK_END);
54433         siz = (ulongT)cimg::ftell(nfile);
54434         if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; }
54435         else _size_y = (unsigned int)(siz*8);
54436         _size_x = _size_z = _size_c = 1;
54437         cimg::fseek(nfile,fpos,SEEK_SET);
54438       }
54439       cimg::fseek(nfile,(longT)offset,SEEK_SET);
54440       assign(_size_x,_size_y,_size_z,_size_c,0);
54441 
54442       if (is_bool) { // Boolean data (bitwise)
54443         unsigned char *const buf = new unsigned char[siz];
54444         cimg::fread(buf,siz,nfile);
54445         _uchar2bool(buf,siz,is_multiplexed);
54446         delete[] buf;
54447       } else { // Non-boolean data
54448         if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed
54449           cimg::fread(_data,siz,nfile);
54450           if (invert_endianness) cimg::invert_endianness(_data,siz);
54451         } else if (siz) { // Multiplexed
54452           CImg<T> buf(1,1,1,_size_c);
54453           cimg_forXYZ(*this,x,y,z) {
54454             cimg::fread(buf._data,_size_c,nfile);
54455             if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
54456             set_vector_at(buf,x,y,z);
54457           }
54458         }
54459       }
54460       if (!file) cimg::fclose(nfile);
54461       return *this;
54462     }
54463 
54464     //! Load image sequence from a YUV file.
54465     /**
54466       \param filename Filename, as a C-string.
54467       \param size_x Width of the frames.
54468       \param size_y Height of the frames.
54469       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
54470       \param first_frame Index of the first frame to read.
54471       \param last_frame Index of the last frame to read.
54472       \param step_frame Step value for frame reading.
54473       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
54474       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54475     **/
54476     CImg<T>& load_yuv(const char *const filename,
54477                       const unsigned int size_x, const unsigned int size_y=1,
54478                       const unsigned int chroma_subsampling=444,
54479                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54480                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
54481       return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
54482                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
54483     }
54484 
54485     //! Load image sequence from a YUV file \newinstance.
54486     static CImg<T> get_load_yuv(const char *const filename,
54487                                 const unsigned int size_x, const unsigned int size_y=1,
54488                                 const unsigned int chroma_subsampling=444,
54489                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54490                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
54491       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
54492                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
54493     }
54494 
54495     //! Load image sequence from a YUV file \overloading.
54496     CImg<T>& load_yuv(std::FILE *const file,
54497                       const unsigned int size_x, const unsigned int size_y=1,
54498                       const unsigned int chroma_subsampling=444,
54499                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54500                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
54501       return get_load_yuv(file,size_x,size_y,chroma_subsampling,
54502                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
54503     }
54504 
54505     //! Load image sequence from a YUV file \newinstance.
54506     static CImg<T> get_load_yuv(std::FILE *const file,
54507                                 const unsigned int size_x, const unsigned int size_y=1,
54508                                 const unsigned int chroma_subsampling=444,
54509                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54510                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
54511       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
54512                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
54513     }
54514 
54515     //! Load 3D object from a .OFF file.
54516     /**
54517         \param[out] primitives Primitives data of the 3D object.
54518         \param[out] colors Colors data of the 3D object.
54519         \param filename Filename, as a C-string.
54520     **/
54521     template<typename tf, typename tc>
54522     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
54523       return _load_off(primitives,colors,0,filename);
54524     }
54525 
54526     //! Load 3D object from a .OFF file \newinstance.
54527     template<typename tf, typename tc>
54528     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
54529       return CImg<T>().load_off(primitives,colors,filename);
54530     }
54531 
54532     //! Load 3D object from a .OFF file \overloading.
54533     template<typename tf, typename tc>
54534     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
54535       return _load_off(primitives,colors,file,0);
54536     }
54537 
54538     //! Load 3D object from a .OFF file \newinstance.
54539     template<typename tf, typename tc>
54540     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
54541       return CImg<T>().load_off(primitives,colors,file);
54542     }
54543 
54544     template<typename tf, typename tc>
54545     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
54546                        std::FILE *const file, const char *const filename) {
54547       if (!file && !filename)
54548         throw CImgArgumentException(_cimg_instance
54549                                     "load_off(): Specified filename is (null).",
54550                                     cimg_instance);
54551 
54552       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
54553       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
54554       CImg<charT> line(256); *line = 0;
54555       int err;
54556 
54557       // Skip comments, and read magic string OFF
54558       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54559       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
54560         if (!file) cimg::fclose(nfile);
54561         throw CImgIOException(_cimg_instance
54562                               "load_off(): OFF header not found in file '%s'.",
54563                               cimg_instance,
54564                               filename?filename:"(FILE*)");
54565       }
54566       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54567       if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
54568         if (!file) cimg::fclose(nfile);
54569         throw CImgIOException(_cimg_instance
54570                               "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
54571                               cimg_instance,
54572                               filename?filename:"(FILE*)");
54573       }
54574 
54575       // Read points data
54576       assign(nb_points,3);
54577       float X = 0, Y = 0, Z = 0;
54578       cimg_forX(*this,l) {
54579         do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54580         if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
54581           if (!file) cimg::fclose(nfile);
54582           throw CImgIOException(_cimg_instance
54583                                 "load_off(): Failed to read vertex %u/%u in file '%s'.",
54584                                 cimg_instance,
54585                                 l + 1,nb_points,filename?filename:"(FILE*)");
54586         }
54587         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
54588       }
54589 
54590       // Read primitive data
54591       primitives.assign();
54592       colors.assign();
54593       bool stop_flag = false;
54594       while (!stop_flag) {
54595         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
54596         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
54597         *line = 0;
54598         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
54599         else {
54600           ++nb_read;
54601           switch (prim) {
54602           case 1 : {
54603             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
54604               cimg::warn(_cimg_instance
54605                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54606                          cimg_instance,
54607                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54608 
54609               err = std::fscanf(nfile,"%*[^\n] ");
54610             } else {
54611               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54612               CImg<tf>::vector(i0).move_to(primitives);
54613               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54614             }
54615           } break;
54616           case 2 : {
54617             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
54618               cimg::warn(_cimg_instance
54619                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54620                          cimg_instance,
54621                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54622 
54623               err = std::fscanf(nfile,"%*[^\n] ");
54624             } else {
54625               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54626               CImg<tf>::vector(i0,i1).move_to(primitives);
54627               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54628             }
54629           } break;
54630           case 3 : {
54631             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
54632               cimg::warn(_cimg_instance
54633                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54634                          cimg_instance,
54635                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54636 
54637               err = std::fscanf(nfile,"%*[^\n] ");
54638             } else {
54639               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54640               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
54641               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54642             }
54643           } break;
54644           case 4 : {
54645             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
54646               cimg::warn(_cimg_instance
54647                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54648                          cimg_instance,
54649                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54650 
54651               err = std::fscanf(nfile,"%*[^\n] ");
54652             } else {
54653               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54654               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54655               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54656             }
54657           } break;
54658           case 5 : {
54659             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
54660               cimg::warn(_cimg_instance
54661                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54662                          cimg_instance,
54663                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54664 
54665               err = std::fscanf(nfile,"%*[^\n] ");
54666             } else {
54667               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54668               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54669               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
54670               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54671               ++nb_primitives;
54672             }
54673           } break;
54674           case 6 : {
54675             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
54676               cimg::warn(_cimg_instance
54677                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54678                          cimg_instance,
54679                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54680 
54681               err = std::fscanf(nfile,"%*[^\n] ");
54682             } else {
54683               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54684               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54685               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
54686               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54687               ++nb_primitives;
54688             }
54689           } break;
54690           case 7 : {
54691             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
54692               cimg::warn(_cimg_instance
54693                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54694                          cimg_instance,
54695                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54696 
54697               err = std::fscanf(nfile,"%*[^\n] ");
54698             } else {
54699               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54700               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
54701               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
54702               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
54703               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54704               ++(++nb_primitives);
54705             }
54706           } break;
54707           case 8 : {
54708             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) {
54709               cimg::warn(_cimg_instance
54710                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54711                          cimg_instance,
54712                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54713 
54714               err = std::fscanf(nfile,"%*[^\n] ");
54715             } else {
54716               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54717               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54718               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
54719               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
54720               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54721               ++(++nb_primitives);
54722             }
54723           } break;
54724           default :
54725             cimg::warn(_cimg_instance
54726                        "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
54727                        cimg_instance,
54728                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
54729 
54730             err = std::fscanf(nfile,"%*[^\n] ");
54731           }
54732         }
54733       }
54734       if (!file) cimg::fclose(nfile);
54735       if (primitives._width!=nb_primitives)
54736         cimg::warn(_cimg_instance
54737                    "load_off(): Only %u/%u primitives read from file '%s'.",
54738                    cimg_instance,
54739                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
54740       return *this;
54741     }
54742 
54743     //! Load image sequence from a video file, using OpenCV library.
54744     /**
54745       \param filename Filename, as a C-string.
54746       \param first_frame Index of the first frame to read.
54747       \param last_frame Index of the last frame to read.
54748       \param step_frame Step value for frame reading.
54749       \param axis Alignment axis.
54750       \param align Appending alignment.
54751     **/
54752     CImg<T>& load_video(const char *const filename,
54753                         const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54754                         const unsigned int step_frame=1,
54755                         const char axis='z', const float align=0) {
54756       return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
54757     }
54758 
54759     //! Load image sequence from a video file, using OpenCV library \newinstance.
54760     static CImg<T> get_load_video(const char *const filename,
54761                                   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54762                                   const unsigned int step_frame=1,
54763                                   const char axis='z', const float align=0) {
54764       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
54765     }
54766 
54767     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
54768     /**
54769       \param filename Filename, as a C-string.
54770       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54771       \param align Appending alignment.
54772     **/
54773     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
54774       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
54775     }
54776 
54777     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
54778     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
54779       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
54780     }
54781 
54782     //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
54783     /**
54784       \param filename Filename, as a C-string.
54785       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54786       \param align Appending alignment.
54787     **/
54788     CImg<T>& load_gif_external(const char *const filename,
54789                                const char axis='z', const float align=0) {
54790       return get_load_gif_external(filename,axis,align).move_to(*this);
54791     }
54792 
54793     //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
54794     static CImg<T> get_load_gif_external(const char *const filename,
54795                                          const char axis='z', const float align=0) {
54796       return CImgList<T>().load_gif_external(filename).get_append(axis,align);
54797     }
54798 
54799     //! Load image from a HEIC file.
54800     /**
54801        \param filename Filename, as a C-string.
54802     **/
54803     CImg<T>& load_heif(const char *const filename) {
54804       return _load_heif(filename);
54805     }
54806 
54807     //! Load image from a HEIC file \newinstance.
54808     static CImg<T> get_load_heif(const char *const filename) {
54809       return CImg<T>().load_heif(filename);
54810     }
54811 
54812     CImg<T>& _load_heif(const char *const filename) {
54813 #ifndef cimg_use_heif
54814       return load_other(filename);
54815 #else
54816       try {
54817         heif::Context ctx;
54818         ctx.read_from_file(filename);
54819 
54820         heif::ImageHandle handle = ctx.get_primary_image_handle();
54821         const heif::Image image =
54822           handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA:
54823                               heif_chroma_interleaved_RGB);
54824         const int
54825           W = image.get_width(heif_channel_interleaved),
54826           H = image.get_height(heif_channel_interleaved),
54827           S = handle.has_alpha_channel()?4:3;
54828         assign(W,H,1,S);
54829 
54830         int stride;
54831         const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride);
54832         T *ptr_r = _data, *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = S>3?data(0,0,0,3):0;
54833         cimg_forY(*this,y) {
54834           const unsigned char *ptrs = buffer + y*stride;
54835           if (ptr_a) cimg_forX(*this,x) { // RGBA
54836               *(ptr_r++) = (T)*(ptrs++);
54837               *(ptr_g++) = (T)*(ptrs++);
54838               *(ptr_b++) = (T)*(ptrs++);
54839               *(ptr_a++) = (T)*(ptrs++);
54840             }
54841           else cimg_forX(*this,x) { // RGB
54842               *(ptr_r++) = (T)*(ptrs++);
54843               *(ptr_g++) = (T)*(ptrs++);
54844               *(ptr_b++) = (T)*(ptrs++);
54845             }
54846         }
54847       } catch (const heif::Error& e) {
54848         throw CImgInstanceException(_cimg_instance
54849                                     "load_heif(): Unable to decode image: %s",
54850                                     cimg_instance,
54851                                     e.get_message().c_str());
54852       } catch (...) {
54853         throw;
54854       }
54855       return *this;
54856 #endif
54857     }
54858 
54859     //! Load image using GraphicsMagick's external tool 'gm'.
54860     /**
54861        \param filename Filename, as a C-string.
54862     **/
54863     CImg<T>& load_graphicsmagick_external(const char *const filename) {
54864       if (!filename)
54865         throw CImgArgumentException(_cimg_instance
54866                                     "load_graphicsmagick_external(): Specified filename is (null).",
54867                                     cimg_instance);
54868       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54869       CImg<charT> command(1024), filename_tmp(256);
54870       std::FILE *file = 0;
54871       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
54872 #if cimg_OS==1
54873       if (!cimg::system("which gm")) {
54874         cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-",
54875                       cimg::graphicsmagick_path(),
54876                       s_filename.data(),
54877 #ifdef cimg_use_png
54878                       "png"
54879 #else
54880                       "pnm"
54881 #endif
54882                       );
54883         file = popen(command,"r");
54884         if (file) {
54885           const unsigned int omode = cimg::exception_mode();
54886           cimg::exception_mode(0);
54887           try {
54888 #ifdef cimg_use_png
54889             load_png(file);
54890 #else
54891             load_pnm(file);
54892 #endif
54893           } catch (...) {
54894             pclose(file);
54895             cimg::exception_mode(omode);
54896             throw CImgIOException(_cimg_instance
54897                                   "load_graphicsmagick_external(): Failed to load file '%s' "
54898                                   "with external command 'gm'.",
54899                                   cimg_instance,
54900                                   filename);
54901           }
54902           pclose(file);
54903           return *this;
54904         }
54905       }
54906 #endif
54907       do {
54908         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54909                       cimg::temporary_path(),
54910                       cimg_file_separator,
54911                       cimg::filenamerand(),
54912 #ifdef cimg_use_png
54913                       "png"
54914 #else
54915                       "pnm"
54916 #endif
54917                       );
54918         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54919       } while (file);
54920       cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"",
54921                     cimg::graphicsmagick_path(),
54922                     s_filename.data(),
54923                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54924       cimg::system(command,cimg::graphicsmagick_path());
54925       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54926         cimg::fclose(cimg::fopen(filename,"r"));
54927         throw CImgIOException(_cimg_instance
54928                               "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
54929                               cimg_instance,
54930                               filename);
54931 
54932       } else cimg::fclose(file);
54933 #ifdef cimg_use_png
54934       load_png(filename_tmp);
54935 #else
54936       load_pnm(filename_tmp);
54937 #endif
54938       std::remove(filename_tmp);
54939       return *this;
54940     }
54941 
54942     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
54943     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
54944       return CImg<T>().load_graphicsmagick_external(filename);
54945     }
54946 
54947     //! Load gzipped image file, using external tool 'gunzip'.
54948     /**
54949        \param filename Filename, as a C-string.
54950     **/
54951     CImg<T>& load_gzip_external(const char *const filename) {
54952       if (!filename)
54953         throw CImgIOException(_cimg_instance
54954                               "load_gzip_external(): Specified filename is (null).",
54955                               cimg_instance);
54956       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54957       CImg<charT> command(1024), filename_tmp(256), body(256);
54958       const char
54959         *const ext = cimg::split_filename(filename,body),
54960         *const ext2 = cimg::split_filename(body,0);
54961 
54962       std::FILE *file = 0;
54963       do {
54964         if (!cimg::strcasecmp(ext,"gz")) {
54965           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54966                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
54967           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
54968                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54969         } else {
54970           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54971                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
54972           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
54973                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54974         }
54975         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54976       } while (file);
54977       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
54978                     cimg::gunzip_path(),
54979                     CImg<charT>::string(filename)._system_strescape().data(),
54980                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54981       cimg::system(command);
54982       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54983         cimg::fclose(cimg::fopen(filename,"r"));
54984         throw CImgIOException(_cimg_instance
54985                               "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
54986                               cimg_instance,
54987                               filename);
54988 
54989       } else cimg::fclose(file);
54990       load(filename_tmp);
54991       std::remove(filename_tmp);
54992       return *this;
54993     }
54994 
54995     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
54996     static CImg<T> get_load_gzip_external(const char *const filename) {
54997       return CImg<T>().load_gzip_external(filename);
54998     }
54999 
55000     //! Load image using ImageMagick's external tool 'convert'.
55001     /**
55002        \param filename Filename, as a C-string.
55003     **/
55004     CImg<T>& load_imagemagick_external(const char *const filename) {
55005       if (!filename)
55006         throw CImgArgumentException(_cimg_instance
55007                                     "load_imagemagick_external(): Specified filename is (null).",
55008                                     cimg_instance);
55009       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55010       CImg<charT> command(1024), filename_tmp(256);
55011       std::FILE *file = 0;
55012       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55013 #if cimg_OS==1
55014       if (!cimg::system("which convert")) {
55015         cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-",
55016                       cimg::imagemagick_path(),
55017                       !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
55018                       s_filename.data(),
55019 #ifdef cimg_use_png
55020                       "png"
55021 #else
55022                       "pnm"
55023 #endif
55024                       );
55025         file = popen(command,"r");
55026         if (file) {
55027           const unsigned int omode = cimg::exception_mode();
55028           cimg::exception_mode(0);
55029           try {
55030 #ifdef cimg_use_png
55031             load_png(file);
55032 #else
55033             load_pnm(file);
55034 #endif
55035           } catch (...) {
55036             pclose(file);
55037             cimg::exception_mode(omode);
55038             throw CImgIOException(_cimg_instance
55039                                   "load_imagemagick_external(): Failed to load file '%s' with "
55040                                   "external command 'magick/convert'.",
55041                                   cimg_instance,
55042                                   filename);
55043           }
55044           pclose(file);
55045           return *this;
55046         }
55047       }
55048 #endif
55049       do {
55050         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
55051                       cimg::temporary_path(),
55052                       cimg_file_separator,
55053                       cimg::filenamerand(),
55054 #ifdef cimg_use_png
55055                       "png"
55056 #else
55057                       "pnm"
55058 #endif
55059                       );
55060         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55061       } while (file);
55062       cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"",
55063                     cimg::imagemagick_path(),
55064                     !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
55065                     s_filename.data(),
55066                     CImg<charT>::string(filename_tmp)._system_strescape().data());
55067       cimg::system(command,cimg::imagemagick_path());
55068       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55069         cimg::fclose(cimg::fopen(filename,"r"));
55070         throw CImgIOException(_cimg_instance
55071                               "load_imagemagick_external(): Failed to load file '%s' with "
55072                               "external command 'magick/convert'.",
55073                               cimg_instance,
55074                               filename);
55075 
55076       } else cimg::fclose(file);
55077 #ifdef cimg_use_png
55078       load_png(filename_tmp);
55079 #else
55080       load_pnm(filename_tmp);
55081 #endif
55082       std::remove(filename_tmp);
55083       return *this;
55084     }
55085 
55086     //! Load image using ImageMagick's external tool 'convert' \newinstance.
55087     static CImg<T> get_load_imagemagick_external(const char *const filename) {
55088       return CImg<T>().load_imagemagick_external(filename);
55089     }
55090 
55091     //! Load image from a DICOM file, using Medcon's external tool 'medcon'.
55092     /**
55093        \param filename Filename, as a C-string.
55094     **/
55095     CImg<T>& load_medcon_external(const char *const filename) {
55096       if (!filename)
55097         throw CImgArgumentException(_cimg_instance
55098                                     "load_medcon_external(): Specified filename is (null).",
55099                                     cimg_instance);
55100       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55101       CImg<charT> command(1024), filename_tmp(256), body(256);
55102       cimg::fclose(cimg::fopen(filename,"r"));
55103       std::FILE *file = 0;
55104       do {
55105         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
55106         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55107       } while (file);
55108       cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"",
55109                     cimg::medcon_path(),
55110                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
55111                     CImg<charT>::string(filename)._system_strescape().data());
55112       cimg::system(command, cimg::medcon_path());
55113       cimg::split_filename(filename_tmp,body);
55114 
55115       cimg_snprintf(command,command._width,"%s.hdr",body._data);
55116       file = cimg::std_fopen(command,"rb");
55117       if (!file) {
55118         cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
55119         file = cimg::std_fopen(command,"rb");
55120         if (!file) {
55121           throw CImgIOException(_cimg_instance
55122                                 "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
55123                                 cimg_instance,
55124                                 filename);
55125         }
55126       }
55127       cimg::fclose(file);
55128       load_analyze(command);
55129       std::remove(command);
55130       cimg::split_filename(command,body);
55131       cimg_snprintf(command,command._width,"%s.img",body._data);
55132       std::remove(command);
55133       return *this;
55134     }
55135 
55136     //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance.
55137     static CImg<T> get_load_medcon_external(const char *const filename) {
55138       return CImg<T>().load_medcon_external(filename);
55139     }
55140 
55141     //! Load image from a .pdf file.
55142     /**
55143        \param filename Filename, as a C-string.
55144        \param resolution Image resolution.
55145     **/
55146     CImg<T>& load_pdf_external(const char *const filename, const unsigned int resolution=400) {
55147       if (!filename)
55148         throw CImgArgumentException(_cimg_instance
55149                                     "load_pdf_external(): Specified filename is (null).",
55150                                     cimg_instance);
55151       CImg<charT> command(1024), filename_tmp(256);
55152       std::FILE *file = 0;
55153       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55154 #if cimg_OS==1
55155       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"",
55156                     resolution,s_filename.data());
55157       file = popen(command,"r");
55158       if (file) {
55159         const unsigned int omode = cimg::exception_mode();
55160         cimg::exception_mode(0);
55161         try { load_pnm(file); } catch (...) {
55162           pclose(file);
55163           cimg::exception_mode(omode);
55164           throw CImgIOException(_cimg_instance
55165                                 "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
55166                                 cimg_instance,
55167                                 filename);
55168         }
55169         pclose(file);
55170         return *this;
55171       }
55172 #endif
55173       do {
55174         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
55175                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55176         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55177       } while (file);
55178       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"",
55179                     CImg<charT>::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data());
55180       cimg::system(command,"gs");
55181       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55182         cimg::fclose(cimg::fopen(filename,"r"));
55183         throw CImgIOException(_cimg_instance
55184                               "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
55185                               cimg_instance,
55186                               filename);
55187       } else cimg::fclose(file);
55188       load_pnm(filename_tmp);
55189       std::remove(filename_tmp);
55190       return *this;
55191     }
55192 
55193     //! Load image from a .pdf file \newinstance.
55194     static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
55195       return CImg<T>().load_pdf_external(filename,resolution);
55196     }
55197 
55198     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
55199     /**
55200        \param filename Filename, as a C-string.
55201     **/
55202     CImg<T>& load_dcraw_external(const char *const filename) {
55203       if (!filename)
55204         throw CImgArgumentException(_cimg_instance
55205                                     "load_dcraw_external(): Specified filename is (null).",
55206                                     cimg_instance);
55207       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55208       CImg<charT> command(1024), filename_tmp(256);
55209       std::FILE *file = 0;
55210       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55211 #if cimg_OS==1
55212       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
55213                     cimg::dcraw_path(),s_filename.data());
55214       file = popen(command,"r");
55215       if (file) {
55216         const unsigned int omode = cimg::exception_mode();
55217         cimg::exception_mode(0);
55218         try { load_pnm(file); } catch (...) {
55219           pclose(file);
55220           cimg::exception_mode(omode);
55221           throw CImgIOException(_cimg_instance
55222                                 "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
55223                                 cimg_instance,
55224                                 filename);
55225         }
55226         pclose(file);
55227         return *this;
55228       }
55229 #endif
55230       do {
55231         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
55232                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55233         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55234       } while (file);
55235       cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"",
55236                     cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
55237       cimg::system(command,cimg::dcraw_path());
55238       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55239         cimg::fclose(cimg::fopen(filename,"r"));
55240         throw CImgIOException(_cimg_instance
55241                               "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
55242                               cimg_instance,
55243                               filename);
55244 
55245       } else cimg::fclose(file);
55246       load_pnm(filename_tmp);
55247       std::remove(filename_tmp);
55248       return *this;
55249     }
55250 
55251     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
55252     static CImg<T> get_load_dcraw_external(const char *const filename) {
55253       return CImg<T>().load_dcraw_external(filename);
55254     }
55255 
55256 #ifdef cimg_use_opencv
55257 
55258     // Convert a continuous cv::Mat<uchar> to a CImg<uchar>.
55259     static CImg<ucharT> _cvmat2cimg(const cv::Mat &src) {
55260       if (src.channels()==1) return CImg<ucharT>(src.ptr(),src.cols,src.rows,1,1);
55261       else if (src.channels()==3) { // BGR
55262         CImg<ucharT> res(src.cols,src.rows,1,src.channels());
55263         const unsigned char *ptrs = src.ptr();
55264         unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2);
55265         cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); }
55266         return res;
55267       }
55268       return CImg<ucharT>(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx");
55269     }
55270 
55271     // Convert a CImg<T> to a cv::Mat.
55272     cv::Mat _cimg2cvmat() const {
55273       if (is_empty())
55274         throw CImgInstanceException(_cimg_instance
55275                                     "_cimg2cvmat() : Instance image is empty.",
55276                                     cimg_instance);
55277       if (_spectrum==2)
55278         throw CImgInstanceException(_cimg_instance
55279                                     "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').",
55280                                     cimg_instance);
55281       if (_depth!=1)
55282         throw CImgInstanceException(_cimg_instance
55283                                     "_cimg2cvmat() : Invalid number of slices (should be '1').",
55284                                     cimg_instance);
55285       int mat_type = -1;
55286       if (pixel_type()==cimg::type<unsigned char>::string()) mat_type = CV_8UC1;
55287       if (pixel_type()==cimg::type<char>::string()) mat_type = CV_8SC1;
55288       if (pixel_type()==cimg::type<unsigned short>::string()) mat_type = CV_16UC1;
55289       if (pixel_type()==cimg::type<short>::string()) mat_type = CV_16SC1;
55290       if (pixel_type()==cimg::type<int>::string()) mat_type = CV_32SC1;
55291       if (pixel_type()==cimg::type<float>::string()) mat_type = CV_32FC1;
55292       if (pixel_type()==cimg::type<double>::string()) mat_type = CV_64FC1;
55293       if (mat_type<0)
55294         throw CImgInstanceException(_cimg_instance
55295                                     "_cvmat2cimg() : pixel type '%s' is not supported.",
55296                                     cimg_instance,pixel_type());
55297       cv::Mat res;
55298       std::vector<cv::Mat> channels(_spectrum);
55299       if (_spectrum>1) {
55300         cimg_forC(*this,c)
55301           channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c));
55302         cv::merge(channels,res);
55303       } else res = cv::Mat(_height,_width,mat_type,_data).clone();
55304       return res;
55305     }
55306 
55307 #endif
55308 
55309     //! Load image from a camera stream, using OpenCV.
55310     /**
55311        \param index Index of the camera to capture images from (from 0 to 63).
55312        \param capture_width Width of the desired image ('0' stands for default value).
55313        \param capture_height Height of the desired image ('0' stands for default value).
55314        \param skip_frames Number of frames to skip before the capture.
55315        \param release_camera Tells if the camera resource must be released at the end of the method.
55316     **/
55317     CImg<T>& load_camera(const unsigned int camera_index=0,
55318                          const unsigned int capture_width=0, const unsigned int capture_height=0,
55319                          const unsigned int skip_frames=0, const bool release_camera=true) {
55320 #ifdef cimg_use_opencv
55321       if (camera_index>=64)
55322         throw CImgArgumentException(_cimg_instance
55323                                     "load_camera(): Invalid request for camera #%u "
55324                                     "(no more than 100 cameras can be managed simultaneously).",
55325                                     cimg_instance,
55326                                     camera_index);
55327       static cv::VideoCapture *captures[64] = { 0 };
55328       static unsigned int captures_w[64], captures_h[64];
55329       if (release_camera) {
55330         cimg::mutex(9);
55331         if (captures[camera_index]) captures[camera_index]->release();
55332         delete captures[camera_index];
55333         captures[camera_index] = 0;
55334         captures_w[camera_index] = captures_h[camera_index] = 0;
55335         cimg::mutex(9,0);
55336         return *this;
55337       }
55338       if (!captures[camera_index]) {
55339         cimg::mutex(9);
55340         captures[camera_index] = new cv::VideoCapture(camera_index);
55341         captures_w[camera_index] = captures_h[camera_index] = 0;
55342         if (!captures[camera_index]->isOpened()) {
55343           delete captures[camera_index];
55344           captures[camera_index] = 0;
55345           cimg::mutex(9,0);
55346           throw CImgIOException(_cimg_instance
55347                                 "load_camera(): Failed to initialize camera #%u.",
55348                                 cimg_instance,
55349                                 camera_index);
55350         }
55351         cimg::mutex(9,0);
55352       }
55353       cimg::mutex(9);
55354       if (capture_width!=captures_w[camera_index]) {
55355         captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width);
55356         captures_w[camera_index] = capture_width;
55357       }
55358       if (capture_height!=captures_h[camera_index]) {
55359         captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height);
55360         captures_h[camera_index] = capture_height;
55361       }
55362       for (unsigned int i = 0; i<skip_frames; ++i) captures[camera_index]->grab();
55363       cv::Mat cvimg;
55364       captures[camera_index]->read(cvimg);
55365       if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this);
55366       cimg::mutex(9,0);
55367       return *this;
55368 #else
55369       cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
55370       throw CImgIOException(_cimg_instance
55371                             "load_camera(): This function requires features from the OpenCV library "
55372                             "('-Dcimg_use_opencv' must be defined).",
55373                             cimg_instance);
55374 #endif
55375     }
55376 
55377     //! Load image from a camera stream, using OpenCV \newinstance.
55378     static CImg<T> get_load_camera(const unsigned int camera_index=0,
55379                                    const unsigned int capture_width=0, const unsigned int capture_height=0,
55380                                    const unsigned int skip_frames=0, const bool release_camera=true) {
55381       return CImg<T>().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera);
55382     }
55383 
55384     //! Load image using various non-native ways.
55385     /**
55386        \param filename Filename, as a C-string.
55387     **/
55388     CImg<T>& load_other(const char *const filename) {
55389       if (!filename)
55390         throw CImgArgumentException(_cimg_instance
55391                                     "load_other(): Specified filename is (null).",
55392                                     cimg_instance);
55393 
55394       const unsigned int omode = cimg::exception_mode();
55395       cimg::exception_mode(0);
55396       try { load_magick(filename); }
55397       catch (CImgException&) {
55398         try { load_imagemagick_external(filename); }
55399         catch (CImgException&) {
55400           try { load_graphicsmagick_external(filename); }
55401           catch (CImgException&) {
55402             try { load_cimg(filename); }
55403             catch (CImgException&) {
55404               try {
55405                 cimg::fclose(cimg::fopen(filename,"rb"));
55406               } catch (CImgException&) {
55407                 cimg::exception_mode(omode);
55408                 throw CImgIOException(_cimg_instance
55409                                       "load_other(): Failed to open file '%s'.",
55410                                       cimg_instance,
55411                                       filename);
55412               }
55413               cimg::exception_mode(omode);
55414               throw CImgIOException(_cimg_instance
55415                                     "load_other(): Failed to recognize format of file '%s'.",
55416                                     cimg_instance,
55417                                     filename);
55418             }
55419           }
55420         }
55421       }
55422       cimg::exception_mode(omode);
55423       return *this;
55424     }
55425 
55426     //! Load image using various non-native ways \newinstance.
55427     static CImg<T> get_load_other(const char *const filename) {
55428       return CImg<T>().load_other(filename);
55429     }
55430 
55431     //@}
55432     //---------------------------
55433     //
55434     //! \name Data Output
55435     //@{
55436     //---------------------------
55437 
55438     //! Display information about the image data.
55439     /**
55440        \param title Name for the considered image.
55441        \param display_stats Tells to compute and display image statistics.
55442     **/
55443     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
55444 
55445       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
55446       CImg<doubleT> st;
55447       if (!is_empty() && display_stats) {
55448         st = get_stats();
55449         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
55450         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
55451       }
55452 
55453       const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
55454         mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
55455 
55456       CImg<charT> _title(64);
55457       if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
55458 
55459       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
55460                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
55461                    cimg::t_bold,cimg::t_normal,(void*)this,
55462                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
55463                    (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
55464                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
55465                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
55466       if (_data)
55467         std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
55468       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
55469 
55470       if (!is_empty()) cimg_foroff(*this,off) {
55471         std::fprintf(cimg::output(),"%g",(double)_data[off]);
55472         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
55473         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
55474       }
55475       if (!is_empty() && display_stats)
55476         std::fprintf(cimg::output(),
55477                      " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
55478                      "%scoords_max%s = (%u,%u,%u,%u).\n",
55479                      cimg::t_bold,cimg::t_normal,st[0],
55480                      cimg::t_bold,cimg::t_normal,st[1],
55481                      cimg::t_bold,cimg::t_normal,st[2],
55482                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
55483                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
55484                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
55485       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
55486       std::fflush(cimg::output());
55487       return *this;
55488     }
55489 
55490     //! Display image into a CImgDisplay window.
55491     /**
55492        \param disp Display window.
55493     **/
55494     const CImg<T>& display(CImgDisplay& disp) const {
55495       disp.display(*this);
55496       return *this;
55497     }
55498 
55499     //! Display image into a CImgDisplay window, in an interactive way.
55500     /**
55501         \param disp Display window.
55502         \param display_info Tells if image information are displayed on the standard output.
55503         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
55504         \param exit_on_anykey Exit function when any key is pressed.
55505     **/
55506     const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
55507                            const bool exit_on_anykey=false) const {
55508       return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
55509     }
55510 
55511     //! Display image into an interactive window.
55512     /**
55513         \param title Window title
55514         \param display_info Tells if image information are displayed on the standard output.
55515         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
55516         \param exit_on_anykey Exit function when any key is pressed.
55517     **/
55518     const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
55519                            const bool exit_on_anykey=false) const {
55520       CImgDisplay disp;
55521       return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
55522     }
55523 
55524     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
55525                             unsigned int *const XYZ, const bool exit_on_anykey,
55526                             const bool exit_on_singleclick) const {
55527       unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
55528       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
55529         old_mouse_x = -1, old_mouse_y = -1;
55530 
55531       if (!disp) {
55532         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
55533         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
55534         else disp.set_title("%s",title);
55535       } else if (title) disp.set_title("%s",title);
55536       disp.show().flush();
55537 
55538       const CImg<char> dtitle = CImg<char>::string(disp.title());
55539       if (display_info) print(dtitle);
55540 
55541       CImg<T> zoom;
55542       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
55543         if (reset_view) {
55544           if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
55545           else {
55546             _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2;
55547             _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2;
55548             _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2;
55549           }
55550           x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
55551           disp.resize(cimg_fitscreen(_width,_height,_depth),false);
55552           oldw = disp._width; oldh = disp._height;
55553           resize_disp = true;
55554           reset_view = false;
55555         }
55556         if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
55557           if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
55558         } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
55559 
55560         const CImg<T>& visu = zoom?zoom:*this;
55561         const unsigned int
55562           dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
55563           tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
55564         if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
55565           const float
55566             ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh,
55567             dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height());
55568           const unsigned int
55569             imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM);
55570           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
55571           resize_disp = false;
55572         }
55573         oldw = tw; oldh = th;
55574 
55575         bool
55576           go_up = false, go_down = false, go_left = false, go_right = false,
55577           go_inc = false, go_dec = false, go_in = false, go_out = false,
55578           go_in_center = false;
55579 
55580         disp.set_title("%s",dtitle._data);
55581         if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
55582         if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
55583         if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
55584 
55585         disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
55586         CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
55587         old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
55588         is_first_select = false;
55589 
55590         if (disp.wheel()) {
55591           if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
55592               (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
55593             go_left = !(go_right = disp.wheel()>0);
55594           } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
55595             go_down = !(go_up = disp.wheel()>0);
55596           } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55597             go_out = !(go_in = disp.wheel()>0); go_in_center = false;
55598           }
55599           disp.set_wheel();
55600         }
55601 
55602         const int
55603           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
55604           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
55605         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
55606           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
55607           x0+=sx0; y0+=sy0; z0+=sz0;
55608           if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
55609             if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true;
55610           }
55611           resize_disp = true;
55612         } else switch (key = disp.key()) {
55613 #if cimg_OS!=2
55614           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
55615 #endif
55616           case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
55617           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
55618               // Special mode: play stack of frames
55619               const unsigned int
55620                 w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
55621                 h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
55622               float frame_timing = 5;
55623               bool is_stopped = false;
55624               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
55625               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
55626                 if (disp.is_resized()) disp.resize(false);
55627                 if (!timer) {
55628                   visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
55629                   (++_XYZ[2])%=visu._depth;
55630                 }
55631                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
55632                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); }
55633                 switch (key = disp.key()) {
55634 #if cimg_OS!=2
55635                 case cimg::keyCTRLRIGHT :
55636 #endif
55637                 case cimg::keyCTRLLEFT : key = 0; break;
55638                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
55639                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
55640                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
55641                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
55642                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
55643                   (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
55644                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55645                     disp.set_fullscreen(false).
55646                       resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
55647                              CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
55648                     disp.set_key(key,false); key = 0;
55649                   } break;
55650                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55651                     disp.set_fullscreen(false).
55652                       resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
55653                   } break;
55654                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55655                     disp.set_fullscreen(false).
55656                       resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
55657                   } break;
55658                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55659                     disp.resize(disp.screen_width(),disp.screen_height(),false).
55660                       toggle_fullscreen().set_key(key,false); key = 0;
55661                   } break;
55662                 }
55663                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
55664                 disp.wait(20);
55665               }
55666               const unsigned int
55667                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
55668                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
55669               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
55670               key = 0;
55671             } break;
55672           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
55673           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
55674           case cimg::keyPADSUB : go_out = true; key = 0; break;
55675           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
55676           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
55677           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
55678           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
55679           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
55680           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
55681           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
55682           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
55683           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
55684           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
55685           }
55686         if (go_in) {
55687           const int
55688             mx = go_in_center?disp.width()/2:disp.mouse_x(),
55689             my = go_in_center?disp.height()/2:disp.mouse_y(),
55690             mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
55691             mY = my*(height() + (depth()>1?depth():0))/disp.height();
55692           int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
55693           if (mX<width() && mY<height())  {
55694             X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
55695           }
55696           if (mX<width() && mY>=height()) {
55697             X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
55698           }
55699           if (mX>=width() && mY<height()) {
55700             Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
55701           }
55702           if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
55703           if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
55704           if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
55705         }
55706         if (go_out) {
55707           const int
55708             delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
55709             ndelta_x = delta_x?delta_x:(_width>1),
55710             ndelta_y = delta_y?delta_y:(_height>1),
55711             ndelta_z = delta_z?delta_z:(_depth>1);
55712           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
55713           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
55714           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
55715           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
55716           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
55717           if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
55718           if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
55719           if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
55720           const float
55721             ratio = (float)(x1-x0)/(y1-y0),
55722             ratiow = (float)disp._width/disp._height,
55723             sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
55724           if (sub>0.01) resize_disp = true;
55725         }
55726         if (go_left) {
55727           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
55728           if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
55729           else { x1-=x0; x0 = 0; }
55730         }
55731         if (go_right) {
55732           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
55733           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
55734           else { x0+=(width() - 1 - x1); x1 = width() - 1; }
55735         }
55736         if (go_up) {
55737           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
55738           if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
55739           else { y1-=y0; y0 = 0; }
55740         }
55741         if (go_down) {
55742           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
55743           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
55744           else { y0+=(height() - 1 - y1); y1 = height() - 1; }
55745         }
55746         if (go_inc) {
55747           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
55748           if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
55749           else { z1-=z0; z0 = 0; }
55750         }
55751         if (go_dec) {
55752           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
55753           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
55754           else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
55755         }
55756         disp.wait(100);
55757         if (!exit_on_anykey && key && key!=cimg::keyESC &&
55758             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
55759           key = 0;
55760         }
55761       }
55762       disp.set_key(key);
55763       if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
55764       return *this;
55765     }
55766 
55767     //! Display object 3D in an interactive window.
55768     /**
55769        \param disp Display window.
55770        \param vertices Vertices data of the 3D object.
55771        \param primitives Primitives data of the 3D object.
55772        \param colors Colors data of the 3D object.
55773        \param opacities Opacities data of the 3D object.
55774        \param centering Tells if the 3D object must be centered for the display.
55775        \param render_static Rendering mode.
55776        \param render_motion Rendering mode, when the 3D object is moved.
55777        \param is_double_sided Tells if the object primitives are double-sided.
55778        \param focale Focale
55779        \param light_x X-coordinate of the light source.
55780        \param light_y Y-coordinate of the light source.
55781        \param light_z Z-coordinate of the light source.
55782        \param specular_lightness Amount of specular light.
55783        \param specular_shininess Shininess of the object material.
55784        \param display_axes Tells if the 3D axes are displayed.
55785        \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix).
55786        \param exit_on_anykey Exit function when any key is pressed.
55787     **/
55788     template<typename tp, typename tf, typename tc, typename to>
55789     const CImg<T>& display_object3d(CImgDisplay& disp,
55790                                     const CImg<tp>& vertices,
55791                                     const CImgList<tf>& primitives,
55792                                     const CImgList<tc>& colors,
55793                                     const to& opacities,
55794                                     const bool centering=true,
55795                                     const int render_static=4, const int render_motion=1,
55796                                     const bool is_double_sided=true, const float focale=700,
55797                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55798                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55799                                     const bool display_axes=true, float *const pose_matrix=0,
55800                                     const bool exit_on_anykey=false) const {
55801       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
55802                                render_motion,is_double_sided,focale,
55803                                light_x,light_y,light_z,specular_lightness,specular_shininess,
55804                                display_axes,pose_matrix,exit_on_anykey);
55805     }
55806 
55807     //! Display object 3D in an interactive window \simplification.
55808     template<typename tp, typename tf, typename tc, typename to>
55809     const CImg<T>& display_object3d(const char *const title,
55810                                     const CImg<tp>& vertices,
55811                                     const CImgList<tf>& primitives,
55812                                     const CImgList<tc>& colors,
55813                                     const to& opacities,
55814                                     const bool centering=true,
55815                                     const int render_static=4, const int render_motion=1,
55816                                     const bool is_double_sided=true, const float focale=700,
55817                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55818                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55819                                     const bool display_axes=true, float *const pose_matrix=0,
55820                                     const bool exit_on_anykey=false) const {
55821       CImgDisplay disp;
55822       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
55823                                render_motion,is_double_sided,focale,
55824                                light_x,light_y,light_z,specular_lightness,specular_shininess,
55825                                display_axes,pose_matrix,exit_on_anykey);
55826     }
55827 
55828     //! Display object 3D in an interactive window \simplification.
55829     template<typename tp, typename tf, typename tc>
55830     const CImg<T>& display_object3d(CImgDisplay &disp,
55831                                     const CImg<tp>& vertices,
55832                                     const CImgList<tf>& primitives,
55833                                     const CImgList<tc>& colors,
55834                                     const bool centering=true,
55835                                     const int render_static=4, const int render_motion=1,
55836                                     const bool is_double_sided=true, const float focale=700,
55837                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55838                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55839                                     const bool display_axes=true, float *const pose_matrix=0,
55840                                     const bool exit_on_anykey=false) const {
55841       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
55842                               render_static,render_motion,is_double_sided,focale,
55843                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55844                               display_axes,pose_matrix,exit_on_anykey);
55845     }
55846 
55847     //! Display object 3D in an interactive window \simplification.
55848     template<typename tp, typename tf, typename tc>
55849     const CImg<T>& display_object3d(const char *const title,
55850                                     const CImg<tp>& vertices,
55851                                     const CImgList<tf>& primitives,
55852                                     const CImgList<tc>& colors,
55853                                     const bool centering=true,
55854                                     const int render_static=4, const int render_motion=1,
55855                                     const bool is_double_sided=true, const float focale=700,
55856                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55857                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55858                                     const bool display_axes=true, float *const pose_matrix=0,
55859                                     const bool exit_on_anykey=false) const {
55860       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
55861                               render_static,render_motion,is_double_sided,focale,
55862                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55863                               display_axes,pose_matrix,exit_on_anykey);
55864     }
55865 
55866     //! Display object 3D in an interactive window \simplification.
55867     template<typename tp, typename tf>
55868     const CImg<T>& display_object3d(CImgDisplay &disp,
55869                                     const CImg<tp>& vertices,
55870                                     const CImgList<tf>& primitives,
55871                                     const bool centering=true,
55872                                     const int render_static=4, const int render_motion=1,
55873                                     const bool is_double_sided=true, const float focale=700,
55874                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55875                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55876                                     const bool display_axes=true, float *const pose_matrix=0,
55877                                     const bool exit_on_anykey=false) const {
55878       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
55879                               render_static,render_motion,is_double_sided,focale,
55880                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55881                               display_axes,pose_matrix,exit_on_anykey);
55882     }
55883 
55884 
55885     //! Display object 3D in an interactive window \simplification.
55886     template<typename tp, typename tf>
55887     const CImg<T>& display_object3d(const char *const title,
55888                                     const CImg<tp>& vertices,
55889                                     const CImgList<tf>& primitives,
55890                                     const bool centering=true,
55891                                     const int render_static=4, const int render_motion=1,
55892                                     const bool is_double_sided=true, const float focale=700,
55893                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55894                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55895                                     const bool display_axes=true, float *const pose_matrix=0,
55896                                     const bool exit_on_anykey=false) const {
55897       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
55898                               render_static,render_motion,is_double_sided,focale,
55899                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55900                               display_axes,pose_matrix,exit_on_anykey);
55901     }
55902 
55903     //! Display object 3D in an interactive window \simplification.
55904     template<typename tp>
55905     const CImg<T>& display_object3d(CImgDisplay &disp,
55906                                     const CImg<tp>& vertices,
55907                                     const bool centering=true,
55908                                     const int render_static=4, const int render_motion=1,
55909                                     const bool is_double_sided=true, const float focale=700,
55910                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55911                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55912                                     const bool display_axes=true, float *const pose_matrix=0,
55913                                     const bool exit_on_anykey=false) const {
55914       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
55915                               render_static,render_motion,is_double_sided,focale,
55916                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55917                               display_axes,pose_matrix,exit_on_anykey);
55918     }
55919 
55920     //! Display object 3D in an interactive window \simplification.
55921     template<typename tp>
55922     const CImg<T>& display_object3d(const char *const title,
55923                                     const CImg<tp>& vertices,
55924                                     const bool centering=true,
55925                                     const int render_static=4, const int render_motion=1,
55926                                     const bool is_double_sided=true, const float focale=700,
55927                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55928                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55929                                     const bool display_axes=true, float *const pose_matrix=0,
55930                                     const bool exit_on_anykey=false) const {
55931       return display_object3d(title,vertices,CImgList<uintT>(),centering,
55932                               render_static,render_motion,is_double_sided,focale,
55933                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55934                               display_axes,pose_matrix,exit_on_anykey);
55935     }
55936 
55937     template<typename tp, typename tf, typename tc, typename to>
55938     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
55939                                      const CImg<tp>& vertices,
55940                                      const CImgList<tf>& primitives,
55941                                      const CImgList<tc>& colors,
55942                                      const to& opacities,
55943                                      const bool centering,
55944                                      const int render_static, const int render_motion,
55945                                      const bool is_double_sided, const float focale,
55946                                      const float light_x, const float light_y, const float light_z,
55947                                      const float specular_lightness, const float specular_shininess,
55948                                      const bool display_axes, float *const pose_matrix,
55949                                      const bool exit_on_anykey) const {
55950       typedef typename cimg::superset<tp,float>::type tpfloat;
55951 
55952       // Check input arguments
55953       if (is_empty()) {
55954         CImg<T> background;
55955         if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128);
55956         else background.assign(1,2,1,3,32,64,32,116,64,96);
55957         if (disp) background.resize(disp.width(),disp.height(),1,-100,3);
55958         else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
55959                                               CImgDisplay::screen_height()/2,1),1,-100,3);
55960         return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
55961                                             render_static,render_motion,is_double_sided,focale,
55962                                             light_x,light_y,light_z,specular_lightness,specular_shininess,
55963                                             display_axes,pose_matrix,exit_on_anykey);
55964       } else { if (disp) disp.resize(*this,false); }
55965       CImg<charT> error_message(1024);
55966       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
55967         throw CImgArgumentException(_cimg_instance
55968                                     "display_object3d(): Invalid specified 3D object (%u,%u) (%s).",
55969                                     cimg_instance,vertices._width,primitives._width,error_message.data());
55970       if (vertices._width && !primitives) {
55971         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
55972         cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
55973         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
55974                                  render_static,render_motion,is_double_sided,focale,
55975                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
55976                                  display_axes,pose_matrix,exit_on_anykey);
55977       }
55978       if (!disp) {
55979         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
55980         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
55981                                    pixel_type(),vertices._width,primitives._width);
55982       } else if (title) disp.set_title("%s",title);
55983 
55984       // Init 3D objects and compute object statistics
55985       CImg<floatT>
55986         pose,
55987         rotated_vertices(vertices._width,3),
55988         bbox_vertices, rotated_bbox_vertices,
55989         axes_vertices, rotated_axes_vertices,
55990         bbox_opacities, axes_opacities;
55991       CImgList<uintT> bbox_primitives, axes_primitives;
55992       CImgList<tf> reverse_primitives;
55993       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
55994       unsigned int ns_width = 0, ns_height = 0;
55995       int _is_double_sided = (int)is_double_sided;
55996       bool ndisplay_axes = display_axes;
55997       const CImg<T>
55998         background_color(1,1,1,_spectrum,0),
55999         foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type<T>::max(),255));
56000       float
56001         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
56002         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
56003         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
56004         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
56005       const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
56006 
56007       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
56008                                                    xm,xM,xM,xm,xm,xM,xM,xm,
56009                                                    ym,ym,yM,yM,ym,ym,yM,yM,
56010                                                    zm,zm,zm,zm,zM,zM,zM,zM);
56011       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);
56012       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
56013       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
56014       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
56015 
56016       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
56017                                                    0,20,0,0,22,-6,-6,
56018                                                    0,0,20,0,-6,22,-6,
56019                                                    0,0,0,20,0,0,22);
56020       axes_opacities.assign(3,1,1,1,1);
56021       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
56022       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
56023 
56024       // Begin user interaction loop
56025       CImg<T> visu0(*this,false), visu;
56026       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
56027       bool init_pose = true, clicked = false, redraw = true;
56028       unsigned int key = 0, font_size = 32;
56029       int
56030         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
56031         nrender_static = render_static,
56032         nrender_motion = render_motion;
56033       disp.show().flush();
56034 
56035       while (!disp.is_closed() && !key) {
56036 
56037         // Init object pose
56038         if (init_pose) {
56039           const float
56040             ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1,
56041             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
56042           if (centering)
56043             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
56044           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
56045           if (pose_matrix) {
56046             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
56047             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
56048             pose0(3,3) = pose(3,3) = 1;
56049             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
56050             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
56051           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
56052           init_pose = false;
56053           redraw = true;
56054         }
56055 
56056         // Rotate and draw 3D object
56057         if (redraw) {
56058           const float
56059             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
56060             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
56061             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
56062           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
56063             const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2);
56064             float
56065               *const prv0 = rotated_vertices.data(),
56066               *const prv1 = rotated_vertices.data(0,1),
56067               *const prv2 = rotated_vertices.data(0,2);
56068             cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024))
56069             cimg_forX(vertices,l) {
56070               const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l];
56071               prv0[l] = r00*x + r10*y + r20*z + r30;
56072               prv1[l] = r01*x + r11*y + r21*z + r31;
56073               prv2[l] = r02*x + r12*y + r22*z + r32;
56074             }
56075           }
56076           else cimg_forX(bbox_vertices,l) {
56077               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
56078               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
56079               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
56080               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
56081             }
56082 
56083           // Draw objects
56084           const bool render_with_zbuffer = !clicked && nrender_static>0;
56085           visu = visu0;
56086           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
56087             visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56088                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
56089               draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56090                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
56091           else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
56092                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56093                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56094                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
56095                                    width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff,
56096                                    specular_lightness,specular_shininess,1,sprite_scale);
56097           // Draw axes
56098           if (ndisplay_axes) {
56099             const float
56100               n = 1e-8f + cimg::hypot(r00,r01,r02),
56101               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
56102               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
56103               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
56104               Xaxes = 25, Yaxes = visu._height - 38.f;
56105             cimg_forX(axes_vertices,l) {
56106               const float
56107                 x = axes_vertices(l,0),
56108                 y = axes_vertices(l,1),
56109                 z = axes_vertices(l,2);
56110               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
56111               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
56112               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
56113             }
56114             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f;
56115             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f;
56116             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f;
56117             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
56118                                axes_colors,axes_opacities,1,false,focale).
56119               draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
56120                         (int)(Yaxes + rotated_axes_vertices(4,1)),
56121                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
56122               draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
56123                         (int)(Yaxes + rotated_axes_vertices(5,1)),
56124                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
56125               draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
56126                         (int)(Yaxes + rotated_axes_vertices(6,1)),
56127                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
56128           }
56129           visu.display(disp);
56130           if (!clicked || nrender_motion==nrender_static) redraw = false;
56131         }
56132 
56133         // Handle user interaction
56134         if (!redraw) disp.wait();
56135         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
56136           redraw = true;
56137           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
56138           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
56139           const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT();
56140           if (disp.button()&1 && !is_keyCTRL) {
56141             const float
56142               R = 0.45f*std::min(disp.width(),disp.height()),
56143               R2 = R*R,
56144               u0 = (float)(x0 - disp.width()/2),
56145               v0 = (float)(y0 - disp.height()/2),
56146               u1 = (float)(x1 - disp.width()/2),
56147               v1 = (float)(y1 - disp.height()/2),
56148               n0 = cimg::hypot(u0,v0),
56149               n1 = cimg::hypot(u1,v1),
56150               nu0 = n0>R?(u0*R/n0):u0,
56151               nv0 = n0>R?(v0*R/n0):v0,
56152               nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
56153               nu1 = n1>R?(u1*R/n1):u1,
56154               nv1 = n1>R?(v1*R/n1):v1,
56155               nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
56156               u = nv0*nw1 - nw0*nv1,
56157               v = nw0*nu1 - nu0*nw1,
56158               w = nv0*nu1 - nu0*nv1,
56159               n = cimg::hypot(u,v,w),
56160               alpha = (float)std::asin(n/R2)*180/cimg::PI;
56161             (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
56162             x0 = x1; y0 = y1;
56163           }
56164           if (disp.button()&2 && !is_keyCTRL) {
56165             if (focale>0) Zoff-=(y0 - y1)*focale/400;
56166             else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; }
56167             x0 = x1; y0 = y1;
56168           }
56169           if (disp.wheel()) {
56170             if (focale>0) Zoff-=disp.wheel()*focale/20;
56171             else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; }
56172             disp.set_wheel();
56173           }
56174           if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) {
56175             Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1;
56176           }
56177           if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) {
56178             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
56179             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
56180           }
56181         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
56182 
56183         CImg<charT> filename(32);
56184         switch (key = disp.key()) {
56185 #if cimg_OS!=2
56186         case cimg::keyCTRLRIGHT :
56187 #endif
56188         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
56189         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56190             disp.set_fullscreen(false).
56191               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
56192                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
56193               _is_resized = true;
56194             disp.set_key(key,false); key = 0;
56195           } break;
56196         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56197             disp.set_fullscreen(false).
56198               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
56199             disp.set_key(key,false); key = 0;
56200           } break;
56201         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56202             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
56203             disp.set_key(key,false); key = 0;
56204           } break;
56205         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56206             if (!ns_width || !ns_height ||
56207                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
56208               ns_width = disp.screen_width()*3U/4;
56209               ns_height = disp.screen_height()*3U/4;
56210             }
56211             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
56212             else {
56213               ns_width = disp._width; ns_height = disp._height;
56214               disp.resize(disp.screen_width(),disp.screen_height(),false);
56215             }
56216             disp.toggle_fullscreen()._is_resized = true;
56217             disp.set_key(key,false); key = 0;
56218           } break;
56219         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56220             // Switch single/double-sided primitives.
56221             if (--_is_double_sided==-2) _is_double_sided = 1;
56222             if (_is_double_sided>=0) reverse_primitives.assign();
56223             else primitives.get_reverse_object3d().move_to(reverse_primitives);
56224             disp.set_key(key,false); key = 0; redraw = true;
56225           } break;
56226         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
56227             if (zbuffer) zbuffer.assign();
56228             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
56229             disp.set_key(key,false); key = 0; redraw = true;
56230           } break;
56231         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes
56232             ndisplay_axes = !ndisplay_axes;
56233             disp.set_key(key,false); key = 0; redraw = true;
56234           } break;
56235         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points
56236             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
56237             disp.set_key(key,false); key = 0; redraw = true;
56238           } break;
56239         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines
56240             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
56241             disp.set_key(key,false); key = 0; redraw = true;
56242           } break;
56243         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat
56244             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
56245             disp.set_key(key,false); key = 0; redraw = true;
56246           } break;
56247         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded
56248             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
56249             disp.set_key(key,false); key = 0; redraw = true;
56250           } break;
56251         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56252             // Set rendering mode to gouraud-shaded.
56253             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
56254             disp.set_key(key,false); key = 0; redraw = true;
56255           } break;
56256         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded
56257             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
56258             disp.set_key(key,false); key = 0; redraw = true;
56259           } break;
56260         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
56261             static unsigned int snap_number = 0;
56262             std::FILE *file;
56263             do {
56264               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
56265               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56266             } while (file);
56267             (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
56268             visu.save(filename);
56269             (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
56270             disp.set_key(key,false); key = 0;
56271           } break;
56272         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
56273             static unsigned int snap_number = 0;
56274             std::FILE *file;
56275             do {
56276               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
56277               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56278             } while (file);
56279             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
56280             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
56281             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56282             disp.set_key(key,false); key = 0;
56283           } break;
56284         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
56285             static unsigned int snap_number = 0;
56286             std::FILE *file;
56287             do {
56288 
56289 #ifdef cimg_use_zlib
56290               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
56291 #else
56292               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
56293 #endif
56294               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56295             } while (file);
56296             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
56297             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
56298               save(filename);
56299             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56300             disp.set_key(key,false); key = 0;
56301           } break;
56302 
56303 #ifdef cimg_use_board
56304         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
56305             static unsigned int snap_number = 0;
56306             std::FILE *file;
56307             do {
56308               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
56309               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56310             } while (file);
56311             (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
56312             LibBoard::Board board;
56313             (+visu)._draw_object3d(&board,zbuffer.fill(0),
56314                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56315                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56316                                    colors,opacities,clicked?nrender_motion:nrender_static,
56317                                    _is_double_sided==1,focale,
56318                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
56319                                    specular_lightness,specular_shininess,1,
56320                                    sprite_scale);
56321             board.saveEPS(filename);
56322             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56323             disp.set_key(key,false); key = 0;
56324           } break;
56325         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
56326             static unsigned int snap_number = 0;
56327             std::FILE *file;
56328             do {
56329               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
56330               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56331             } while (file);
56332             (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
56333             LibBoard::Board board;
56334             (+visu)._draw_object3d(&board,zbuffer.fill(0),
56335                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56336                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56337                                    colors,opacities,clicked?nrender_motion:nrender_static,
56338                                    _is_double_sided==1,focale,
56339                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
56340                                    specular_lightness,specular_shininess,1,
56341                                    sprite_scale);
56342             board.saveSVG(filename);
56343             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56344             disp.set_key(key,false); key = 0;
56345           } break;
56346 #endif
56347         }
56348         if (disp.is_resized()) {
56349           disp.resize(false); visu0 = get_resize(disp,1);
56350           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
56351           redraw = true;
56352         }
56353         if (!exit_on_anykey && key && key!=cimg::keyESC &&
56354             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
56355           key = 0;
56356         }
56357       }
56358       if (pose_matrix) {
56359         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
56360         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
56361       }
56362       disp.set_button().set_key(key);
56363       return *this;
56364     }
56365 
56366     //! Display 1D graph in an interactive window.
56367     /**
56368        \param disp Display window.
56369        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
56370        \param vertex_type Vertex type.
56371        \param labelx Title for the horizontal axis, as a C-string.
56372        \param xmin Minimum value along the X-axis.
56373        \param xmax Maximum value along the X-axis.
56374        \param labely Title for the vertical axis, as a C-string.
56375        \param ymin Minimum value along the X-axis.
56376        \param ymax Maximum value along the X-axis.
56377        \param exit_on_anykey Exit function when any key is pressed.
56378     **/
56379     const CImg<T>& display_graph(CImgDisplay &disp,
56380                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
56381                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
56382                                  const char *const labely=0, const double ymin=0, const double ymax=0,
56383                                  const bool exit_on_anykey=false) const {
56384       return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
56385     }
56386 
56387     //! Display 1D graph in an interactive window \overloading.
56388     const CImg<T>& display_graph(const char *const title=0,
56389                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
56390                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
56391                                  const char *const labely=0, const double ymin=0, const double ymax=0,
56392                                  const bool exit_on_anykey=false) const {
56393       CImgDisplay disp;
56394       return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
56395     }
56396 
56397     const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
56398                                   const unsigned int plot_type=1, const unsigned int vertex_type=1,
56399                                   const char *const labelx=0, const double xmin=0, const double xmax=0,
56400                                   const char *const labely=0, const double ymin=0, const double ymax=0,
56401                                   const bool exit_on_anykey=false) const {
56402       if (is_empty())
56403         throw CImgInstanceException(_cimg_instance
56404                                     "display_graph(): Empty instance.",
56405                                     cimg_instance);
56406       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
56407                    set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
56408       const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
56409       const unsigned int old_normalization = disp.normalization();
56410       disp.show().flush()._normalization = 0;
56411 
56412       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
56413       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
56414       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
56415 
56416       for (bool reset_view = true; !key && !disp.is_closed(); ) {
56417         if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
56418         CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
56419         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
56420         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
56421         if (y0==y1) { --y0; ++y1; }
56422 
56423         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
56424                                                            labelx,
56425                                                            nxmin + x0*(nxmax - nxmin)/siz1,
56426                                                            nxmin + x1*(nxmax - nxmin)/siz1,
56427                                                            labely,y0,y1,true);
56428         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
56429         if (selection[0]>=0) {
56430           if (selection[2]<0) reset_view = true;
56431           else {
56432             x1 = x0 + selection[2]; x0+=selection[0];
56433             if (selection[1]>=0 && selection[3]>=0) {
56434               y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
56435               y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
56436             }
56437           }
56438         } else {
56439           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
56440           switch (key = (int)disp.key()) {
56441           case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
56442           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
56443           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
56444           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
56445             break;
56446           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
56447             break;
56448           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
56449           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
56450           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
56451           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
56452           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
56453           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
56454           }
56455           if (disp.wheel()) {
56456             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
56457             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
56458             else go_out = !(go_in = disp.wheel()>0);
56459             key = 0;
56460           }
56461 
56462           if (go_in) {
56463             const int
56464               xsiz = x1 - x0,
56465               mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
56466               cx = x0 + cimg::cut(mx,0,xsiz);
56467             if (x1 - x0>4) {
56468               x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
56469               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56470                 const double
56471                   ysiz = y1 - y0,
56472                   my = (mouse_y - 16)*ysiz/(disp.height() - 32),
56473                   cy = y1 - cimg::cut(my,0.,ysiz);
56474                 y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
56475               } else y0 = y1 = 0;
56476             }
56477           }
56478           if (go_out) {
56479             if (x0>0 || x1<(int)siz1) {
56480               const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
56481               const double ndelta_y = (y1 - y0)/8;
56482               x0-=ndelta_x; x1+=ndelta_x;
56483               y0-=ndelta_y; y1+=ndelta_y;
56484               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
56485               if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
56486             }
56487           }
56488           if (go_left) {
56489             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
56490             if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
56491             else { x1-=x0; x0 = 0; }
56492             go_left = false;
56493           }
56494           if (go_right) {
56495             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
56496             if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
56497             else { x0+=(siz1 - x1); x1 = (int)siz1; }
56498             go_right = false;
56499           }
56500           if (go_up) {
56501             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
56502             y0+=ndelta; y1+=ndelta;
56503             go_up = false;
56504           }
56505           if (go_down) {
56506             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
56507             y0-=ndelta; y1-=ndelta;
56508             go_down = false;
56509           }
56510         }
56511         if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
56512             (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
56513           disp.set_key(key,false);
56514           key = 0;
56515         }
56516       }
56517       disp._normalization = old_normalization;
56518       return *this;
56519     }
56520 
56521     //! Save image as a file.
56522     /**
56523        \param filename Filename, as a C-string.
56524        \param number When positive, represents an index added to the filename. Otherwise, no number is added.
56525        \param digits Number of digits used for adding the number to the filename.
56526        \note
56527        - The used file format is defined by the file extension in the filename \p filename.
56528        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
56529 
56530     **/
56531     const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
56532       if (!filename)
56533         throw CImgArgumentException(_cimg_instance
56534                                     "save(): Specified filename is (null).",
56535                                     cimg_instance);
56536       // Do not test for empty instances, since .cimg format is able to manage empty instances.
56537       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
56538       const char *const ext = cimg::split_filename(filename);
56539       CImg<charT> nfilename(1024);
56540       const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
56541         filename;
56542 
56543 #ifdef cimg_save_plugin
56544       cimg_save_plugin(fn);
56545 #endif
56546 #ifdef cimg_save_plugin1
56547       cimg_save_plugin1(fn);
56548 #endif
56549 #ifdef cimg_save_plugin2
56550       cimg_save_plugin2(fn);
56551 #endif
56552 #ifdef cimg_save_plugin3
56553       cimg_save_plugin3(fn);
56554 #endif
56555 #ifdef cimg_save_plugin4
56556       cimg_save_plugin4(fn);
56557 #endif
56558 #ifdef cimg_save_plugin5
56559       cimg_save_plugin5(fn);
56560 #endif
56561 #ifdef cimg_save_plugin6
56562       cimg_save_plugin6(fn);
56563 #endif
56564 #ifdef cimg_save_plugin7
56565       cimg_save_plugin7(fn);
56566 #endif
56567 #ifdef cimg_save_plugin8
56568       cimg_save_plugin8(fn);
56569 #endif
56570       // Text formats
56571       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
56572       else if (!cimg::strcasecmp(ext,"csv") ||
56573                !cimg::strcasecmp(ext,"dlm") ||
56574                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
56575       else if (!cimg::strcasecmp(ext,"cpp") ||
56576                !cimg::strcasecmp(ext,"hpp") ||
56577                !cimg::strcasecmp(ext,"h") ||
56578                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
56579 
56580       // 2D binary formats
56581       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
56582       else if (!cimg::strcasecmp(ext,"jpg") ||
56583                !cimg::strcasecmp(ext,"jpeg") ||
56584                !cimg::strcasecmp(ext,"jpe") ||
56585                !cimg::strcasecmp(ext,"jfif") ||
56586                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
56587       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
56588       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
56589       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
56590       else if (!cimg::strcasecmp(ext,"pgm") ||
56591                !cimg::strcasecmp(ext,"ppm") ||
56592                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
56593       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
56594       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
56595       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
56596       else if (!cimg::strcasecmp(ext,"tif") ||
56597                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
56598 
56599       // 3D binary formats
56600       else if (!*ext) {
56601 #ifdef cimg_use_zlib
56602         return save_cimg(fn,true);
56603 #else
56604         return save_cimg(fn,false);
56605 #endif
56606       } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
56607       else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
56608       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
56609       else if (!cimg::strcasecmp(ext,"hdr") ||
56610                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
56611       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
56612       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
56613       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
56614       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
56615 
56616       // Archive files
56617       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
56618 
56619       // Image sequences
56620       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
56621       else if (!cimg::strcasecmp(ext,"avi") ||
56622                !cimg::strcasecmp(ext,"mov") ||
56623                !cimg::strcasecmp(ext,"asf") ||
56624                !cimg::strcasecmp(ext,"divx") ||
56625                !cimg::strcasecmp(ext,"flv") ||
56626                !cimg::strcasecmp(ext,"mpg") ||
56627                !cimg::strcasecmp(ext,"m1v") ||
56628                !cimg::strcasecmp(ext,"m2v") ||
56629                !cimg::strcasecmp(ext,"m4v") ||
56630                !cimg::strcasecmp(ext,"mjp") ||
56631                !cimg::strcasecmp(ext,"mp4") ||
56632                !cimg::strcasecmp(ext,"mkv") ||
56633                !cimg::strcasecmp(ext,"mpe") ||
56634                !cimg::strcasecmp(ext,"movie") ||
56635                !cimg::strcasecmp(ext,"ogm") ||
56636                !cimg::strcasecmp(ext,"ogg") ||
56637                !cimg::strcasecmp(ext,"ogv") ||
56638                !cimg::strcasecmp(ext,"qt") ||
56639                !cimg::strcasecmp(ext,"rm") ||
56640                !cimg::strcasecmp(ext,"vob") ||
56641                !cimg::strcasecmp(ext,"webm") ||
56642                !cimg::strcasecmp(ext,"wmv") ||
56643                !cimg::strcasecmp(ext,"xvid") ||
56644                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
56645       return save_other(fn);
56646     }
56647 
56648     //! Save image as an ascii file.
56649     /**
56650       \param filename Filename, as a C-string.
56651     **/
56652     const CImg<T>& save_ascii(const char *const filename) const {
56653       return _save_ascii(0,filename);
56654     }
56655 
56656     //! Save image as an Ascii file \overloading.
56657     const CImg<T>& save_ascii(std::FILE *const file) const {
56658       return _save_ascii(file,0);
56659     }
56660 
56661     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
56662       if (!file && !filename)
56663         throw CImgArgumentException(_cimg_instance
56664                                     "save_ascii(): Specified filename is (null).",
56665                                     cimg_instance);
56666       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56667       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
56668       const T* ptrs = _data;
56669       cimg_forYZC(*this,y,z,c) {
56670         cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
56671         std::fputc('\n',nfile);
56672       }
56673       if (!file) cimg::fclose(nfile);
56674       return *this;
56675     }
56676 
56677     //! Save image as a .cpp source file.
56678     /**
56679       \param filename Filename, as a C-string.
56680     **/
56681     const CImg<T>& save_cpp(const char *const filename) const {
56682       return _save_cpp(0,filename);
56683     }
56684 
56685     //! Save image as a .cpp source file \overloading.
56686     const CImg<T>& save_cpp(std::FILE *const file) const {
56687       return _save_cpp(file,0);
56688     }
56689 
56690     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
56691       if (!file && !filename)
56692         throw CImgArgumentException(_cimg_instance
56693                                     "save_cpp(): Specified filename is (null).",
56694                                     cimg_instance);
56695       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56696       CImg<charT> varname(1024); *varname = 0;
56697       if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
56698       if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
56699       std::fprintf(nfile,
56700                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
56701                    "%s data_%s[] = { %s\n  ",
56702                    varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
56703                    is_empty()?"};":"");
56704       if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
56705         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
56706         if (off==siz) std::fprintf(nfile," };\n");
56707         else if (!((off + 1)%16)) std::fprintf(nfile,",\n  ");
56708         else std::fprintf(nfile,", ");
56709       }
56710       if (!file) cimg::fclose(nfile);
56711       return *this;
56712     }
56713 
56714     //! Save image as a DLM file.
56715     /**
56716        \param filename Filename, as a C-string.
56717     **/
56718     const CImg<T>& save_dlm(const char *const filename) const {
56719       return _save_dlm(0,filename);
56720     }
56721 
56722     //! Save image as a DLM file \overloading.
56723     const CImg<T>& save_dlm(std::FILE *const file) const {
56724       return _save_dlm(file,0);
56725     }
56726 
56727     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
56728       if (!file && !filename)
56729         throw CImgArgumentException(_cimg_instance
56730                                     "save_dlm(): Specified filename is (null).",
56731                                     cimg_instance);
56732       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56733       if (_depth>1)
56734         cimg::warn(_cimg_instance
56735                    "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
56736                    cimg_instance,
56737                    filename?filename:"(FILE*)");
56738       if (_spectrum>1)
56739         cimg::warn(_cimg_instance
56740                    "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
56741                    cimg_instance,
56742                    filename?filename:"(FILE*)");
56743 
56744       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56745       const T* ptrs = _data;
56746       cimg_forYZC(*this,y,z,c) {
56747         cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
56748         std::fputc('\n',nfile);
56749       }
56750       if (!file) cimg::fclose(nfile);
56751       return *this;
56752     }
56753 
56754     //! Save image as a BMP file.
56755     /**
56756       \param filename Filename, as a C-string.
56757     **/
56758     const CImg<T>& save_bmp(const char *const filename) const {
56759       return _save_bmp(0,filename);
56760     }
56761 
56762     //! Save image as a BMP file \overloading.
56763     const CImg<T>& save_bmp(std::FILE *const file) const {
56764       return _save_bmp(file,0);
56765     }
56766 
56767     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
56768       if (!file && !filename)
56769         throw CImgArgumentException(_cimg_instance
56770                                     "save_bmp(): Specified filename is (null).",
56771                                     cimg_instance);
56772       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56773       if (_depth>1)
56774         cimg::warn(_cimg_instance
56775                    "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56776                    cimg_instance,
56777                    filename?filename:"(FILE*)");
56778       if (_spectrum>3)
56779         cimg::warn(_cimg_instance
56780                    "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
56781                    cimg_instance,
56782                    filename?filename:"(FILE*)");
56783 
56784       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56785       CImg<ucharT> header(54,1,1,1,0);
56786       unsigned char align_buf[4] = { 0 };
56787       const unsigned int
56788         align = (4 - (3*_width)%4)%4,
56789         buf_size = (3*_width + align)*height(),
56790         file_size = 54 + buf_size;
56791       header[0] = 'B'; header[1] = 'M';
56792       header[0x02] = file_size&0xFF;
56793       header[0x03] = (file_size>>8)&0xFF;
56794       header[0x04] = (file_size>>16)&0xFF;
56795       header[0x05] = (file_size>>24)&0xFF;
56796       header[0x0A] = 0x36;
56797       header[0x0E] = 0x28;
56798       header[0x12] = _width&0xFF;
56799       header[0x13] = (_width>>8)&0xFF;
56800       header[0x14] = (_width>>16)&0xFF;
56801       header[0x15] = (_width>>24)&0xFF;
56802       header[0x16] = _height&0xFF;
56803       header[0x17] = (_height>>8)&0xFF;
56804       header[0x18] = (_height>>16)&0xFF;
56805       header[0x19] = (_height>>24)&0xFF;
56806       header[0x1A] = 1;
56807       header[0x1B] = 0;
56808       header[0x1C] = 24;
56809       header[0x1D] = 0;
56810       header[0x22] = buf_size&0xFF;
56811       header[0x23] = (buf_size>>8)&0xFF;
56812       header[0x24] = (buf_size>>16)&0xFF;
56813       header[0x25] = (buf_size>>24)&0xFF;
56814       header[0x27] = 0x1;
56815       header[0x2B] = 0x1;
56816       cimg::fwrite(header._data,54,nfile);
56817 
56818       const T
56819         *ptr_r = data(0,_height - 1,0,0),
56820         *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
56821         *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
56822 
56823       switch (_spectrum) {
56824       case 1 : {
56825         cimg_forY(*this,y) {
56826           cimg_forX(*this,x) {
56827             const unsigned char val = (unsigned char)*(ptr_r++);
56828             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
56829           }
56830           cimg::fwrite(align_buf,align,nfile);
56831           ptr_r-=2*_width;
56832         }
56833       } break;
56834       case 2 : {
56835         cimg_forY(*this,y) {
56836           cimg_forX(*this,x) {
56837             std::fputc(0,nfile);
56838             std::fputc((unsigned char)(*(ptr_g++)),nfile);
56839             std::fputc((unsigned char)(*(ptr_r++)),nfile);
56840           }
56841           cimg::fwrite(align_buf,align,nfile);
56842           ptr_r-=2*_width; ptr_g-=2*_width;
56843         }
56844       } break;
56845       default : {
56846         cimg_forY(*this,y) {
56847           cimg_forX(*this,x) {
56848             std::fputc((unsigned char)(*(ptr_b++)),nfile);
56849             std::fputc((unsigned char)(*(ptr_g++)),nfile);
56850             std::fputc((unsigned char)(*(ptr_r++)),nfile);
56851           }
56852           cimg::fwrite(align_buf,align,nfile);
56853           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
56854         }
56855       }
56856       }
56857       if (!file) cimg::fclose(nfile);
56858       return *this;
56859     }
56860 
56861     //! Save image as a JPEG file.
56862     /**
56863       \param filename Filename, as a C-string.
56864       \param quality Image quality (in %)
56865     **/
56866     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
56867       return _save_jpeg(0,filename,quality);
56868     }
56869 
56870     //! Save image as a JPEG file \overloading.
56871     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
56872       return _save_jpeg(file,0,quality);
56873     }
56874 
56875     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
56876       if (!file && !filename)
56877         throw CImgArgumentException(_cimg_instance
56878                                     "save_jpeg(): Specified filename is (null).",
56879                                     cimg_instance);
56880       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56881       if (_depth>1)
56882         cimg::warn(_cimg_instance
56883                    "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56884                    cimg_instance,
56885                    filename?filename:"(FILE*)");
56886 
56887 #ifndef cimg_use_jpeg
56888       if (!file) return save_other(filename,quality);
56889       else throw CImgIOException(_cimg_instance
56890                                  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
56891                                  cimg_instance);
56892 #else
56893       unsigned int dimbuf = 0;
56894       J_COLOR_SPACE colortype = JCS_RGB;
56895 
56896       switch (_spectrum) {
56897       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
56898       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
56899       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
56900       default : dimbuf = 4; colortype = JCS_CMYK; break;
56901       }
56902 
56903       // Call libjpeg functions
56904       struct jpeg_compress_struct cinfo;
56905       struct jpeg_error_mgr jerr;
56906       cinfo.err = jpeg_std_error(&jerr);
56907       jpeg_create_compress(&cinfo);
56908       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56909       jpeg_stdio_dest(&cinfo,nfile);
56910       cinfo.image_width = _width;
56911       cinfo.image_height = _height;
56912       cinfo.input_components = dimbuf;
56913       cinfo.in_color_space = colortype;
56914       jpeg_set_defaults(&cinfo);
56915       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
56916       jpeg_start_compress(&cinfo,TRUE);
56917 
56918       JSAMPROW row_pointer[1];
56919       CImg<ucharT> buffer(_width*dimbuf);
56920 
56921       while (cinfo.next_scanline<cinfo.image_height) {
56922         unsigned char *ptrd = buffer._data;
56923 
56924         // Fill pixel buffer
56925         switch (_spectrum) {
56926         case 1 : { // Greyscale images
56927           const T *ptr_g = data(0, cinfo.next_scanline);
56928           for (unsigned int b = 0; b<cinfo.image_width; b++)
56929             *(ptrd++) = (unsigned char)*(ptr_g++);
56930         } break;
56931         case 2 : { // RG images
56932           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56933             *ptr_g = data(0,cinfo.next_scanline,0,1);
56934           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56935             *(ptrd++) = (unsigned char)*(ptr_r++);
56936             *(ptrd++) = (unsigned char)*(ptr_g++);
56937             *(ptrd++) = 0;
56938           }
56939         } break;
56940         case 3 : { // RGB images
56941           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56942             *ptr_g = data(0,cinfo.next_scanline,0,1),
56943             *ptr_b = data(0,cinfo.next_scanline,0,2);
56944           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56945             *(ptrd++) = (unsigned char)*(ptr_r++);
56946             *(ptrd++) = (unsigned char)*(ptr_g++);
56947             *(ptrd++) = (unsigned char)*(ptr_b++);
56948           }
56949         } break;
56950         default : { // CMYK images
56951           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56952             *ptr_g = data(0,cinfo.next_scanline,0,1),
56953             *ptr_b = data(0,cinfo.next_scanline,0,2),
56954             *ptr_a = data(0,cinfo.next_scanline,0,3);
56955           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56956             *(ptrd++) = (unsigned char)*(ptr_r++);
56957             *(ptrd++) = (unsigned char)*(ptr_g++);
56958             *(ptrd++) = (unsigned char)*(ptr_b++);
56959             *(ptrd++) = (unsigned char)*(ptr_a++);
56960           }
56961         }
56962         }
56963         *row_pointer = buffer._data;
56964         jpeg_write_scanlines(&cinfo,row_pointer,1);
56965       }
56966       jpeg_finish_compress(&cinfo);
56967       if (!file) cimg::fclose(nfile);
56968       jpeg_destroy_compress(&cinfo);
56969       return *this;
56970 #endif
56971     }
56972 
56973     //! Save image, using built-in ImageMagick++ library.
56974     /**
56975       \param filename Filename, as a C-string.
56976       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
56977     **/
56978     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
56979       if (!filename)
56980         throw CImgArgumentException(_cimg_instance
56981                                     "save_magick(): Specified filename is (null).",
56982                                     cimg_instance);
56983       if (is_empty()) { cimg::fempty(0,filename); return *this; }
56984 
56985 #ifdef cimg_use_magick
56986       double stmin, stmax = (double)max_min(stmin);
56987       if (_depth>1)
56988         cimg::warn(_cimg_instance
56989                    "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56990                    cimg_instance,
56991                    filename);
56992 
56993       if (_spectrum>3)
56994         cimg::warn(_cimg_instance
56995                    "save_magick(): Instance is multispectral, only the three first channels will be "
56996                    "saved in file '%s'.",
56997                    cimg_instance,
56998                    filename);
56999 
57000       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57001         cimg::warn(_cimg_instance
57002                    "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57003                    cimg_instance,
57004                    filename,stmin,stmax);
57005 
57006       Magick::Image image(Magick::Geometry(_width,_height),"black");
57007       image.type(Magick::TrueColorType);
57008       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
57009       const T
57010         *ptr_r = data(0,0,0,0),
57011         *ptr_g = _spectrum>1?data(0,0,0,1):0,
57012         *ptr_b = _spectrum>2?data(0,0,0,2):0;
57013       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
57014       switch (_spectrum) {
57015       case 1 : // Scalar images
57016         for (ulongT off = (ulongT)_width*_height; off; --off) {
57017           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
57018           ++pixels;
57019         }
57020         break;
57021       case 2 : // RG images
57022         for (ulongT off = (ulongT)_width*_height; off; --off) {
57023           pixels->red = (Magick::Quantum)*(ptr_r++);
57024           pixels->green = (Magick::Quantum)*(ptr_g++);
57025           pixels->blue = 0; ++pixels;
57026         }
57027         break;
57028       default : // RGB images
57029         for (ulongT off = (ulongT)_width*_height; off; --off) {
57030           pixels->red = (Magick::Quantum)*(ptr_r++);
57031           pixels->green = (Magick::Quantum)*(ptr_g++);
57032           pixels->blue = (Magick::Quantum)*(ptr_b++);
57033           ++pixels;
57034         }
57035       }
57036       image.syncPixels();
57037       image.write(filename);
57038       return *this;
57039 #else
57040       cimg::unused(bytes_per_pixel);
57041       throw CImgIOException(_cimg_instance
57042                             "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
57043                             cimg_instance,
57044                             filename);
57045 #endif
57046     }
57047 
57048     //! Save image as a PNG file.
57049     /**
57050        \param filename Filename, as a C-string.
57051        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
57052     **/
57053     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
57054       return _save_png(0,filename,bytes_per_pixel);
57055     }
57056 
57057     //! Save image as a PNG file \overloading.
57058     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
57059       return _save_png(file,0,bytes_per_pixel);
57060     }
57061 
57062     const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
57063                              const unsigned int bytes_per_pixel=0) const {
57064       if (!file && !filename)
57065         throw CImgArgumentException(_cimg_instance
57066                                     "save_png(): Specified filename is (null).",
57067                                     cimg_instance);
57068       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57069 
57070 #ifndef cimg_use_png
57071       cimg::unused(bytes_per_pixel);
57072       if (!file) return save_other(filename);
57073       else throw CImgIOException(_cimg_instance
57074                                  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
57075                                  cimg_instance);
57076 #else
57077 
57078 #if defined __GNUC__
57079       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
57080       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
57081       volatile double stmin, stmax = (double)max_min(stmin);
57082 #else
57083       const char *nfilename = filename;
57084       std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
57085       double stmin, stmax = (double)max_min(stmin);
57086 #endif
57087 
57088       if (_depth>1)
57089         cimg::warn(_cimg_instance
57090                    "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57091                    cimg_instance,
57092                    filename);
57093 
57094       if (_spectrum>4)
57095         cimg::warn(_cimg_instance
57096                    "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
57097                    cimg_instance,
57098                    filename);
57099 
57100       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57101         cimg::warn(_cimg_instance
57102                    "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57103                    cimg_instance,
57104                    filename,stmin,stmax);
57105 
57106       // Setup PNG structures for write
57107       png_voidp user_error_ptr = 0;
57108       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
57109       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
57110                                                     user_warning_fn);
57111       if (!png_ptr){
57112         if (!file) cimg::fclose(nfile);
57113         throw CImgIOException(_cimg_instance
57114                               "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
57115                               cimg_instance,
57116                               nfilename?nfilename:"(FILE*)");
57117       }
57118       png_infop info_ptr = png_create_info_struct(png_ptr);
57119       if (!info_ptr) {
57120         png_destroy_write_struct(&png_ptr,(png_infopp)0);
57121         if (!file) cimg::fclose(nfile);
57122         throw CImgIOException(_cimg_instance
57123                               "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
57124                               cimg_instance,
57125                               nfilename?nfilename:"(FILE*)");
57126       }
57127       if (setjmp(png_jmpbuf(png_ptr))) {
57128         png_destroy_write_struct(&png_ptr, &info_ptr);
57129         if (!file) cimg::fclose(nfile);
57130         throw CImgIOException(_cimg_instance
57131                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
57132                               cimg_instance,
57133                               nfilename?nfilename:"(FILE*)");
57134       }
57135       png_init_io(png_ptr, nfile);
57136 
57137       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
57138 
57139       int color_type;
57140       switch (spectrum()) {
57141       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
57142       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
57143       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
57144       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
57145       }
57146       const int interlace_type = PNG_INTERLACE_NONE;
57147       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
57148       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
57149       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
57150       png_write_info(png_ptr,info_ptr);
57151       const int byte_depth = bit_depth>>3;
57152       const int numChan = spectrum()>4?4:spectrum();
57153       const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
57154 
57155       // Allocate Memory for Image Save and Fill pixel data
57156       png_bytep *const imgData = new png_byte*[_height];
57157       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
57158       const T *pC0 = data(0,0,0,0);
57159       switch (pixel_bit_depth_flag) {
57160       case 7 :  { // Gray 8-bit
57161         cimg_forY(*this,y) {
57162           unsigned char *ptrd = imgData[y];
57163           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
57164         }
57165       } break;
57166       case 14 : { // Gray w/ Alpha 8-bit
57167         const T *pC1 = data(0,0,0,1);
57168         cimg_forY(*this,y) {
57169           unsigned char *ptrd = imgData[y];
57170           cimg_forX(*this,x) {
57171             *(ptrd++) = (unsigned char)*(pC0++);
57172             *(ptrd++) = (unsigned char)*(pC1++);
57173           }
57174         }
57175       } break;
57176       case 21 :  { // RGB 8-bit
57177         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
57178         cimg_forY(*this,y) {
57179           unsigned char *ptrd = imgData[y];
57180           cimg_forX(*this,x) {
57181             *(ptrd++) = (unsigned char)*(pC0++);
57182             *(ptrd++) = (unsigned char)*(pC1++);
57183             *(ptrd++) = (unsigned char)*(pC2++);
57184           }
57185         }
57186       } break;
57187       case 28 : { // RGB x/ Alpha 8-bit
57188         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
57189         cimg_forY(*this,y){
57190           unsigned char *ptrd = imgData[y];
57191           cimg_forX(*this,x){
57192             *(ptrd++) = (unsigned char)*(pC0++);
57193             *(ptrd++) = (unsigned char)*(pC1++);
57194             *(ptrd++) = (unsigned char)*(pC2++);
57195             *(ptrd++) = (unsigned char)*(pC3++);
57196           }
57197         }
57198       } break;
57199       case 15 : { // Gray 16-bit
57200         cimg_forY(*this,y){
57201           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57202           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
57203           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
57204         }
57205       } break;
57206       case 30 : { // Gray w/ Alpha 16-bit
57207         const T *pC1 = data(0,0,0,1);
57208         cimg_forY(*this,y){
57209           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57210           cimg_forX(*this,x) {
57211             *(ptrd++) = (unsigned short)*(pC0++);
57212             *(ptrd++) = (unsigned short)*(pC1++);
57213           }
57214           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
57215         }
57216       } break;
57217       case 45 : { // RGB 16-bit
57218         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
57219         cimg_forY(*this,y) {
57220           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57221           cimg_forX(*this,x) {
57222             *(ptrd++) = (unsigned short)*(pC0++);
57223             *(ptrd++) = (unsigned short)*(pC1++);
57224             *(ptrd++) = (unsigned short)*(pC2++);
57225           }
57226           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
57227         }
57228       } break;
57229       case 60 : { // RGB w/ Alpha 16-bit
57230         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
57231         cimg_forY(*this,y) {
57232           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57233           cimg_forX(*this,x) {
57234             *(ptrd++) = (unsigned short)*(pC0++);
57235             *(ptrd++) = (unsigned short)*(pC1++);
57236             *(ptrd++) = (unsigned short)*(pC2++);
57237             *(ptrd++) = (unsigned short)*(pC3++);
57238           }
57239           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
57240         }
57241       } break;
57242       default :
57243         if (!file) cimg::fclose(nfile);
57244         throw CImgIOException(_cimg_instance
57245                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
57246                               cimg_instance,
57247                               nfilename?nfilename:"(FILE*)");
57248       }
57249       png_write_image(png_ptr,imgData);
57250       png_write_end(png_ptr,info_ptr);
57251       png_destroy_write_struct(&png_ptr, &info_ptr);
57252 
57253       // Deallocate Image Write Memory
57254       cimg_forY(*this,n) delete[] imgData[n];
57255       delete[] imgData;
57256 
57257       if (!file) cimg::fclose(nfile);
57258       return *this;
57259 #endif
57260     }
57261 
57262     //! Save image as a PNM file.
57263     /**
57264       \param filename Filename, as a C-string.
57265       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
57266     **/
57267     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
57268       return _save_pnm(0,filename,bytes_per_pixel);
57269     }
57270 
57271     //! Save image as a PNM file \overloading.
57272     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
57273       return _save_pnm(file,0,bytes_per_pixel);
57274     }
57275 
57276     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
57277                              const unsigned int bytes_per_pixel=0) const {
57278       if (!file && !filename)
57279         throw CImgArgumentException(_cimg_instance
57280                                     "save_pnm(): Specified filename is (null).",
57281                                     cimg_instance);
57282       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57283 
57284       double stmin, stmax = (double)max_min(stmin);
57285       if (_depth>1)
57286         cimg::warn(_cimg_instance
57287                    "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57288                    cimg_instance,
57289                    filename?filename:"(FILE*)");
57290       if (_spectrum>3)
57291         cimg::warn(_cimg_instance
57292                    "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
57293                    cimg_instance,
57294                    filename?filename:"(FILE*)");
57295       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57296         cimg::warn(_cimg_instance
57297                    "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57298                    cimg_instance,
57299                    stmin,stmax,filename?filename:"(FILE*)");
57300 
57301       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57302       const T
57303         *ptr_r = data(0,0,0,0),
57304         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
57305         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
57306       const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
57307 
57308       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
57309                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
57310 
57311       switch (_spectrum) {
57312       case 1 : { // Scalar image
57313         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
57314           CImg<ucharT> buf((unsigned int)buf_size);
57315           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57316             const ulongT N = std::min((ulongT)to_write,buf_size);
57317             unsigned char *ptrd = buf._data;
57318             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
57319             cimg::fwrite(buf._data,N,nfile);
57320             to_write-=N;
57321           }
57322         } else { // Binary PGM 16 bits
57323           CImg<ushortT> buf((unsigned int)buf_size);
57324           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57325             const ulongT N = std::min((ulongT)to_write,buf_size);
57326             unsigned short *ptrd = buf._data;
57327             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
57328             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57329             cimg::fwrite(buf._data,N,nfile);
57330             to_write-=N;
57331           }
57332         }
57333       } break;
57334       case 2 : { // RG image
57335         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
57336           CImg<ucharT> buf((unsigned int)buf_size);
57337           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57338             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57339             unsigned char *ptrd = buf._data;
57340             for (ulongT i = N; i>0; --i) {
57341               *(ptrd++) = (unsigned char)*(ptr_r++);
57342               *(ptrd++) = (unsigned char)*(ptr_g++);
57343               *(ptrd++) = 0;
57344             }
57345             cimg::fwrite(buf._data,3*N,nfile);
57346             to_write-=N;
57347           }
57348         } else {             // Binary PPM 16 bits
57349           CImg<ushortT> buf((unsigned int)buf_size);
57350           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57351             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57352             unsigned short *ptrd = buf._data;
57353             for (ulongT i = N; i>0; --i) {
57354               *(ptrd++) = (unsigned short)*(ptr_r++);
57355               *(ptrd++) = (unsigned short)*(ptr_g++);
57356               *(ptrd++) = 0;
57357             }
57358             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57359             cimg::fwrite(buf._data,3*N,nfile);
57360             to_write-=N;
57361           }
57362         }
57363       } break;
57364       default : { // RGB image
57365         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
57366           CImg<ucharT> buf((unsigned int)buf_size);
57367           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57368             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57369             unsigned char *ptrd = buf._data;
57370             for (ulongT i = N; i>0; --i) {
57371               *(ptrd++) = (unsigned char)*(ptr_r++);
57372               *(ptrd++) = (unsigned char)*(ptr_g++);
57373               *(ptrd++) = (unsigned char)*(ptr_b++);
57374             }
57375             cimg::fwrite(buf._data,3*N,nfile);
57376             to_write-=N;
57377           }
57378         } else { // Binary PPM 16 bits
57379           CImg<ushortT> buf((unsigned int)buf_size);
57380           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57381             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57382             unsigned short *ptrd = buf._data;
57383             for (ulongT i = N; i>0; --i) {
57384               *(ptrd++) = (unsigned short)*(ptr_r++);
57385               *(ptrd++) = (unsigned short)*(ptr_g++);
57386               *(ptrd++) = (unsigned short)*(ptr_b++);
57387             }
57388             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57389             cimg::fwrite(buf._data,3*N,nfile);
57390             to_write-=N;
57391           }
57392         }
57393       }
57394       }
57395       if (!file) cimg::fclose(nfile);
57396       return *this;
57397     }
57398 
57399     //! Save image as a PNK file.
57400     /**
57401       \param filename Filename, as a C-string.
57402     **/
57403     const CImg<T>& save_pnk(const char *const filename) const {
57404       return _save_pnk(0,filename);
57405     }
57406 
57407     //! Save image as a PNK file \overloading.
57408     const CImg<T>& save_pnk(std::FILE *const file) const {
57409       return _save_pnk(file,0);
57410     }
57411 
57412     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
57413       if (!file && !filename)
57414         throw CImgArgumentException(_cimg_instance
57415                                     "save_pnk(): Specified filename is (null).",
57416                                     cimg_instance);
57417       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57418       if (_spectrum>1)
57419         cimg::warn(_cimg_instance
57420                    "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
57421                    cimg_instance,
57422                    filename?filename:"(FILE*)");
57423 
57424       const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
57425       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57426       const T *ptr = data(0,0,0,0);
57427 
57428       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file
57429         _save_pnm(file,filename,0);
57430       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D
57431         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
57432         CImg<ucharT> buf((unsigned int)buf_size);
57433         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
57434           const ulongT N = std::min((ulongT)to_write,buf_size);
57435           unsigned char *ptrd = buf._data;
57436           for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
57437           cimg::fwrite(buf._data,N,nfile);
57438           to_write-=N;
57439         }
57440       } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D
57441         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
57442         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
57443         CImg<intT> buf((unsigned int)buf_size);
57444         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
57445           const ulongT N = std::min((ulongT)to_write,buf_size);
57446           int *ptrd = buf._data;
57447           for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
57448           cimg::fwrite(buf._data,N,nfile);
57449           to_write-=N;
57450         }
57451       } else { // Save as P9: Binary float-valued 3D
57452         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
57453         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
57454         CImg<floatT> buf((unsigned int)buf_size);
57455         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
57456           const ulongT N = std::min((ulongT)to_write,buf_size);
57457           float *ptrd = buf._data;
57458           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
57459           cimg::fwrite(buf._data,N,nfile);
57460           to_write-=N;
57461         }
57462       }
57463 
57464       if (!file) cimg::fclose(nfile);
57465       return *this;
57466     }
57467 
57468     //! Save image as a PFM file.
57469     /**
57470       \param filename Filename, as a C-string.
57471     **/
57472     const CImg<T>& save_pfm(const char *const filename) const {
57473       get_mirror('y')._save_pfm(0,filename);
57474       return *this;
57475     }
57476 
57477     //! Save image as a PFM file \overloading.
57478     const CImg<T>& save_pfm(std::FILE *const file) const {
57479       get_mirror('y')._save_pfm(file,0);
57480       return *this;
57481     }
57482 
57483     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
57484       if (!file && !filename)
57485         throw CImgArgumentException(_cimg_instance
57486                                     "save_pfm(): Specified filename is (null).",
57487                                     cimg_instance);
57488       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57489       if (_depth>1)
57490         cimg::warn(_cimg_instance
57491                    "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57492                    cimg_instance,
57493                    filename?filename:"(FILE*)");
57494       if (_spectrum>3)
57495         cimg::warn(_cimg_instance
57496                    "save_pfm(): image instance is multispectral, only the three first channels will be saved "
57497                    "in file '%s'.",
57498                    cimg_instance,
57499                    filename?filename:"(FILE*)");
57500 
57501       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57502       const T
57503         *ptr_r = data(0,0,0,0),
57504         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
57505         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
57506       const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
57507 
57508       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
57509                    (_spectrum==1?'f':'F'),_width,_height);
57510 
57511       switch (_spectrum) {
57512       case 1 : { // Scalar image
57513         CImg<floatT> buf(buf_size);
57514         for (longT to_write = (longT)width()*height(); to_write>0; ) {
57515           const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
57516           float *ptrd = buf._data;
57517           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
57518           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57519           cimg::fwrite(buf._data,N,nfile);
57520           to_write-=N;
57521         }
57522       } break;
57523       case 2 : { // RG image
57524         CImg<floatT> buf(buf_size);
57525         for (longT to_write = (longT)width()*height(); to_write>0; ) {
57526           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
57527           float *ptrd = buf._data;
57528           for (ulongT i = N; i>0; --i) {
57529             *(ptrd++) = (float)*(ptr_r++);
57530             *(ptrd++) = (float)*(ptr_g++);
57531             *(ptrd++) = 0;
57532           }
57533           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57534           cimg::fwrite(buf._data,3*N,nfile);
57535           to_write-=N;
57536         }
57537       } break;
57538       default : { // RGB image
57539         CImg<floatT> buf(buf_size);
57540         for (longT to_write = (longT)width()*height(); to_write>0; ) {
57541           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
57542           float *ptrd = buf._data;
57543           for (ulongT i = N; i>0; --i) {
57544             *(ptrd++) = (float)*(ptr_r++);
57545             *(ptrd++) = (float)*(ptr_g++);
57546             *(ptrd++) = (float)*(ptr_b++);
57547           }
57548           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57549           cimg::fwrite(buf._data,3*N,nfile);
57550           to_write-=N;
57551         }
57552       }
57553       }
57554       if (!file) cimg::fclose(nfile);
57555       return *this;
57556     }
57557 
57558     //! Save image as a RGB file.
57559     /**
57560       \param filename Filename, as a C-string.
57561     **/
57562     const CImg<T>& save_rgb(const char *const filename) const {
57563       return _save_rgb(0,filename);
57564     }
57565 
57566     //! Save image as a RGB file \overloading.
57567     const CImg<T>& save_rgb(std::FILE *const file) const {
57568       return _save_rgb(file,0);
57569     }
57570 
57571     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
57572       if (!file && !filename)
57573         throw CImgArgumentException(_cimg_instance
57574                                     "save_rgb(): Specified filename is (null).",
57575                                     cimg_instance);
57576       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57577       if (_spectrum!=3)
57578         cimg::warn(_cimg_instance
57579                    "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
57580                    cimg_instance,
57581                    filename?filename:"(FILE*)");
57582 
57583       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57584       const ulongT wh = (ulongT)_width*_height;
57585       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
57586       const T
57587         *ptr1 = data(0,0,0,0),
57588         *ptr2 = _spectrum>1?data(0,0,0,1):0,
57589         *ptr3 = _spectrum>2?data(0,0,0,2):0;
57590       switch (_spectrum) {
57591       case 1 : { // Scalar image
57592         for (ulongT k = 0; k<wh; ++k) {
57593           const unsigned char val = (unsigned char)*(ptr1++);
57594           *(nbuffer++) = val;
57595           *(nbuffer++) = val;
57596           *(nbuffer++) = val;
57597         }
57598       } break;
57599       case 2 : { // RG image
57600         for (ulongT k = 0; k<wh; ++k) {
57601           *(nbuffer++) = (unsigned char)(*(ptr1++));
57602           *(nbuffer++) = (unsigned char)(*(ptr2++));
57603           *(nbuffer++) = 0;
57604         }
57605       } break;
57606       default : { // RGB image
57607         for (ulongT k = 0; k<wh; ++k) {
57608           *(nbuffer++) = (unsigned char)(*(ptr1++));
57609           *(nbuffer++) = (unsigned char)(*(ptr2++));
57610           *(nbuffer++) = (unsigned char)(*(ptr3++));
57611         }
57612       }
57613       }
57614       cimg::fwrite(buffer,3*wh,nfile);
57615       if (!file) cimg::fclose(nfile);
57616       delete[] buffer;
57617       return *this;
57618     }
57619 
57620     //! Save image as a RGBA file.
57621     /**
57622        \param filename Filename, as a C-string.
57623     **/
57624     const CImg<T>& save_rgba(const char *const filename) const {
57625       return _save_rgba(0,filename);
57626     }
57627 
57628     //! Save image as a RGBA file \overloading.
57629     const CImg<T>& save_rgba(std::FILE *const file) const {
57630       return _save_rgba(file,0);
57631     }
57632 
57633     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
57634       if (!file && !filename)
57635         throw CImgArgumentException(_cimg_instance
57636                                     "save_rgba(): Specified filename is (null).",
57637                                     cimg_instance);
57638       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57639       if (_spectrum!=4)
57640         cimg::warn(_cimg_instance
57641                    "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
57642                    cimg_instance,
57643                    filename?filename:"(FILE*)");
57644 
57645       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57646       const ulongT wh = (ulongT)_width*_height;
57647       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
57648       const T
57649         *ptr1 = data(0,0,0,0),
57650         *ptr2 = _spectrum>1?data(0,0,0,1):0,
57651         *ptr3 = _spectrum>2?data(0,0,0,2):0,
57652         *ptr4 = _spectrum>3?data(0,0,0,3):0;
57653       switch (_spectrum) {
57654       case 1 : { // Scalar images
57655         for (ulongT k = 0; k<wh; ++k) {
57656           const unsigned char val = (unsigned char)*(ptr1++);
57657           *(nbuffer++) = val;
57658           *(nbuffer++) = val;
57659           *(nbuffer++) = val;
57660           *(nbuffer++) = 255;
57661         }
57662       } break;
57663       case 2 : { // RG images
57664         for (ulongT k = 0; k<wh; ++k) {
57665           *(nbuffer++) = (unsigned char)(*(ptr1++));
57666           *(nbuffer++) = (unsigned char)(*(ptr2++));
57667           *(nbuffer++) = 0;
57668           *(nbuffer++) = 255;
57669         }
57670       } break;
57671       case 3 : { // RGB images
57672         for (ulongT k = 0; k<wh; ++k) {
57673           *(nbuffer++) = (unsigned char)(*(ptr1++));
57674           *(nbuffer++) = (unsigned char)(*(ptr2++));
57675           *(nbuffer++) = (unsigned char)(*(ptr3++));
57676           *(nbuffer++) = 255;
57677         }
57678       } break;
57679       default : { // RGBA images
57680         for (ulongT k = 0; k<wh; ++k) {
57681           *(nbuffer++) = (unsigned char)(*(ptr1++));
57682           *(nbuffer++) = (unsigned char)(*(ptr2++));
57683           *(nbuffer++) = (unsigned char)(*(ptr3++));
57684           *(nbuffer++) = (unsigned char)(*(ptr4++));
57685         }
57686       }
57687       }
57688       cimg::fwrite(buffer,4*wh,nfile);
57689       if (!file) cimg::fclose(nfile);
57690       delete[] buffer;
57691       return *this;
57692     }
57693 
57694     //! Save image as a TIFF file.
57695     /**
57696        \param filename Filename, as a C-string.
57697        \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
57698        \param[out] voxel_size Voxel size, to be stored in the filename.
57699        \param[out] description Description, to be stored in the filename.
57700        \param use_bigtiff Allow to save big tiff files (>4Gb).
57701        \note
57702        - libtiff support is enabled by defining the precompilation
57703         directive \c cimg_use_tif.
57704        - When libtiff is enabled, 2D and 3D (multipage) several
57705         channel per pixel are supported for
57706         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
57707        - If \c cimg_use_tiff is not defined at compile time the
57708         function uses CImg<T>&save_other(const char*).
57709      **/
57710     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
57711 
57712                              const float *const voxel_size=0, const char *const description=0,
57713                              const bool use_bigtiff=true) const {
57714       if (!filename)
57715         throw CImgArgumentException(_cimg_instance
57716                                     "save_tiff(): Specified filename is (null).",
57717                                     cimg_instance);
57718       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57719 
57720 #ifdef cimg_use_tiff
57721       const bool
57722         _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images
57723       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
57724       if (tif) {
57725         cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
57726         TIFFClose(tif);
57727       } else throw CImgIOException(_cimg_instance
57728                                    "save_tiff(): Failed to open file '%s' for writing.",
57729                                    cimg_instance,
57730                                    filename);
57731       return *this;
57732 #else
57733       cimg::unused(compression_type,voxel_size,description,use_bigtiff);
57734       return save_other(filename);
57735 #endif
57736     }
57737 
57738 #ifdef cimg_use_tiff
57739 
57740 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
57741       const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
57742 
57743     // [internal] Save a plane into a tiff file
57744     template<typename t>
57745     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
57746                               const unsigned int compression_type, const float *const voxel_size,
57747                               const char *const description) const {
57748       if (is_empty() || !tif || pixel_t) return *this;
57749       const char *const filename = TIFFFileName(tif);
57750       uint32_t rowsperstrip = (uint32_t)-1;
57751       uint16_t spp = _spectrum, bpp = sizeof(t)*8, photometric;
57752       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
57753       else photometric = PHOTOMETRIC_MINISBLACK;
57754       TIFFSetDirectory(tif,directory);
57755       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
57756       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
57757       if (voxel_size) {
57758         const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
57759         TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
57760         TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx);
57761         TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy);
57762         CImg<charT> s_description(256);
57763         cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
57764         TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
57765       }
57766       if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
57767       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
57768       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
57769       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
57770       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
57771       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
57772       double valm, valM = max_min(valm);
57773       TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
57774       TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
57775       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
57776       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
57777       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
57778       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
57779                    compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
57780       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
57781       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
57782       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
57783       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
57784 
57785       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
57786       if (buf) {
57787         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
57788           uint32_t nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
57789           tstrip_t strip = TIFFComputeStrip(tif,row,0);
57790           tsize_t i = 0;
57791           for (unsigned int rr = 0; rr<nrow; ++rr)
57792             for (unsigned int cc = 0; cc<_width; ++cc)
57793               for (unsigned int vv = 0; vv<spp; ++vv)
57794                 buf[i++] = (t)(*this)(cc,row + rr,z,vv);
57795           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
57796             throw CImgIOException(_cimg_instance
57797                                   "save_tiff(): Invalid strip writing when saving file '%s'.",
57798                                   cimg_instance,
57799                                   filename?filename:"(FILE*)");
57800         }
57801         _TIFFfree(buf);
57802       }
57803       TIFFWriteDirectory(tif);
57804       return *this;
57805     }
57806 
57807     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
57808                               const unsigned int compression_type, const float *const voxel_size,
57809                               const char *const description) const {
57810       _cimg_save_tiff("bool",unsigned char,compression_type);
57811       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
57812       _cimg_save_tiff("char",char,compression_type);
57813       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
57814       _cimg_save_tiff("short",short,compression_type);
57815       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
57816       _cimg_save_tiff("int",int,compression_type);
57817       _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
57818       _cimg_save_tiff("int64",int,compression_type);
57819       _cimg_save_tiff("float",float,compression_type);
57820       _cimg_save_tiff("double",float,compression_type);
57821       const char *const filename = TIFFFileName(tif);
57822       throw CImgInstanceException(_cimg_instance
57823                                   "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
57824                                   cimg_instance,
57825                                   pixel_type(),filename?filename:"(FILE*)");
57826       return *this;
57827     }
57828 #endif
57829 
57830     //! Save image as a MINC2 file.
57831     /**
57832        \param filename Filename, as a C-string.
57833        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
57834     **/
57835     const CImg<T>& save_minc2(const char *const filename,
57836                               const char *const imitate_file=0) const {
57837       if (!filename)
57838         throw CImgArgumentException(_cimg_instance
57839                                    "save_minc2(): Specified filename is (null).",
57840                                    cimg_instance);
57841       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57842 
57843 #ifndef cimg_use_minc2
57844      cimg::unused(imitate_file);
57845      return save_other(filename);
57846 #else
57847      minc::minc_1_writer wtr;
57848      if (imitate_file)
57849        wtr.open(filename, imitate_file);
57850      else {
57851        minc::minc_info di;
57852        if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
57853        if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
57854        if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
57855        if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
57856        wtr.open(filename,di,1,NC_FLOAT,0);
57857      }
57858      if (pixel_type()==cimg::type<unsigned char>::string())
57859        wtr.setup_write_byte();
57860      else if (pixel_type()==cimg::type<int>::string())
57861        wtr.setup_write_int();
57862      else if (pixel_type()==cimg::type<double>::string())
57863        wtr.setup_write_double();
57864      else
57865        wtr.setup_write_float();
57866      minc::save_standard_volume(wtr, this->_data);
57867      return *this;
57868 #endif
57869     }
57870 
57871     //! Save image as an ANALYZE7.5 or NIFTI file.
57872     /**
57873       \param filename Filename, as a C-string.
57874       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
57875     **/
57876     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
57877       if (!filename)
57878         throw CImgArgumentException(_cimg_instance
57879                                     "save_analyze(): Specified filename is (null).",
57880                                     cimg_instance);
57881       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57882 
57883       std::FILE *file;
57884       CImg<charT> hname(1024), iname(1024);
57885       const char *const ext = cimg::split_filename(filename);
57886       short datatype = -1;
57887       if (!*ext) {
57888         cimg_snprintf(hname,hname._width,"%s.hdr",filename);
57889         cimg_snprintf(iname,iname._width,"%s.img",filename);
57890       }
57891       if (!cimg::strncasecmp(ext,"hdr",3)) {
57892         std::strcpy(hname,filename);
57893         std::strncpy(iname,filename,iname._width - 1);
57894         cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
57895       }
57896       if (!cimg::strncasecmp(ext,"img",3)) {
57897         std::strcpy(hname,filename);
57898         std::strncpy(iname,filename,iname._width - 1);
57899         cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
57900       }
57901       if (!cimg::strncasecmp(ext,"nii",3)) {
57902         std::strncpy(hname,filename,hname._width - 1); *iname = 0;
57903       }
57904 
57905       CImg<charT> header(*iname?348:352,1,1,1,0);
57906       int *const iheader = (int*)header._data;
57907       *iheader = 348;
57908       std::strcpy(header._data + 4,"CImg");
57909       std::strcpy(header._data + 14," ");
57910       ((short*)&(header[36]))[0] = 4096;
57911       ((char*)&(header[38]))[0] = 114;
57912       ((short*)&(header[40]))[0] = 4;
57913       ((short*)&(header[40]))[1] = (short)_width;
57914       ((short*)&(header[40]))[2] = (short)_height;
57915       ((short*)&(header[40]))[3] = (short)_depth;
57916       ((short*)&(header[40]))[4] = (short)_spectrum;
57917       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
57918       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
57919       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
57920       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
57921       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
57922       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
57923       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
57924       if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
57925       if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
57926       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
57927       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
57928       if (datatype<0)
57929         throw CImgIOException(_cimg_instance
57930                               "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
57931                               cimg_instance,
57932                               pixel_type(),filename);
57933 
57934       ((short*)&(header[70]))[0] = datatype;
57935       ((short*)&(header[72]))[0] = sizeof(T);
57936       ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
57937       ((float*)&(header[112]))[0] = 1;
57938       ((float*)&(header[76]))[0] = 0;
57939       if (voxel_size) {
57940         ((float*)&(header[76]))[1] = voxel_size[0];
57941         ((float*)&(header[76]))[2] = voxel_size[1];
57942         ((float*)&(header[76]))[3] = voxel_size[2];
57943       } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
57944       file = cimg::fopen(hname,"wb");
57945       cimg::fwrite(header._data,header.width(),file);
57946       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
57947       cimg::fwrite(_data,size(),file);
57948       cimg::fclose(file);
57949       return *this;
57950     }
57951 
57952     //! Save image as a .cimg file.
57953     /**
57954       \param filename Filename, as a C-string.
57955       \param is_compressed Tells if the file contains compressed image data.
57956     **/
57957     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
57958       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
57959       return *this;
57960     }
57961 
57962     //! Save image as a .cimg file \overloading.
57963     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
57964       CImgList<T>(*this,true).save_cimg(file,is_compressed);
57965       return *this;
57966     }
57967 
57968     //! Save image as a sub-image into an existing .cimg file.
57969     /**
57970       \param filename Filename, as a C-string.
57971       \param n0 Index of the image inside the file.
57972       \param x0 X-coordinate of the sub-image location.
57973       \param y0 Y-coordinate of the sub-image location.
57974       \param z0 Z-coordinate of the sub-image location.
57975       \param c0 C-coordinate of the sub-image location.
57976     **/
57977     const CImg<T>& save_cimg(const char *const filename,
57978                              const unsigned int n0,
57979                              const unsigned int x0, const unsigned int y0,
57980                              const unsigned int z0, const unsigned int c0) const {
57981       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
57982       return *this;
57983     }
57984 
57985     //! Save image as a sub-image into an existing .cimg file \overloading.
57986     const CImg<T>& save_cimg(std::FILE *const file,
57987                              const unsigned int n0,
57988                              const unsigned int x0, const unsigned int y0,
57989                              const unsigned int z0, const unsigned int c0) const {
57990       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
57991       return *this;
57992     }
57993 
57994     //! Save blank image as a .cimg file.
57995     /**
57996         \param filename Filename, as a C-string.
57997         \param dx Width of the image.
57998         \param dy Height of the image.
57999         \param dz Depth of the image.
58000         \param dc Number of channels of the image.
58001         \note
58002         - All pixel values of the saved image are set to \c 0.
58003         - Use this method to save large images without having to instantiate and allocate them.
58004     **/
58005     static void save_empty_cimg(const char *const filename,
58006                                 const unsigned int dx, const unsigned int dy=1,
58007                                 const unsigned int dz=1, const unsigned int dc=1) {
58008       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
58009     }
58010 
58011     //! Save blank image as a .cimg file \overloading.
58012     /**
58013        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
58014        with a file stream argument instead of a filename string.
58015     **/
58016     static void save_empty_cimg(std::FILE *const file,
58017                                 const unsigned int dx, const unsigned int dy=1,
58018                                 const unsigned int dz=1, const unsigned int dc=1) {
58019       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
58020     }
58021 
58022     //! Save image as an INRIMAGE-4 file.
58023     /**
58024       \param filename Filename, as a C-string.
58025       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
58026     **/
58027     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
58028       return _save_inr(0,filename,voxel_size);
58029     }
58030 
58031     //! Save image as an INRIMAGE-4 file \overloading.
58032     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
58033       return _save_inr(file,0,voxel_size);
58034     }
58035 
58036     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
58037       if (!file && !filename)
58038         throw CImgArgumentException(_cimg_instance
58039                                     "save_inr(): Specified filename is (null).",
58040                                     cimg_instance);
58041       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58042 
58043       int inrpixsize = -1;
58044       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
58045       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
58046         inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
58047       }
58048       if (!cimg::strcasecmp(pixel_type(),"char")) {
58049         inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
58050       }
58051       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
58052         inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
58053       }
58054       if (!cimg::strcasecmp(pixel_type(),"short")) {
58055         inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
58056       }
58057       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
58058         inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
58059       }
58060       if (!cimg::strcasecmp(pixel_type(),"int")) {
58061         inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
58062       }
58063       if (!cimg::strcasecmp(pixel_type(),"float")) {
58064         inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
58065       }
58066       if (!cimg::strcasecmp(pixel_type(),"double")) {
58067         inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
58068       }
58069       if (inrpixsize<=0)
58070         throw CImgIOException(_cimg_instance
58071                               "save_inr(): Unsupported pixel type '%s' for file '%s'",
58072                               cimg_instance,
58073                               pixel_type(),filename?filename:"(FILE*)");
58074 
58075       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58076       CImg<charT> header(257);
58077       int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
58078                               _width,_height,_depth,_spectrum);
58079       if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
58080                                         voxel_size[0],voxel_size[1],voxel_size[2]);
58081       err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
58082       std::memset(header._data + err,'\n',252 - err);
58083       std::memcpy(header._data + 252,"##}\n",4);
58084       cimg::fwrite(header._data,256,nfile);
58085       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
58086       if (!file) cimg::fclose(nfile);
58087       return *this;
58088     }
58089 
58090     //! Save image as an OpenEXR file.
58091     /**
58092        \param filename Filename, as a C-string.
58093        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
58094     **/
58095     const CImg<T>& save_exr(const char *const filename) const {
58096       if (!filename)
58097         throw CImgArgumentException(_cimg_instance
58098                                     "save_exr(): Specified filename is (null).",
58099                                     cimg_instance);
58100       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58101       if (_depth>1)
58102         cimg::warn(_cimg_instance
58103                    "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
58104                    cimg_instance,
58105                    filename);
58106 
58107 #ifndef cimg_use_openexr
58108       return save_other(filename);
58109 #else
58110       Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
58111       switch (_spectrum) {
58112       case 1 : { // Grayscale image
58113         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58114           rgba.r = (half)(*(ptr_r));
58115           rgba.g = (half)(*(ptr_r));
58116           rgba.b = (half)(*(ptr_r++));
58117           rgba.a = (half)1;
58118           *(ptrd++) = rgba;
58119         }
58120       } break;
58121       case 2 : { // RG image
58122         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
58123                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
58124           rgba.r = (half)(*(ptr_r++));
58125           rgba.g = (half)(*(ptr_g++));
58126           rgba.b = (half)0;
58127           rgba.a = (half)1;
58128           *(ptrd++) = rgba;
58129         }
58130       } break;
58131       case 3 : { // RGB image
58132         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
58133                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58134           rgba.r = (half)(*(ptr_r++));
58135           rgba.g = (half)(*(ptr_g++));
58136           rgba.b = (half)(*(ptr_b++));
58137           rgba.a = (half)1;
58138           *(ptrd++) = rgba;
58139         }
58140       } break;
58141       default : { // RGBA image
58142         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),
58143                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58144           rgba.r = (half)(*(ptr_r++));
58145           rgba.g = (half)(*(ptr_g++));
58146           rgba.b = (half)(*(ptr_b++));
58147           rgba.a = (half)(*(ptr_a++));
58148           *(ptrd++) = rgba;
58149         }
58150       } break;
58151       }
58152       Imf::RgbaOutputFile outFile(filename,_width,_height,
58153                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
58154                                   Imf::WRITE_RGB:Imf::WRITE_RGBA);
58155       outFile.setFrameBuffer(ptrd0,1,_width);
58156       outFile.writePixels(_height);
58157       delete[] ptrd0;
58158       return *this;
58159 #endif
58160     }
58161 
58162     //! Save image as a Pandore-5 file.
58163     /**
58164        \param filename Filename, as a C-string.
58165        \param colorspace Colorspace data field in output file
58166        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
58167        for more information).
58168     **/
58169     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
58170       return _save_pandore(0,filename,colorspace);
58171     }
58172 
58173     //! Save image as a Pandore-5 file \overloading.
58174     /**
58175         Same as save_pandore(const char *,unsigned int) const
58176         with a file stream argument instead of a filename string.
58177     **/
58178     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
58179       return _save_pandore(file,0,colorspace);
58180     }
58181 
58182     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
58183       unsigned int nbdims = 0;
58184       if (id==2 || id==3 || id==4) {
58185         dims[0] = 1; dims[1] = _width; nbdims = 2;
58186       }
58187       if (id==5 || id==6 || id==7) {
58188         dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
58189       }
58190       if (id==8 || id==9 || id==10) {
58191         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
58192       }
58193       if (id==16 || id==17 || id==18) {
58194         dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
58195       }
58196       if (id==19 || id==20 || id==21) {
58197         dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
58198       }
58199       if (id==22 || id==23 || id==25) {
58200         dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
58201       }
58202       if (id==26 || id==27 || id==29) {
58203         dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
58204       }
58205       if (id==30 || id==31 || id==33) {
58206         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
58207       }
58208       return nbdims;
58209     }
58210 
58211     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
58212                                  const unsigned int colorspace) const {
58213 
58214 #define __cimg_save_pandore_case(dtype) \
58215        dtype *buffer = new dtype[size()]; \
58216        const T *ptrs = _data; \
58217        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
58218        buffer-=size(); \
58219        cimg::fwrite(buffer,size(),nfile); \
58220        delete[] buffer
58221 
58222 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
58223       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
58224           (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
58225         unsigned int *iheader = (unsigned int*)(header + 12); \
58226         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
58227         cimg::fwrite(header,36,nfile); \
58228         if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
58229           for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \
58230           cimg::fwrite(ndims._data,nbdims,nfile); } \
58231         else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
58232           for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \
58233           cimg::fwrite(ndims._data,nbdims,nfile); } \
58234         else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
58235           for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \
58236           cimg::fwrite(ndims._data,nbdims,nfile); } \
58237         else throw CImgIOException(_cimg_instance \
58238                                    "save_pandore(): Unsupported datatype for file '%s'.",\
58239                                    cimg_instance, \
58240                                    filename?filename:"(FILE*)"); \
58241         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
58242           __cimg_save_pandore_case(unsigned char); \
58243         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
58244           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
58245           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
58246           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
58247           else throw CImgIOException(_cimg_instance \
58248                                      "save_pandore(): Unsupported datatype for file '%s'.",\
58249                                      cimg_instance, \
58250                                      filename?filename:"(FILE*)"); \
58251         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
58252           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
58253           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
58254           else throw CImgIOException(_cimg_instance \
58255                                      "save_pandore(): Unsupported datatype for file '%s'.",\
58256                                      cimg_instance, \
58257                                      filename?filename:"(FILE*)"); \
58258         } \
58259         saved = true; \
58260       }
58261 
58262       if (!file && !filename)
58263         throw CImgArgumentException(_cimg_instance
58264                                     "save_pandore(): Specified filename is (null).",
58265                                     cimg_instance);
58266       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58267 
58268       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58269       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
58270                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,
58271                                    'N','o',' ','d','a','t','e',0,0,0,0 };
58272       unsigned int nbdims, dims[5] = { 0 };
58273       bool saved = false;
58274       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
58275       _cimg_save_pandore_case(1,1,1,"char",3);
58276       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
58277       _cimg_save_pandore_case(1,1,1,"short",3);
58278       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
58279       _cimg_save_pandore_case(1,1,1,"int",3);
58280       _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
58281       _cimg_save_pandore_case(1,1,1,"int64",3);
58282       _cimg_save_pandore_case(1,1,1,"float",4);
58283       _cimg_save_pandore_case(1,1,1,"double",4);
58284 
58285       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
58286       _cimg_save_pandore_case(0,1,1,"char",6);
58287       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
58288       _cimg_save_pandore_case(0,1,1,"short",6);
58289       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
58290       _cimg_save_pandore_case(0,1,1,"int",6);
58291       _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
58292       _cimg_save_pandore_case(0,1,1,"int64",6);
58293       _cimg_save_pandore_case(0,1,1,"float",7);
58294       _cimg_save_pandore_case(0,1,1,"double",7);
58295 
58296       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
58297       _cimg_save_pandore_case(0,0,1,"char",9);
58298       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
58299       _cimg_save_pandore_case(0,0,1,"short",9);
58300       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
58301       _cimg_save_pandore_case(0,0,1,"int",9);
58302       _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
58303       _cimg_save_pandore_case(0,0,1,"int64",9);
58304       _cimg_save_pandore_case(0,0,1,"float",10);
58305       _cimg_save_pandore_case(0,0,1,"double",10);
58306 
58307       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
58308       _cimg_save_pandore_case(0,1,3,"char",17);
58309       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
58310       _cimg_save_pandore_case(0,1,3,"short",17);
58311       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
58312       _cimg_save_pandore_case(0,1,3,"int",17);
58313       _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
58314       _cimg_save_pandore_case(0,1,3,"int64",17);
58315       _cimg_save_pandore_case(0,1,3,"float",18);
58316       _cimg_save_pandore_case(0,1,3,"double",18);
58317 
58318       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
58319       _cimg_save_pandore_case(0,0,3,"char",20);
58320       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
58321       _cimg_save_pandore_case(0,0,3,"short",20);
58322       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
58323       _cimg_save_pandore_case(0,0,3,"int",20);
58324       _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
58325       _cimg_save_pandore_case(0,0,3,"int64",20);
58326       _cimg_save_pandore_case(0,0,3,"float",21);
58327       _cimg_save_pandore_case(0,0,3,"double",21);
58328 
58329       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
58330       _cimg_save_pandore_case(1,1,0,"char",23);
58331       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
58332       _cimg_save_pandore_case(1,1,0,"short",23);
58333       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
58334       _cimg_save_pandore_case(1,1,0,"int",23);
58335       _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
58336       _cimg_save_pandore_case(1,1,0,"int64",23);
58337       _cimg_save_pandore_case(1,1,0,"float",25);
58338       _cimg_save_pandore_case(1,1,0,"double",25);
58339 
58340       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
58341       _cimg_save_pandore_case(0,1,0,"char",27);
58342       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
58343       _cimg_save_pandore_case(0,1,0,"short",27);
58344       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
58345       _cimg_save_pandore_case(0,1,0,"int",27);
58346       _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
58347       _cimg_save_pandore_case(0,1,0,"int64",27);
58348       _cimg_save_pandore_case(0,1,0,"float",29);
58349       _cimg_save_pandore_case(0,1,0,"double",29);
58350 
58351       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
58352       _cimg_save_pandore_case(0,0,0,"char",31);
58353       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
58354       _cimg_save_pandore_case(0,0,0,"short",31);
58355       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
58356       _cimg_save_pandore_case(0,0,0,"int",31);
58357       _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
58358       _cimg_save_pandore_case(0,0,0,"int64",31);
58359       _cimg_save_pandore_case(0,0,0,"float",33);
58360       _cimg_save_pandore_case(0,0,0,"double",33);
58361 
58362       if (!file) cimg::fclose(nfile);
58363       return *this;
58364     }
58365 
58366     //! Save image as a raw data file.
58367     /**
58368        \param filename Filename, as a C-string.
58369        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
58370        \note The .raw format does not store the image dimensions in the output file,
58371        so you have to keep track of them somewhere to be able to read the file correctly afterwards.
58372     **/
58373     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
58374       return _save_raw(0,filename,is_multiplexed);
58375     }
58376 
58377     //! Save image as a raw data file \overloading.
58378     /**
58379        Same as save_raw(const char *,bool) const
58380        with a file stream argument instead of a filename string.
58381     **/
58382     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
58383       return _save_raw(file,0,is_multiplexed);
58384     }
58385 
58386     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
58387       if (!file && !filename)
58388         throw CImgArgumentException(_cimg_instance
58389                                     "save_raw(): Specified filename is (null).",
58390                                     cimg_instance);
58391       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58392 
58393       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58394       if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
58395         ulongT siz;
58396         const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
58397         cimg::fwrite(buf,siz,nfile);
58398         delete[] buf;
58399       } else { // Non boolean data
58400         if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
58401         else { // Multiplexed
58402           CImg<T> buf(_spectrum);
58403           cimg_forXYZ(*this,x,y,z) {
58404             cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
58405             cimg::fwrite(buf._data,_spectrum,nfile);
58406           }
58407         }
58408       }
58409       if (!file) cimg::fclose(nfile);
58410       return *this;
58411     }
58412 
58413     // Return unsigned char buffer that encodes data of a CImg<bool> instance bitwise.
58414     // (buffer needs to be deallocated afterwards, with delete[]).
58415     const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const {
58416       const ulongT _siz = size();
58417       siz = _siz/8 + (_siz%8?1:0);
58418       unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0;
58419 
58420       if (!is_multiplexed || _spectrum==1) // Non-multiplexed
58421         cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }}
58422       else // Multiplexed
58423         cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) {
58424           (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }
58425         }
58426       if (bit) *ptrd = val;
58427       return buf;
58428     }
58429 
58430     // Fill CImg<T> instance from bitwise data encoded in an unsigned char buffer.
58431     void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) {
58432       const ulongT S = std::min(siz*8,size());
58433       const unsigned char *ptrs = buf;
58434       unsigned char val = 0, mask = 0;
58435       T *ptrd = _data;
58436       if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed
58437         for (ulongT off = 0; off<S; ++off) {
58438           if (!(mask>>=1)) { val = *(ptrs++); mask = 128; }
58439           *(ptrd++) = (T)((val&mask)?1:0);
58440         }
58441       else if (S) { // Multiplexed
58442         ulongT off = 0;
58443         for (int z = 0; z<depth() && off<=S; ++z)
58444           for (int y = 0; y<height() && off<=S; ++y)
58445             for (int x = 0; x<width() && off<=S; ++x)
58446               for (int c = 0; c<spectrum() && off<=S; ++c) {
58447                 if (!(mask>>=1)) { val = *(ptrs++); ++off; mask = 128; }
58448                 (*this)(x,y,z,c) = (T)((val&mask)?1:0);
58449               }
58450       }
58451     }
58452 
58453     //! Save image as a .yuv video file.
58454     /**
58455        \param filename Filename, as a C-string.
58456        \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
58457        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
58458        \note Each slice of the instance image is considered to be a single frame of the output video file.
58459     **/
58460     const CImg<T>& save_yuv(const char *const filename,
58461                             const unsigned int chroma_subsampling=444,
58462                             const bool is_rgb=true) const {
58463       CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
58464       return *this;
58465     }
58466 
58467     //! Save image as a .yuv video file \overloading.
58468     /**
58469        Same as save_yuv(const char*,const unsigned int,const bool) const
58470        with a file stream argument instead of a filename string.
58471     **/
58472     const CImg<T>& save_yuv(std::FILE *const file,
58473                             const unsigned int chroma_subsampling=444,
58474                             const bool is_rgb=true) const {
58475       CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
58476       return *this;
58477     }
58478 
58479     //! Save 3D object as an Object File Format (.off) file.
58480     /**
58481        \param filename Filename, as a C-string.
58482        \param primitives List of 3D object primitives.
58483        \param colors List of 3D object colors.
58484        \note
58485        - Instance image contains the vertices data of the 3D object.
58486        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
58487        Such primitives will be lost or simplified during file saving.
58488        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
58489     **/
58490     template<typename tf, typename tc>
58491     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
58492                             const char *const filename) const {
58493       return _save_off(primitives,colors,0,filename);
58494     }
58495 
58496     //! Save 3D object as an Object File Format (.off) file \overloading.
58497     /**
58498        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
58499        with a file stream argument instead of a filename string.
58500     **/
58501     template<typename tf, typename tc>
58502     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
58503                             std::FILE *const file) const {
58504       return _save_off(primitives,colors,file,0);
58505     }
58506 
58507     template<typename tf, typename tc>
58508     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
58509                              std::FILE *const file, const char *const filename) const {
58510       if (!file && !filename)
58511         throw CImgArgumentException(_cimg_instance
58512                                     "save_off(): Specified filename is (null).",
58513                                     cimg_instance);
58514       if (is_empty())
58515         throw CImgInstanceException(_cimg_instance
58516                                     "save_off(): Empty instance, for file '%s'.",
58517                                     cimg_instance,
58518                                     filename?filename:"(FILE*)");
58519 
58520       CImgList<T> opacities;
58521       CImg<charT> error_message(1024);
58522       if (!is_object3d(primitives,colors,opacities,true,error_message))
58523         throw CImgInstanceException(_cimg_instance
58524                                     "save_off(): Invalid specified 3D object, for file '%s' (%s).",
58525                                     cimg_instance,
58526                                     filename?filename:"(FILE*)",error_message.data());
58527 
58528       const CImg<tc> default_color(1,3,1,1,(tc)std::min((int)cimg::type<tc>::max(),200));
58529       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
58530       unsigned int supported_primitives = 0;
58531       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
58532       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
58533       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
58534                                       (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
58535       cimglist_for(primitives,l) {
58536         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
58537         const unsigned int psiz = primitives[l].size(), csiz = color.size();
58538         const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f;
58539         switch (psiz) {
58540         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
58541                               (unsigned int)primitives(l,0),r,g,b); break;
58542         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
58543                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
58544         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
58545                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
58546                               (unsigned int)primitives(l,1),r,g,b); break;
58547         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
58548                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
58549                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
58550         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
58551                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
58552         case 6 : {
58553           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
58554           const float
58555             rt = color.atXY(xt,yt,0)/255.f,
58556             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
58557             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
58558           std::fprintf(nfile,"2 %u %u %f %f %f\n",
58559                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
58560         } break;
58561         case 9 : {
58562           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
58563           const float
58564             rt = color.atXY(xt,yt,0)/255.f,
58565             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
58566             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
58567           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
58568                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
58569                        (unsigned int)primitives(l,1),rt,gt,bt);
58570         } break;
58571         case 12 : {
58572           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
58573           const float
58574             rt = color.atXY(xt,yt,0)/255.f,
58575             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
58576             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
58577           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
58578                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
58579                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
58580         } break;
58581         }
58582       }
58583       if (!file) cimg::fclose(nfile);
58584       return *this;
58585     }
58586 
58587     //! Save volumetric image as a video (using the OpenCV library when available).
58588     /**
58589       \param filename Filename to write data to.
58590       \param fps Number of frames per second.
58591       \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
58592       \param keep_open Tells if the video writer associated to the specified filename
58593         must be kept open or not (to allow frames to be added in the same file afterwards).
58594     **/
58595     const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
58596                               const char *codec=0, const bool keep_open=false) const {
58597       if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
58598       CImgList<T> list;
58599       get_split('z').move_to(list);
58600       list.save_video(filename,fps,codec,keep_open);
58601       return *this;
58602     }
58603 
58604     //! Save volumetric image as a video, using ffmpeg external binary.
58605     /**
58606        \param filename Filename, as a C-string.
58607        \param fps Video framerate.
58608        \param codec Video codec, as a C-string.
58609        \param bitrate Video bitrate.
58610        \note
58611        - Each slice of the instance image is considered to be a single frame of the output video file.
58612        - This method uses \c ffmpeg, an external executable binary provided by
58613          <a href="http://www.ffmpeg.org">FFmpeg</a>.
58614        It must be installed for the method to succeed.
58615     **/
58616     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
58617                                         const char *const codec=0, const unsigned int bitrate=2048) const {
58618       if (!filename)
58619         throw CImgArgumentException(_cimg_instance
58620                                     "save_ffmpeg_external(): Specified filename is (null).",
58621                                     cimg_instance);
58622       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58623 
58624       CImgList<T> list;
58625       get_split('z').move_to(list);
58626       list.save_ffmpeg_external(filename,fps,codec,bitrate);
58627       return *this;
58628     }
58629 
58630     //! Save image using gzip external binary.
58631     /**
58632        \param filename Filename, as a C-string.
58633        \note This method uses \c gzip, an external executable binary provided by
58634          <a href="//http://www.gzip.org">gzip</a>.
58635        It must be installed for the method to succeed.
58636     **/
58637     const CImg<T>& save_gzip_external(const char *const filename) const {
58638       if (!filename)
58639         throw CImgArgumentException(_cimg_instance
58640                                     "save_gzip_external(): Specified filename is (null).",
58641                                     cimg_instance);
58642       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58643 
58644       CImg<charT> command(1024), filename_tmp(256), body(256);
58645       const char
58646         *ext = cimg::split_filename(filename,body),
58647         *ext2 = cimg::split_filename(body,0);
58648       std::FILE *file;
58649       do {
58650         if (!cimg::strcasecmp(ext,"gz")) {
58651           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58652                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
58653           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
58654                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58655         } else {
58656           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58657                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
58658           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
58659                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58660         }
58661         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58662       } while (file);
58663       save(filename_tmp);
58664       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
58665                     cimg::gzip_path(),
58666                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58667                     CImg<charT>::string(filename)._system_strescape().data());
58668       cimg::system(command, cimg::gzip_path());
58669       file = cimg::std_fopen(filename,"rb");
58670       if (!file)
58671         throw CImgIOException(_cimg_instance
58672                               "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
58673                               cimg_instance,
58674                               filename);
58675 
58676       else cimg::fclose(file);
58677       std::remove(filename_tmp);
58678       return *this;
58679     }
58680 
58681     //! Save image using GraphicsMagick's external binary.
58682     /**
58683        \param filename Filename, as a C-string.
58684        \param quality Image quality (expressed in percent), when the file format supports it.
58685        \note This method uses \c gm, an external executable binary provided by
58686          <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
58687        It must be installed for the method to succeed.
58688     **/
58689     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
58690       if (!filename)
58691         throw CImgArgumentException(_cimg_instance
58692                                     "save_graphicsmagick_external(): Specified filename is (null).",
58693                                     cimg_instance);
58694       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58695       if (_depth>1)
58696         cimg::warn(_cimg_instance
58697                    "save_other(): File '%s', saving a volumetric image with an external call to "
58698                    "GraphicsMagick only writes the first image slice.",
58699                    cimg_instance,filename);
58700 
58701 #ifdef cimg_use_png
58702 #define _cimg_sge_extension1 "png"
58703 #define _cimg_sge_extension2 "png"
58704 #else
58705 #define _cimg_sge_extension1 "pgm"
58706 #define _cimg_sge_extension2 "ppm"
58707 #endif
58708       CImg<charT> command(1024), filename_tmp(256);
58709       std::FILE *file;
58710       do {
58711         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58712                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
58713                       _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2);
58714         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58715       } while (file);
58716 
58717 #ifdef cimg_use_png
58718       save_png(filename_tmp);
58719 #else
58720       save_pnm(filename_tmp);
58721 #endif
58722       cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"",
58723                     cimg::graphicsmagick_path(),quality,
58724                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58725                     CImg<charT>::string(filename)._system_strescape().data());
58726       cimg::system(command, cimg::graphicsmagick_path());
58727       file = cimg::std_fopen(filename,"rb");
58728       if (!file)
58729         throw CImgIOException(_cimg_instance
58730                               "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
58731                               cimg_instance,
58732                               filename);
58733 
58734       if (file) cimg::fclose(file);
58735       std::remove(filename_tmp);
58736       return *this;
58737     }
58738 
58739     //! Save image using ImageMagick's external binary.
58740     /**
58741        \param filename Filename, as a C-string.
58742        \param quality Image quality (expressed in percent), when the file format supports it.
58743        \note This method uses \c convert, an external executable binary provided by
58744        <a href="http://www.imagemagick.org">ImageMagick</a>.
58745        It must be installed for the method to succeed.
58746     **/
58747     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
58748       if (!filename)
58749         throw CImgArgumentException(_cimg_instance
58750                                     "save_imagemagick_external(): Specified filename is (null).",
58751                                     cimg_instance);
58752       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58753       if (_depth>1)
58754         cimg::warn(_cimg_instance
58755                    "save_other(): File '%s', saving a volumetric image with an external call to "
58756                    "ImageMagick only writes the first image slice.",
58757                    cimg_instance,filename);
58758 #ifdef cimg_use_png
58759 #define _cimg_sie_extension1 "png"
58760 #define _cimg_sie_extension2 "png"
58761 #else
58762 #define _cimg_sie_extension1 "pgm"
58763 #define _cimg_sie_extension2 "ppm"
58764 #endif
58765       CImg<charT> command(1024), filename_tmp(256);
58766       std::FILE *file;
58767       do {
58768         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
58769                       cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2);
58770         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58771       } while (file);
58772 #ifdef cimg_use_png
58773       save_png(filename_tmp);
58774 #else
58775       save_pnm(filename_tmp);
58776 #endif
58777       cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"",
58778                     cimg::imagemagick_path(),quality,
58779                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58780                     CImg<charT>::string(filename)._system_strescape().data());
58781       cimg::system(command, cimg::imagemagick_path());
58782       file = cimg::std_fopen(filename,"rb");
58783       if (!file)
58784         throw CImgIOException(_cimg_instance
58785                               "save_imagemagick_external(): Failed to save file '%s' with "
58786                               "external command 'magick/convert'.",
58787                               cimg_instance,
58788                               filename);
58789 
58790       if (file) cimg::fclose(file);
58791       std::remove(filename_tmp);
58792       return *this;
58793     }
58794 
58795     //! Save image as a Dicom file.
58796     /**
58797        \param filename Filename, as a C-string.
58798        \note This method uses \c medcon, an external executable binary provided by
58799          <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
58800        It must be installed for the method to succeed.
58801     **/
58802     const CImg<T>& save_medcon_external(const char *const filename) const {
58803       if (!filename)
58804         throw CImgArgumentException(_cimg_instance
58805                                     "save_medcon_external(): Specified filename is (null).",
58806                                     cimg_instance);
58807       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58808 
58809       CImg<charT> command(1024), filename_tmp(256), body(256);
58810       std::FILE *file;
58811       do {
58812         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
58813         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58814       } while (file);
58815       save_analyze(filename_tmp);
58816       cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"",
58817                     cimg::medcon_path(),
58818                     CImg<charT>::string(filename)._system_strescape().data(),
58819                     CImg<charT>::string(filename_tmp)._system_strescape().data());
58820       cimg::system(command, cimg::medcon_path());
58821       std::remove(filename_tmp);
58822       cimg::split_filename(filename_tmp,body);
58823       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
58824       std::remove(filename_tmp);
58825 
58826       file = cimg::std_fopen(filename,"rb");
58827       if (!file) {
58828         cimg_snprintf(command,command._width,"m000-%s",filename);
58829         file = cimg::std_fopen(command,"rb");
58830         if (!file) {
58831           cimg::fclose(cimg::fopen(filename,"r"));
58832           throw CImgIOException(_cimg_instance
58833                                 "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
58834                                 cimg_instance,
58835                                 filename);
58836         }
58837       }
58838       cimg::fclose(file);
58839       std::rename(command,filename);
58840       return *this;
58841     }
58842 
58843     // Save image for non natively supported formats.
58844     /**
58845        \param filename Filename, as a C-string.
58846        \param quality Image quality (expressed in percent), when the file format supports it.
58847        \note
58848        - The filename extension tells about the desired file format.
58849        - This method tries to save the instance image as a file, using external tools from
58850        <a href="http://www.imagemagick.org">ImageMagick</a> or
58851        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
58852          At least one of these tool must be installed for the method to succeed.
58853        - It is recommended to use the generic method save(const char*, int) const instead,
58854          as it can handle some file formats natively.
58855     **/
58856     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
58857       if (!filename)
58858         throw CImgArgumentException(_cimg_instance
58859                                     "save_other(): Specified filename is (null).",
58860                                     cimg_instance);
58861       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58862       if (_depth>1)
58863         cimg::warn(_cimg_instance
58864                    "save_other(): File '%s', saving a volumetric image with an external call to "
58865                    "ImageMagick or GraphicsMagick only writes the first image slice.",
58866                    cimg_instance,filename);
58867 
58868       const unsigned int omode = cimg::exception_mode();
58869       bool is_saved = true;
58870       cimg::exception_mode(0);
58871       try { save_magick(filename); }
58872       catch (CImgException&) {
58873         try { save_imagemagick_external(filename,quality); }
58874         catch (CImgException&) {
58875           try { save_graphicsmagick_external(filename,quality); }
58876           catch (CImgException&) {
58877             is_saved = false;
58878           }
58879         }
58880       }
58881       cimg::exception_mode(omode);
58882       if (!is_saved)
58883         throw CImgIOException(_cimg_instance
58884                               "save_other(): Failed to save file '%s'. Format is not natively supported, "
58885                               "and no external commands succeeded.",
58886                               cimg_instance,
58887                               filename);
58888       return *this;
58889     }
58890 
58891     //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
58892     /**
58893        \param is_compressed tells if zlib compression must be used for serialization
58894        (this requires 'cimg_use_zlib' been enabled).
58895     **/
58896     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
58897       return CImgList<T>(*this,true).get_serialize(is_compressed);
58898     }
58899 
58900     // [internal] Return a 40x38 color logo of a 'danger' item.
58901     static CImg<T> _logo40x38() {
58902       CImg<T> res(40,38,1,3);
58903       const unsigned char *ptrs = cimg::logo40x38;
58904       T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
58905       for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
58906         const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
58907         for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
58908       }
58909       return res;
58910     }
58911 
58912     //@}
58913   }; // struct CImg { ...
58914 
58915   /*
58916    #-----------------------------------------
58917    #
58918    #
58919    #
58920    # Definition of the CImgList<T> structure
58921    #
58922    #
58923    #
58924    #------------------------------------------
58925    */
58926   //! Represent a list of images CImg<T>.
58927   template<typename T>
58928   struct CImgList {
58929     unsigned int _width, _allocated_width;
58930     CImg<T> *_data;
58931 
58932     //! Simple iterator type, to loop through each image of a list.
58933     /**
58934        \note
58935        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
58936        - You may use it like this:
58937        \code
58938        CImgList<> list;   // Assuming this image list is not empty
58939        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
58940        \endcode
58941        - Using the loop macro \c cimglist_for is another (more concise) alternative:
58942        \code
58943        cimglist_for(list,l) list[l].mirror('x');
58944        \endcode
58945     **/
58946     typedef CImg<T>* iterator;
58947 
58948     //! Simple const iterator type, to loop through each image of a \c const list instance.
58949     /**
58950        \note
58951        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
58952        - Similar to CImgList<T>::iterator, but for constant list instances.
58953     **/
58954     typedef const CImg<T>* const_iterator;
58955 
58956     //! Pixel value type.
58957     /**
58958        Refer to the pixels value type of the images in the list.
58959        \note
58960        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
58961          It is then similar to CImg<T>::value_type.
58962        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
58963          compatibility with STL naming conventions.
58964     **/
58965     typedef T value_type;
58966 
58967     // Define common types related to template type T.
58968     typedef typename cimg::superset<T,bool>::type Tbool;
58969     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
58970     typedef typename cimg::superset<T,char>::type Tchar;
58971     typedef typename cimg::superset<T,unsigned short>::type Tushort;
58972     typedef typename cimg::superset<T,short>::type Tshort;
58973     typedef typename cimg::superset<T,unsigned int>::type Tuint;
58974     typedef typename cimg::superset<T,int>::type Tint;
58975     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
58976     typedef typename cimg::superset<T,cimg_long>::type Tlong;
58977     typedef typename cimg::superset<T,float>::type Tfloat;
58978     typedef typename cimg::superset<T,double>::type Tdouble;
58979     typedef typename cimg::last<T,bool>::type boolT;
58980     typedef typename cimg::last<T,unsigned char>::type ucharT;
58981     typedef typename cimg::last<T,char>::type charT;
58982     typedef typename cimg::last<T,unsigned short>::type ushortT;
58983     typedef typename cimg::last<T,short>::type shortT;
58984     typedef typename cimg::last<T,unsigned int>::type uintT;
58985     typedef typename cimg::last<T,int>::type intT;
58986     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
58987     typedef typename cimg::last<T,cimg_long>::type longT;
58988     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
58989     typedef typename cimg::last<T,cimg_int64>::type int64T;
58990     typedef typename cimg::last<T,float>::type floatT;
58991     typedef typename cimg::last<T,double>::type doubleT;
58992 
58993     //@}
58994     //---------------------------
58995     //
58996     //! \name Plugins
58997     //@{
58998     //---------------------------
58999 #ifdef cimglist_plugin
59000 #include cimglist_plugin
59001 #endif
59002 #ifdef cimglist_plugin1
59003 #include cimglist_plugin1
59004 #endif
59005 #ifdef cimglist_plugin2
59006 #include cimglist_plugin2
59007 #endif
59008 #ifdef cimglist_plugin3
59009 #include cimglist_plugin3
59010 #endif
59011 #ifdef cimglist_plugin4
59012 #include cimglist_plugin4
59013 #endif
59014 #ifdef cimglist_plugin5
59015 #include cimglist_plugin5
59016 #endif
59017 #ifdef cimglist_plugin6
59018 #include cimglist_plugin6
59019 #endif
59020 #ifdef cimglist_plugin7
59021 #include cimglist_plugin7
59022 #endif
59023 #ifdef cimglist_plugin8
59024 #include cimglist_plugin8
59025 #endif
59026 
59027     //@}
59028     //--------------------------------------------------------
59029     //
59030     //! \name Constructors / Destructor / Instance Management
59031     //@{
59032     //--------------------------------------------------------
59033 
59034     //! Destructor.
59035     /**
59036        Destroy current list instance.
59037        \note
59038        - Any allocated buffer is deallocated.
59039        - Destroying an empty list does nothing actually.
59040      **/
59041     ~CImgList() {
59042       delete[] _data;
59043     }
59044 
59045     //! Default constructor.
59046     /**
59047        Construct a new empty list instance.
59048        \note
59049        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
59050          image buffer pointer data().
59051        - An empty list may be reassigned afterwards, with the family of the assign() methods.
59052          In all cases, the type of pixels stays \c T.
59053      **/
59054     CImgList():
59055       _width(0),_allocated_width(0),_data(0) {}
59056 
59057     //! Construct list containing empty images.
59058     /**
59059        \param n Number of empty images.
59060        \note Useful when you know by advance the number of images you want to manage, as
59061        it will allocate the right amount of memory for the list, without needs for reallocation
59062        (that may occur when starting from an empty list and inserting several images in it).
59063     **/
59064     explicit CImgList(const unsigned int n):_width(n) {
59065       if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
59066       else { _allocated_width = 0; _data = 0; }
59067     }
59068 
59069     //! Construct list containing images of specified size.
59070     /**
59071        \param n Number of images.
59072        \param width Width of images.
59073        \param height Height of images.
59074        \param depth Depth of images.
59075        \param spectrum Number of channels of images.
59076        \note Pixel values are not initialized and may probably contain garbage.
59077     **/
59078     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
59079              const unsigned int depth=1, const unsigned int spectrum=1):
59080       _width(0),_allocated_width(0),_data(0) {
59081       assign(n);
59082       cimglist_apply(*this,assign)(width,height,depth,spectrum);
59083     }
59084 
59085     //! Construct list containing images of specified size, and initialize pixel values.
59086     /**
59087        \param n Number of images.
59088        \param width Width of images.
59089        \param height Height of images.
59090        \param depth Depth of images.
59091        \param spectrum Number of channels of images.
59092        \param val Initialization value for images pixels.
59093     **/
59094     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59095              const unsigned int depth, const unsigned int spectrum, const T& val):
59096       _width(0),_allocated_width(0),_data(0) {
59097       assign(n);
59098       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
59099     }
59100 
59101     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
59102     /**
59103        \param n Number of images.
59104        \param width Width of images.
59105        \param height Height of images.
59106        \param depth Depth of images.
59107        \param spectrum Number of channels of images.
59108        \param val0 First value of the initializing integers sequence.
59109        \param val1 Second value of the initializing integers sequence.
59110        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
59111          or you will probably segfault.
59112     **/
59113     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59114              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
59115       _width(0),_allocated_width(0),_data(0) {
59116 #define _CImgList_stdarg(t) { \
59117         assign(n,width,height,depth,spectrum); \
59118         const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
59119         T *ptrd = _data->_data; \
59120         va_list ap; \
59121         va_start(ap,val1); \
59122         for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
59123           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
59124           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
59125         } \
59126         va_end(ap); \
59127       }
59128       _CImgList_stdarg(int);
59129     }
59130 
59131     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
59132     /**
59133        \param n Number of images.
59134        \param width Width of images.
59135        \param height Height of images.
59136        \param depth Depth of images.
59137        \param spectrum Number of channels of images.
59138        \param val0 First value of the initializing doubles sequence.
59139        \param val1 Second value of the initializing doubles sequence.
59140        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
59141          or you will probably segfault.
59142     **/
59143     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59144              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
59145       _width(0),_allocated_width(0),_data(0) {
59146       _CImgList_stdarg(double);
59147     }
59148 
59149     //! Construct list containing copies of an input image.
59150     /**
59151        \param n Number of images.
59152        \param img Input image to copy in the constructed list.
59153        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
59154     **/
59155     template<typename t>
59156     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
59157       _width(0),_allocated_width(0),_data(0) {
59158       assign(n);
59159       cimglist_apply(*this,assign)(img,is_shared);
59160     }
59161 
59162     //! Construct list from one image.
59163     /**
59164        \param img Input image to copy in the constructed list.
59165        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
59166      **/
59167     template<typename t>
59168     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
59169       _width(0),_allocated_width(0),_data(0) {
59170       assign(1);
59171       _data[0].assign(img,is_shared);
59172     }
59173 
59174     //! Construct list from two images.
59175     /**
59176        \param img1 First input image to copy in the constructed list.
59177        \param img2 Second input image to copy in the constructed list.
59178        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59179      **/
59180     template<typename t1, typename t2>
59181     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
59182       _width(0),_allocated_width(0),_data(0) {
59183       assign(2);
59184       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
59185     }
59186 
59187     //! Construct list from three images.
59188     /**
59189        \param img1 First input image to copy in the constructed list.
59190        \param img2 Second input image to copy in the constructed list.
59191        \param img3 Third input image to copy in the constructed list.
59192        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59193     **/
59194     template<typename t1, typename t2, typename t3>
59195     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
59196       _width(0),_allocated_width(0),_data(0) {
59197       assign(3);
59198       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59199     }
59200 
59201     //! Construct list from four images.
59202     /**
59203        \param img1 First input image to copy in the constructed list.
59204        \param img2 Second input image to copy in the constructed list.
59205        \param img3 Third input image to copy in the constructed list.
59206        \param img4 Fourth input image to copy in the constructed list.
59207        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59208     **/
59209     template<typename t1, typename t2, typename t3, typename t4>
59210     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59211              const bool is_shared=false):
59212       _width(0),_allocated_width(0),_data(0) {
59213       assign(4);
59214       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59215       _data[3].assign(img4,is_shared);
59216     }
59217 
59218     //! Construct list from five images.
59219     /**
59220        \param img1 First input image to copy in the constructed list.
59221        \param img2 Second input image to copy in the constructed list.
59222        \param img3 Third input image to copy in the constructed list.
59223        \param img4 Fourth input image to copy in the constructed list.
59224        \param img5 Fifth input image to copy in the constructed list.
59225        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59226     **/
59227     template<typename t1, typename t2, typename t3, typename t4, typename t5>
59228     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59229              const CImg<t5>& img5, const bool is_shared=false):
59230       _width(0),_allocated_width(0),_data(0) {
59231       assign(5);
59232       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59233       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
59234     }
59235 
59236     //! Construct list from six images.
59237     /**
59238        \param img1 First input image to copy in the constructed list.
59239        \param img2 Second input image to copy in the constructed list.
59240        \param img3 Third input image to copy in the constructed list.
59241        \param img4 Fourth input image to copy in the constructed list.
59242        \param img5 Fifth input image to copy in the constructed list.
59243        \param img6 Sixth input image to copy in the constructed list.
59244        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59245     **/
59246     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
59247     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59248              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
59249       _width(0),_allocated_width(0),_data(0) {
59250       assign(6);
59251       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59252       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59253     }
59254 
59255     //! Construct list from seven images.
59256     /**
59257        \param img1 First input image to copy in the constructed list.
59258        \param img2 Second input image to copy in the constructed list.
59259        \param img3 Third input image to copy in the constructed list.
59260        \param img4 Fourth input image to copy in the constructed list.
59261        \param img5 Fifth input image to copy in the constructed list.
59262        \param img6 Sixth input image to copy in the constructed list.
59263        \param img7 Seventh input image to copy in the constructed list.
59264        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59265     **/
59266     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
59267     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59268              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
59269       _width(0),_allocated_width(0),_data(0) {
59270       assign(7);
59271       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59272       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59273       _data[6].assign(img7,is_shared);
59274     }
59275 
59276     //! Construct list from eight images.
59277     /**
59278        \param img1 First input image to copy in the constructed list.
59279        \param img2 Second input image to copy in the constructed list.
59280        \param img3 Third input image to copy in the constructed list.
59281        \param img4 Fourth input image to copy in the constructed list.
59282        \param img5 Fifth input image to copy in the constructed list.
59283        \param img6 Sixth input image to copy in the constructed list.
59284        \param img7 Seventh input image to copy in the constructed list.
59285        \param img8 Eighth input image to copy in the constructed list.
59286        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59287     **/
59288     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
59289     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59290              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
59291              const bool is_shared=false):
59292       _width(0),_allocated_width(0),_data(0) {
59293       assign(8);
59294       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59295       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59296       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
59297     }
59298 
59299     //! Construct list copy.
59300     /**
59301        \param list Input list to copy.
59302        \note The shared state of each element of the constructed list is kept the same as in \c list.
59303     **/
59304     template<typename t>
59305     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
59306       assign(list._width);
59307       cimglist_for(*this,l) _data[l].assign(list[l],false);
59308     }
59309 
59310     //! Construct list copy \specialization.
59311     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
59312       assign(list._width);
59313       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
59314     }
59315 
59316     //! Construct list copy, and force the shared state of the list elements.
59317     /**
59318        \param list Input list to copy.
59319        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59320     **/
59321     template<typename t>
59322     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
59323       assign(list._width);
59324       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
59325     }
59326 
59327     //! Construct list by reading the content of a file.
59328     /**
59329        \param filename Filename, as a C-string.
59330     **/
59331     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
59332       assign(filename);
59333     }
59334 
59335     //! Construct list from the content of a display window.
59336     /**
59337        \param disp Display window to get content from.
59338        \note Constructed list contains a single image only.
59339     **/
59340     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
59341       assign(disp);
59342     }
59343 
59344     //! Return a list with elements being shared copies of images in the list instance.
59345     /**
59346       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
59347     **/
59348     CImgList<T> get_shared() {
59349       CImgList<T> res(_width);
59350       cimglist_for(*this,l) res[l].assign(_data[l],true);
59351       return res;
59352     }
59353 
59354     //! Return a list with elements being shared copies of images in the list instance \const.
59355     const CImgList<T> get_shared() const {
59356       CImgList<T> res(_width);
59357       cimglist_for(*this,l) res[l].assign(_data[l],true);
59358       return res;
59359     }
59360 
59361     //! Destructor \inplace.
59362     /**
59363        \see CImgList().
59364     **/
59365     CImgList<T>& assign() {
59366       delete[] _data;
59367       _width = _allocated_width = 0;
59368       _data = 0;
59369       return *this;
59370     }
59371 
59372     //! Destructor \inplace.
59373     /**
59374        Equivalent to assign().
59375        \note Only here for compatibility with STL naming conventions.
59376     **/
59377     CImgList<T>& clear() {
59378       return assign();
59379     }
59380 
59381     //! Construct list containing empty images \inplace.
59382     /**
59383        \see CImgList(unsigned int).
59384     **/
59385     CImgList<T>& assign(const unsigned int n) {
59386       if (!n) return assign();
59387       if (_allocated_width<n || _allocated_width>(n<<2)) {
59388         delete[] _data;
59389         _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
59390       }
59391       _width = n;
59392       return *this;
59393     }
59394 
59395     //! Construct list containing images of specified size \inplace.
59396     /**
59397        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
59398     **/
59399     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
59400                         const unsigned int depth=1, const unsigned int spectrum=1) {
59401       assign(n);
59402       cimglist_apply(*this,assign)(width,height,depth,spectrum);
59403       return *this;
59404     }
59405 
59406     //! Construct list containing images of specified size, and initialize pixel values \inplace.
59407     /**
59408        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
59409     **/
59410     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
59411                         const unsigned int depth, const unsigned int spectrum, const T& val) {
59412       assign(n);
59413       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
59414       return *this;
59415     }
59416 
59417     //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
59418     /**
59419        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
59420     **/
59421     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
59422                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
59423       _CImgList_stdarg(int);
59424       return *this;
59425     }
59426 
59427     //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
59428     /**
59429        \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
59430     **/
59431     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
59432                         const unsigned int depth, const unsigned int spectrum,
59433                         const double val0, const double val1, ...) {
59434       _CImgList_stdarg(double);
59435       return *this;
59436     }
59437 
59438     //! Construct list containing copies of an input image \inplace.
59439     /**
59440        \see CImgList(unsigned int, const CImg<t>&, bool).
59441     **/
59442     template<typename t>
59443     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
59444       assign(n);
59445       cimglist_apply(*this,assign)(img,is_shared);
59446       return *this;
59447     }
59448 
59449     //! Construct list from one image \inplace.
59450     /**
59451        \see CImgList(const CImg<t>&, bool).
59452     **/
59453     template<typename t>
59454     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
59455       assign(1);
59456       _data[0].assign(img,is_shared);
59457       return *this;
59458     }
59459 
59460     //! Construct list from two images \inplace.
59461     /**
59462        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
59463     **/
59464     template<typename t1, typename t2>
59465     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
59466       assign(2);
59467       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
59468       return *this;
59469     }
59470 
59471     //! Construct list from three images \inplace.
59472     /**
59473        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
59474     **/
59475     template<typename t1, typename t2, typename t3>
59476     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
59477       assign(3);
59478       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59479       return *this;
59480     }
59481 
59482     //! Construct list from four images \inplace.
59483     /**
59484        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
59485     **/
59486     template<typename t1, typename t2, typename t3, typename t4>
59487     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59488                         const bool is_shared=false) {
59489       assign(4);
59490       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59491       _data[3].assign(img4,is_shared);
59492       return *this;
59493     }
59494 
59495     //! Construct list from five images \inplace.
59496     /**
59497        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
59498     **/
59499     template<typename t1, typename t2, typename t3, typename t4, typename t5>
59500     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59501                         const CImg<t5>& img5, const bool is_shared=false) {
59502       assign(5);
59503       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59504       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
59505       return *this;
59506     }
59507 
59508     //! Construct list from six images \inplace.
59509     /**
59510        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
59511     **/
59512     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
59513     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59514                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
59515       assign(6);
59516       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59517       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59518       return *this;
59519     }
59520 
59521     //! Construct list from seven images \inplace.
59522     /**
59523        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
59524        const CImg<t>&, bool).
59525     **/
59526     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
59527     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59528                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
59529       assign(7);
59530       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59531       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59532       _data[6].assign(img7,is_shared);
59533       return *this;
59534     }
59535 
59536     //! Construct list from eight images \inplace.
59537     /**
59538        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
59539        const CImg<t>&, const CImg<t>&, bool).
59540     **/
59541     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
59542     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59543                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
59544                         const bool is_shared=false) {
59545       assign(8);
59546       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59547       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59548       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
59549       return *this;
59550     }
59551 
59552     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
59553     /**
59554       \see CImgList(const CImgList<t>&, bool is_shared).
59555     **/
59556     template<typename t>
59557     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
59558       cimg::unused(is_shared);
59559       assign(list._width);
59560       cimglist_for(*this,l) _data[l].assign(list[l],false);
59561       return *this;
59562     }
59563 
59564     //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
59565     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
59566       if (this==&list) return *this;
59567       CImgList<T> res(list._width);
59568       cimglist_for(res,l) res[l].assign(list[l],is_shared);
59569       return res.move_to(*this);
59570     }
59571 
59572     //! Construct list by reading the content of a file \inplace.
59573     /**
59574       \see CImgList(const char *const).
59575     **/
59576     CImgList<T>& assign(const char *const filename) {
59577       return load(filename);
59578     }
59579 
59580     //! Construct list from the content of a display window \inplace.
59581     /**
59582       \see CImgList(const CImgDisplay&).
59583     **/
59584     CImgList<T>& assign(const CImgDisplay &disp) {
59585       return assign(CImg<T>(disp));
59586     }
59587 
59588     //! Transfer the content of the list instance to another list.
59589     /**
59590        \param list Destination list.
59591        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
59592     **/
59593     template<typename t>
59594     CImgList<t>& move_to(CImgList<t>& list) {
59595       list.assign(_width);
59596       bool is_one_shared_element = false;
59597       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
59598       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
59599       else cimglist_for(*this,l) _data[l].move_to(list[l]);
59600       assign();
59601       return list;
59602     }
59603 
59604     //! Transfer the content of the list instance at a specified position in another list.
59605     /**
59606        \param list Destination list.
59607        \param pos Index of the insertion in the list.
59608        \note When returning, the list instance is empty and the initial content of \c list is preserved
59609        (only images indexes may be modified).
59610      **/
59611     template<typename t>
59612     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
59613       if (is_empty()) return list;
59614       const unsigned int npos = pos>list._width?list._width:pos;
59615       list.insert(_width,npos);
59616       bool is_one_shared_element = false;
59617       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
59618       if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
59619       else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
59620       assign();
59621       return list;
59622     }
59623 
59624     //! Swap all fields between two list instances.
59625     /**
59626        \param list List to swap fields with.
59627        \note Can be used to exchange the content of two lists in a fast way.
59628     **/
59629     CImgList<T>& swap(CImgList<T>& list) {
59630       cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
59631       cimg::swap(_data,list._data);
59632       return list;
59633     }
59634 
59635     //! Return a reference to an empty list.
59636     /**
59637       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
59638       \code
59639       void f(const CImgList<char>& list=CImgList<char>::empty());
59640       \endcode
59641     **/
59642     static CImgList<T>& empty() {
59643       static CImgList<T> _empty;
59644       return _empty.assign();
59645     }
59646 
59647     //! Return a reference to an empty list \const.
59648     static const CImgList<T>& const_empty() {
59649       static const CImgList<T> _empty;
59650       return _empty;
59651     }
59652 
59653     //@}
59654     //------------------------------------------
59655     //
59656     //! \name Overloaded Operators
59657     //@{
59658     //------------------------------------------
59659 
59660     //! Return a reference to one image element of the list.
59661     /**
59662        \param pos Index of the image element.
59663     **/
59664     CImg<T>& operator()(const unsigned int pos) {
59665 #if cimg_verbosity>=3
59666       if (pos>=_width) {
59667         cimg::warn(_cimglist_instance
59668                    "operator(): Invalid image request, at position [%u].",
59669                    cimglist_instance,
59670                    pos);
59671         return *_data;
59672       }
59673 #endif
59674       return _data[pos];
59675     }
59676 
59677     //! Return a reference to one image of the list.
59678     /**
59679        \param pos Index of the image element.
59680     **/
59681     const CImg<T>& operator()(const unsigned int pos) const {
59682       return const_cast<CImgList<T>*>(this)->operator()(pos);
59683     }
59684 
59685     //! Return a reference to one pixel value of one image of the list.
59686     /**
59687        \param pos Index of the image element.
59688        \param x X-coordinate of the pixel value.
59689        \param y Y-coordinate of the pixel value.
59690        \param z Z-coordinate of the pixel value.
59691        \param c C-coordinate of the pixel value.
59692        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
59693     **/
59694     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
59695                   const unsigned int z=0, const unsigned int c=0) {
59696       return (*this)[pos](x,y,z,c);
59697     }
59698 
59699     //! Return a reference to one pixel value of one image of the list \const.
59700     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
59701                         const unsigned int z=0, const unsigned int c=0) const {
59702       return (*this)[pos](x,y,z,c);
59703     }
59704 
59705     //! Return pointer to the first image of the list.
59706     /**
59707        \note Images in a list are stored as a buffer of \c CImg<T>.
59708     **/
59709     operator CImg<T>*() {
59710       return _data;
59711     }
59712 
59713     //! Return pointer to the first image of the list \const.
59714     operator const CImg<T>*() const {
59715       return _data;
59716     }
59717 
59718     //! Construct list from one image \inplace.
59719     /**
59720         \param img Input image to copy in the constructed list.
59721         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
59722     **/
59723     template<typename t>
59724     CImgList<T>& operator=(const CImg<t>& img) {
59725       return assign(img);
59726     }
59727 
59728     //! Construct list from another list.
59729     /**
59730        \param list Input list to copy.
59731        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
59732     **/
59733     template<typename t>
59734     CImgList<T>& operator=(const CImgList<t>& list) {
59735       return assign(list);
59736     }
59737 
59738     //! Construct list from another list \specialization.
59739     CImgList<T>& operator=(const CImgList<T>& list) {
59740       return assign(list);
59741     }
59742 
59743     //! Construct list by reading the content of a file \inplace.
59744     /**
59745        \see CImgList(const char *const).
59746     **/
59747     CImgList<T>& operator=(const char *const filename) {
59748       return assign(filename);
59749     }
59750 
59751     //! Construct list from the content of a display window \inplace.
59752     /**
59753         \see CImgList(const CImgDisplay&).
59754     **/
59755     CImgList<T>& operator=(const CImgDisplay& disp) {
59756       return assign(disp);
59757     }
59758 
59759     //! Return a non-shared copy of a list.
59760     /**
59761         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
59762           It forces the copy to have non-shared elements.
59763     **/
59764     CImgList<T> operator+() const {
59765       return CImgList<T>(*this,false);
59766     }
59767 
59768     //! Return a copy of the list instance, where image \c img has been inserted at the end.
59769     /**
59770        \param img Image inserted at the end of the instance copy.
59771        \note Define a convenient way to create temporary lists of images, as in the following code:
59772        \code
59773        (img1,img2,img3,img4).display("My four images");
59774        \endcode
59775     **/
59776     template<typename t>
59777     CImgList<T>& operator,(const CImg<t>& img) {
59778       return insert(img);
59779     }
59780 
59781     //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
59782     template<typename t>
59783     CImgList<T> operator,(const CImg<t>& img) const {
59784       return (+*this).insert(img);
59785     }
59786 
59787     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
59788     /**
59789        \param list List inserted at the end of the instance copy.
59790     **/
59791     template<typename t>
59792     CImgList<T>& operator,(const CImgList<t>& list) {
59793       return insert(list);
59794     }
59795 
59796     //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
59797     template<typename t>
59798     CImgList<T>& operator,(const CImgList<t>& list) const {
59799       return (+*this).insert(list);
59800     }
59801 
59802     //! Return image corresponding to the appending of all images of the instance list along specified axis.
59803     /**
59804       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
59805       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
59806     **/
59807     CImg<T> operator>(const char axis) const {
59808       return get_append(axis,0);
59809     }
59810 
59811     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
59812     /**
59813       \param axis Axis used for image splitting.
59814       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
59815     **/
59816     CImgList<T> operator<(const char axis) const {
59817       return get_split(axis);
59818     }
59819 
59820     //@}
59821     //-------------------------------------
59822     //
59823     //! \name Instance Characteristics
59824     //@{
59825     //-------------------------------------
59826 
59827     //! Return the type of image pixel values as a C string.
59828     /**
59829        Return a \c char* string containing the usual type name of the image pixel values
59830        (i.e. a stringified version of the template parameter \c T).
59831        \note
59832        - The returned string may contain spaces (as in \c "unsigned char").
59833        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
59834     **/
59835     static const char* pixel_type() {
59836       return cimg::type<T>::string();
59837     }
59838 
59839     //! Return the size of the list, i.e. the number of images contained in it.
59840     /**
59841       \note Similar to size() but returns result as a (signed) integer.
59842     **/
59843     int width() const {
59844       return (int)_width;
59845     }
59846 
59847     //! Return the size of the list, i.e. the number of images contained in it.
59848     /**
59849       \note Similar to width() but returns result as an unsigned integer.
59850     **/
59851     unsigned int size() const {
59852       return _width;
59853     }
59854 
59855     //! Return pointer to the first image of the list.
59856     /**
59857        \note Images in a list are stored as a buffer of \c CImg<T>.
59858     **/
59859     CImg<T> *data() {
59860       return _data;
59861     }
59862 
59863     //! Return pointer to the first image of the list \const.
59864     const CImg<T> *data() const {
59865       return _data;
59866     }
59867 
59868     //! Return pointer to the pos-th image of the list.
59869     /**
59870        \param pos Index of the image element to access.
59871        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
59872     **/
59873 #if cimg_verbosity>=3
59874     CImg<T> *data(const unsigned int pos) {
59875       if (pos>=size())
59876         cimg::warn(_cimglist_instance
59877                    "data(): Invalid pointer request, at position [%u].",
59878                    cimglist_instance,
59879                    pos);
59880       return _data + pos;
59881     }
59882 
59883     const CImg<T> *data(const unsigned int l) const {
59884       return const_cast<CImgList<T>*>(this)->data(l);
59885     }
59886 #else
59887     CImg<T> *data(const unsigned int l) {
59888       return _data + l;
59889     }
59890 
59891     //! Return pointer to the pos-th image of the list \const.
59892     const CImg<T> *data(const unsigned int l) const {
59893       return _data + l;
59894     }
59895 #endif
59896 
59897     //! Return iterator to the first image of the list.
59898     /**
59899     **/
59900     iterator begin() {
59901       return _data;
59902     }
59903 
59904     //! Return iterator to the first image of the list \const.
59905     const_iterator begin() const {
59906       return _data;
59907     }
59908 
59909     //! Return iterator to one position after the last image of the list.
59910     /**
59911     **/
59912     iterator end() {
59913       return _data + _width;
59914     }
59915 
59916     //! Return iterator to one position after the last image of the list \const.
59917     const_iterator end() const {
59918       return _data + _width;
59919     }
59920 
59921     //! Return reference to the first image of the list.
59922     /**
59923     **/
59924     CImg<T>& front() {
59925       return *_data;
59926     }
59927 
59928     //! Return reference to the first image of the list \const.
59929     const CImg<T>& front() const {
59930       return *_data;
59931     }
59932 
59933     //! Return a reference to the last image of the list.
59934     /**
59935     **/
59936     const CImg<T>& back() const {
59937       return *(_data + _width - 1);
59938     }
59939 
59940     //! Return a reference to the last image of the list \const.
59941     CImg<T>& back() {
59942       return *(_data + _width - 1);
59943     }
59944 
59945     //! Return pos-th image of the list.
59946     /**
59947        \param pos Index of the image element to access.
59948     **/
59949     CImg<T>& at(const int pos) {
59950       if (is_empty())
59951         throw CImgInstanceException(_cimglist_instance
59952                                     "at(): Empty instance.",
59953                                     cimglist_instance);
59954 
59955       return _data[cimg::cut(pos,0,width() - 1)];
59956     }
59957 
59958     //! Access to pixel value with Dirichlet boundary conditions.
59959     /**
59960        \param pos Index of the image element to access.
59961        \param x X-coordinate of the pixel value.
59962        \param y Y-coordinate of the pixel value.
59963        \param z Z-coordinate of the pixel value.
59964        \param c C-coordinate of the pixel value.
59965        \param out_value Default value returned if \c offset is outside image bounds.
59966        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
59967     **/
59968     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59969       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
59970     }
59971 
59972     //! Access to pixel value with Dirichlet boundary conditions \const.
59973     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59974       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
59975     }
59976 
59977     //! Access to pixel value with Neumann boundary conditions.
59978     /**
59979        \param pos Index of the image element to access.
59980        \param x X-coordinate of the pixel value.
59981        \param y Y-coordinate of the pixel value.
59982        \param z Z-coordinate of the pixel value.
59983        \param c C-coordinate of the pixel value.
59984        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
59985     **/
59986     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
59987       if (is_empty())
59988         throw CImgInstanceException(_cimglist_instance
59989                                     "atNXYZC(): Empty instance.",
59990                                     cimglist_instance);
59991 
59992       return _atNXYZC(pos,x,y,z,c);
59993     }
59994 
59995     //! Access to pixel value with Neumann boundary conditions \const.
59996     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
59997       if (is_empty())
59998         throw CImgInstanceException(_cimglist_instance
59999                                     "atNXYZC(): Empty instance.",
60000                                     cimglist_instance);
60001 
60002       return _atNXYZC(pos,x,y,z,c);
60003     }
60004 
60005     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
60006       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
60007     }
60008 
60009     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
60010       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
60011     }
60012 
60013     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
60014     /**
60015        \param pos Index of the image element to access.
60016        \param x X-coordinate of the pixel value.
60017        \param y Y-coordinate of the pixel value.
60018        \param z Z-coordinate of the pixel value.
60019        \param c C-coordinate of the pixel value.
60020        \param out_value Default value returned if \c offset is outside image bounds.
60021        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60022     **/
60023     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60024       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
60025     }
60026 
60027     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
60028     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60029       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
60030     }
60031 
60032     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
60033     /**
60034        \param pos Index of the image element to access.
60035        \param x X-coordinate of the pixel value.
60036        \param y Y-coordinate of the pixel value.
60037        \param z Z-coordinate of the pixel value.
60038        \param c C-coordinate of the pixel value.
60039        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60040     **/
60041    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
60042       if (is_empty())
60043         throw CImgInstanceException(_cimglist_instance
60044                                     "atNXYZ(): Empty instance.",
60045                                     cimglist_instance);
60046 
60047       return _atNXYZ(pos,x,y,z,c);
60048     }
60049 
60050     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
60051     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
60052       if (is_empty())
60053         throw CImgInstanceException(_cimglist_instance
60054                                     "atNXYZ(): Empty instance.",
60055                                     cimglist_instance);
60056 
60057       return _atNXYZ(pos,x,y,z,c);
60058     }
60059 
60060     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
60061       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
60062     }
60063 
60064     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
60065       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
60066     }
60067 
60068     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
60069     /**
60070        \param pos Index of the image element to access.
60071        \param x X-coordinate of the pixel value.
60072        \param y Y-coordinate of the pixel value.
60073        \param z Z-coordinate of the pixel value.
60074        \param c C-coordinate of the pixel value.
60075        \param out_value Default value returned if \c offset is outside image bounds.
60076        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60077     **/
60078     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60079       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
60080     }
60081 
60082     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
60083     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60084       return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value);
60085     }
60086 
60087     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
60088     /**
60089        \param pos Index of the image element to access.
60090        \param x X-coordinate of the pixel value.
60091        \param y Y-coordinate of the pixel value.
60092        \param z Z-coordinate of the pixel value.
60093        \param c C-coordinate of the pixel value.
60094        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60095     **/
60096     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
60097       if (is_empty())
60098         throw CImgInstanceException(_cimglist_instance
60099                                     "atNXY(): Empty instance.",
60100                                     cimglist_instance);
60101 
60102       return _atNXY(pos,x,y,z,c);
60103     }
60104 
60105     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
60106     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
60107       if (is_empty())
60108         throw CImgInstanceException(_cimglist_instance
60109                                     "atNXY(): Empty instance.",
60110                                     cimglist_instance);
60111 
60112       return _atNXY(pos,x,y,z,c);
60113     }
60114 
60115     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
60116       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
60117     }
60118 
60119     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
60120       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
60121     }
60122 
60123     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
60124     /**
60125        \param pos Index of the image element to access.
60126        \param x X-coordinate of the pixel value.
60127        \param y Y-coordinate of the pixel value.
60128        \param z Z-coordinate of the pixel value.
60129        \param c C-coordinate of the pixel value.
60130        \param out_value Default value returned if \c offset is outside image bounds.
60131        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60132     **/
60133     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60134       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
60135     }
60136 
60137     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
60138     T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60139       return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
60140     }
60141 
60142     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
60143     /**
60144        \param pos Index of the image element to access.
60145        \param x X-coordinate of the pixel value.
60146        \param y Y-coordinate of the pixel value.
60147        \param z Z-coordinate of the pixel value.
60148        \param c C-coordinate of the pixel value.
60149        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60150     **/
60151     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
60152       if (is_empty())
60153         throw CImgInstanceException(_cimglist_instance
60154                                     "atNX(): Empty instance.",
60155                                     cimglist_instance);
60156 
60157       return _atNX(pos,x,y,z,c);
60158     }
60159 
60160     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
60161     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
60162       if (is_empty())
60163         throw CImgInstanceException(_cimglist_instance
60164                                     "atNX(): Empty instance.",
60165                                     cimglist_instance);
60166 
60167       return _atNX(pos,x,y,z,c);
60168     }
60169 
60170     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
60171       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
60172     }
60173 
60174     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
60175       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
60176     }
60177 
60178     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
60179     /**
60180        \param pos Index of the image element to access.
60181        \param x X-coordinate of the pixel value.
60182        \param y Y-coordinate of the pixel value.
60183        \param z Z-coordinate of the pixel value.
60184        \param c C-coordinate of the pixel value.
60185        \param out_value Default value returned if \c offset is outside image bounds.
60186        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60187     **/
60188     T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60189       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
60190     }
60191 
60192     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
60193     T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60194       return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c);
60195     }
60196 
60197     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
60198     /**
60199        \param pos Index of the image element to access.
60200        \param x X-coordinate of the pixel value.
60201        \param y Y-coordinate of the pixel value.
60202        \param z Z-coordinate of the pixel value.
60203        \param c C-coordinate of the pixel value.
60204        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60205     **/
60206     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
60207       if (is_empty())
60208         throw CImgInstanceException(_cimglist_instance
60209                                     "atN(): Empty instance.",
60210                                     cimglist_instance);
60211       return _atN(pos,x,y,z,c);
60212     }
60213 
60214     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
60215     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
60216       if (is_empty())
60217         throw CImgInstanceException(_cimglist_instance
60218                                     "atN(): Empty instance.",
60219                                     cimglist_instance);
60220       return _atN(pos,x,y,z,c);
60221     }
60222 
60223     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
60224       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
60225     }
60226 
60227     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
60228       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
60229     }
60230 
60231     //@}
60232     //-------------------------------------
60233     //
60234     //! \name Instance Checking
60235     //@{
60236     //-------------------------------------
60237 
60238     //! Return \c true if list is empty.
60239     /**
60240     **/
60241     bool is_empty() const {
60242       return (!_data || !_width);
60243     }
60244 
60245     //! Test if number of image elements is equal to specified value.
60246     /**
60247         \param size_n Number of image elements to test.
60248     **/
60249     bool is_sameN(const unsigned int size_n) const {
60250       return _width==size_n;
60251     }
60252 
60253     //! Test if number of image elements is equal between two images lists.
60254     /**
60255         \param list Input list to compare with.
60256     **/
60257     template<typename t>
60258     bool is_sameN(const CImgList<t>& list) const {
60259       return is_sameN(list._width);
60260     }
60261 
60262     // Define useful functions to check list dimensions.
60263     // (cannot be documented because macro-generated).
60264 #define _cimglist_def_is_same1(axis) \
60265     bool is_same##axis(const unsigned int val) const { \
60266       bool res = true; \
60267       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \
60268       return res; \
60269     } \
60270     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
60271       return is_sameN(n) && is_same##axis(val); \
60272     } \
60273 
60274 #define _cimglist_def_is_same2(axis1,axis2) \
60275     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
60276       bool res = true; \
60277       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \
60278       return res; \
60279     } \
60280     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
60281       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
60282     } \
60283 
60284 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
60285     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
60286                                       const unsigned int val3) const { \
60287       bool res = true; \
60288       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
60289       return res; \
60290     } \
60291     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
60292                                        const unsigned int val2, const unsigned int val3) const { \
60293       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
60294     } \
60295 
60296 #define _cimglist_def_is_same(axis) \
60297     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
60298       bool res = true; \
60299       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \
60300       return res; \
60301     } \
60302     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
60303       const unsigned int lmin = std::min(_width,list._width); \
60304       bool res = true; \
60305       for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); \
60306       return res; \
60307     } \
60308     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
60309       return (is_sameN(n) && is_same##axis(img)); \
60310     } \
60311     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
60312       return (is_sameN(list) && is_same##axis(list)); \
60313     }
60314 
60315     _cimglist_def_is_same(XY)
60316     _cimglist_def_is_same(XZ)
60317     _cimglist_def_is_same(XC)
60318     _cimglist_def_is_same(YZ)
60319     _cimglist_def_is_same(YC)
60320     _cimglist_def_is_same(XYZ)
60321     _cimglist_def_is_same(XYC)
60322     _cimglist_def_is_same(YZC)
60323     _cimglist_def_is_same(XYZC)
60324     _cimglist_def_is_same1(X)
60325     _cimglist_def_is_same1(Y)
60326     _cimglist_def_is_same1(Z)
60327     _cimglist_def_is_same1(C)
60328     _cimglist_def_is_same2(X,Y)
60329     _cimglist_def_is_same2(X,Z)
60330     _cimglist_def_is_same2(X,C)
60331     _cimglist_def_is_same2(Y,Z)
60332     _cimglist_def_is_same2(Y,C)
60333     _cimglist_def_is_same2(Z,C)
60334     _cimglist_def_is_same3(X,Y,Z)
60335     _cimglist_def_is_same3(X,Y,C)
60336     _cimglist_def_is_same3(X,Z,C)
60337     _cimglist_def_is_same3(Y,Z,C)
60338 
60339     //! Test if dimensions of each image of the list match specified arguments.
60340     /**
60341       \param dx Checked image width.
60342       \param dy Checked image height.
60343       \param dz Checked image depth.
60344       \param dc Checked image spectrum.
60345     **/
60346     bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
60347                      const unsigned int dz, const unsigned int dc) const {
60348       bool res = true;
60349       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
60350       return res;
60351     }
60352 
60353     //! Test if list dimensions match specified arguments.
60354     /**
60355        \param n Number of images in the list.
60356        \param dx Checked image width.
60357        \param dy Checked image height.
60358        \param dz Checked image depth.
60359        \param dc Checked image spectrum.
60360     **/
60361     bool is_sameNXYZC(const unsigned int n,
60362                       const unsigned int dx, const unsigned int dy,
60363                       const unsigned int dz, const unsigned int dc) const {
60364       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
60365     }
60366 
60367     //! Test if list contains one particular pixel location.
60368     /**
60369        \param n Index of the image whom checked pixel value belong to.
60370        \param x X-coordinate of the checked pixel value.
60371        \param y Y-coordinate of the checked pixel value.
60372        \param z Z-coordinate of the checked pixel value.
60373        \param c C-coordinate of the checked pixel value.
60374     **/
60375     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
60376       if (is_empty()) return false;
60377       return n>=0 && n<width() && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
60378         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
60379     }
60380 
60381     //! Test if list contains image with specified index.
60382     /**
60383        \param n Index of the checked image.
60384     **/
60385     bool containsN(const int n) const {
60386       if (is_empty()) return false;
60387       return n>=0 && n<width();
60388     }
60389 
60390     //! Test if one image of the list contains the specified referenced value.
60391     /**
60392        \param pixel Reference to pixel value to test.
60393        \param[out] n Index of image containing the pixel value, if test succeeds.
60394        \param[out] x X-coordinate of the pixel value, if test succeeds.
60395        \param[out] y Y-coordinate of the pixel value, if test succeeds.
60396        \param[out] z Z-coordinate of the pixel value, if test succeeds.
60397        \param[out] c C-coordinate of the pixel value, if test succeeds.
60398        \note If true, set coordinates (n,x,y,z,c).
60399     **/
60400     template<typename t>
60401     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
60402       if (is_empty()) return false;
60403       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
60404       return false;
60405     }
60406 
60407     //! Test if one of the image list contains the specified referenced value.
60408     /**
60409        \param pixel Reference to pixel value to test.
60410        \param[out] n Index of image containing the pixel value, if test succeeds.
60411        \param[out] x X-coordinate of the pixel value, if test succeeds.
60412        \param[out] y Y-coordinate of the pixel value, if test succeeds.
60413        \param[out] z Z-coordinate of the pixel value, if test succeeds.
60414        \note If true, set coordinates (n,x,y,z).
60415     **/
60416     template<typename t>
60417     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
60418       t c;
60419       return contains(pixel,n,x,y,z,c);
60420     }
60421 
60422     //! Test if one of the image list contains the specified referenced value.
60423     /**
60424        \param pixel Reference to pixel value to test.
60425        \param[out] n Index of image containing the pixel value, if test succeeds.
60426        \param[out] x X-coordinate of the pixel value, if test succeeds.
60427        \param[out] y Y-coordinate of the pixel value, if test succeeds.
60428        \note If true, set coordinates (n,x,y).
60429     **/
60430     template<typename t>
60431     bool contains(const T& pixel, t& n, t& x, t&y) const {
60432       t z, c;
60433       return contains(pixel,n,x,y,z,c);
60434     }
60435 
60436     //! Test if one of the image list contains the specified referenced value.
60437     /**
60438        \param pixel Reference to pixel value to test.
60439        \param[out] n Index of image containing the pixel value, if test succeeds.
60440        \param[out] x X-coordinate of the pixel value, if test succeeds.
60441        \note If true, set coordinates (n,x).
60442     **/
60443     template<typename t>
60444     bool contains(const T& pixel, t& n, t& x) const {
60445       t y, z, c;
60446       return contains(pixel,n,x,y,z,c);
60447     }
60448 
60449     //! Test if one of the image list contains the specified referenced value.
60450     /**
60451        \param pixel Reference to pixel value to test.
60452        \param[out] n Index of image containing the pixel value, if test succeeds.
60453        \note If true, set coordinates (n).
60454     **/
60455     template<typename t>
60456     bool contains(const T& pixel, t& n) const {
60457       t x, y, z, c;
60458       return contains(pixel,n,x,y,z,c);
60459     }
60460 
60461     //! Test if one of the image list contains the specified referenced value.
60462     /**
60463        \param pixel Reference to pixel value to test.
60464     **/
60465     bool contains(const T& pixel) const {
60466       unsigned int n, x, y, z, c;
60467       return contains(pixel,n,x,y,z,c);
60468     }
60469 
60470     //! Test if the list contains the image 'img'.
60471     /**
60472        \param img Reference to image to test.
60473        \param[out] n Index of image in the list, if test succeeds.
60474        \note If true, returns the position (n) of the image in the list.
60475     **/
60476     template<typename t>
60477     bool contains(const CImg<T>& img, t& n) const {
60478       if (is_empty()) return false;
60479       const CImg<T> *const ptr = &img;
60480       cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
60481       return false;
60482     }
60483 
60484     //! Test if the list contains the image img.
60485     /**
60486        \param img Reference to image to test.
60487     **/
60488     bool contains(const CImg<T>& img) const {
60489       unsigned int n;
60490       return contains(img,n);
60491     }
60492 
60493     //@}
60494     //-------------------------------------
60495     //
60496     //! \name Mathematical Functions
60497     //@{
60498     //-------------------------------------
60499 
60500     //! Return a reference to the minimum pixel value of the instance list.
60501     /**
60502     **/
60503     T& min() {
60504       bool is_all_empty = true;
60505       T *ptr_min = 0;
60506       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60507         ptr_min = _data[l]._data;
60508         is_all_empty = false;
60509         break;
60510       }
60511       if (is_all_empty)
60512         throw CImgInstanceException(_cimglist_instance
60513                                     "min(): %s.",
60514                                     _data?"List of empty images":"Empty instance",
60515                                     cimglist_instance);
60516       T min_value = *ptr_min;
60517       cimglist_for(*this,l) {
60518         const CImg<T>& img = _data[l];
60519         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
60520       }
60521       return *ptr_min;
60522     }
60523 
60524     //! Return a reference to the minimum pixel value of the instance list \const.
60525     const T& min() const {
60526       bool is_all_empty = true;
60527       T *ptr_min = 0;
60528       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60529         ptr_min = _data[l]._data;
60530         is_all_empty = false;
60531         break;
60532       }
60533       if (is_all_empty)
60534         throw CImgInstanceException(_cimglist_instance
60535                                     "min(): %s.",
60536                                     _data?"List of empty images":"Empty instance",
60537                                     cimglist_instance);
60538       T min_value = *ptr_min;
60539       cimglist_for(*this,l) {
60540         const CImg<T>& img = _data[l];
60541         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
60542       }
60543       return *ptr_min;
60544     }
60545 
60546     //! Return a reference to the maximum pixel value of the instance list.
60547     /**
60548     **/
60549     T& max() {
60550       bool is_all_empty = true;
60551       T *ptr_max = 0;
60552       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60553         ptr_max = _data[l]._data;
60554         is_all_empty = false;
60555         break;
60556       }
60557       if (is_all_empty)
60558         throw CImgInstanceException(_cimglist_instance
60559                                     "max(): %s.",
60560                                     _data?"List of empty images":"Empty instance",
60561                                     cimglist_instance);
60562       T max_value = *ptr_max;
60563       cimglist_for(*this,l) {
60564         const CImg<T>& img = _data[l];
60565         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
60566       }
60567       return *ptr_max;
60568     }
60569 
60570     //! Return a reference to the maximum pixel value of the instance list \const.
60571     const T& max() const {
60572       bool is_all_empty = true;
60573       T *ptr_max = 0;
60574       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60575         ptr_max = _data[l]._data;
60576         is_all_empty = false;
60577         break;
60578       }
60579       if (is_all_empty)
60580         throw CImgInstanceException(_cimglist_instance
60581                                     "max(): %s.",
60582                                     _data?"List of empty images":"Empty instance",
60583                                     cimglist_instance);
60584       T max_value = *ptr_max;
60585       cimglist_for(*this,l) {
60586         const CImg<T>& img = _data[l];
60587         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
60588       }
60589       return *ptr_max;
60590     }
60591 
60592     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
60593     /**
60594        \param[out] max_val Value of the maximum value found.
60595     **/
60596     template<typename t>
60597     T& min_max(t& max_val) {
60598       bool is_all_empty = true;
60599       T *ptr_min = 0;
60600       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60601         ptr_min = _data[l]._data;
60602         is_all_empty = false;
60603         break;
60604       }
60605       if (is_all_empty)
60606         throw CImgInstanceException(_cimglist_instance
60607                                     "min_max(): %s.",
60608                                     _data?"List of empty images":"Empty instance",
60609                                     cimglist_instance);
60610       T min_value = *ptr_min, max_value = min_value;
60611       cimglist_for(*this,l) {
60612         const CImg<T>& img = _data[l];
60613         cimg_for(img,ptrs,T) {
60614           const T val = *ptrs;
60615           if (val<min_value) { min_value = val; ptr_min = ptrs; }
60616           if (val>max_value) max_value = val;
60617         }
60618       }
60619       max_val = (t)max_value;
60620       return *ptr_min;
60621     }
60622 
60623     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
60624     /**
60625        \param[out] max_val Value of the maximum value found.
60626     **/
60627     template<typename t>
60628     const T& min_max(t& max_val) const {
60629       bool is_all_empty = true;
60630       T *ptr_min = 0;
60631       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60632         ptr_min = _data[l]._data;
60633         is_all_empty = false;
60634         break;
60635       }
60636       if (is_all_empty)
60637         throw CImgInstanceException(_cimglist_instance
60638                                     "min_max(): %s.",
60639                                     _data?"List of empty images":"Empty instance",
60640                                     cimglist_instance);
60641       T min_value = *ptr_min, max_value = min_value;
60642       cimglist_for(*this,l) {
60643         const CImg<T>& img = _data[l];
60644         cimg_for(img,ptrs,T) {
60645           const T val = *ptrs;
60646           if (val<min_value) { min_value = val; ptr_min = ptrs; }
60647           if (val>max_value) max_value = val;
60648         }
60649       }
60650       max_val = (t)max_value;
60651       return *ptr_min;
60652     }
60653 
60654     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
60655     /**
60656        \param[out] min_val Value of the minimum value found.
60657     **/
60658     template<typename t>
60659     T& max_min(t& min_val) {
60660       bool is_all_empty = true;
60661       T *ptr_max = 0;
60662       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60663         ptr_max = _data[l]._data;
60664         is_all_empty = false;
60665         break;
60666       }
60667       if (is_all_empty)
60668         throw CImgInstanceException(_cimglist_instance
60669                                     "max_min(): %s.",
60670                                     _data?"List of empty images":"Empty instance",
60671                                     cimglist_instance);
60672       T min_value = *ptr_max, max_value = min_value;
60673       cimglist_for(*this,l) {
60674         const CImg<T>& img = _data[l];
60675         cimg_for(img,ptrs,T) {
60676           const T val = *ptrs;
60677           if (val>max_value) { max_value = val; ptr_max = ptrs; }
60678           if (val<min_value) min_value = val;
60679         }
60680       }
60681       min_val = (t)min_value;
60682       return *ptr_max;
60683     }
60684 
60685     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
60686     template<typename t>
60687     const T& max_min(t& min_val) const {
60688       bool is_all_empty = true;
60689       T *ptr_max = 0;
60690       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60691         ptr_max = _data[l]._data;
60692         is_all_empty = false;
60693         break;
60694       }
60695       if (is_all_empty)
60696         throw CImgInstanceException(_cimglist_instance
60697                                     "max_min(): %s.",
60698                                     _data?"List of empty images":"Empty instance",
60699                                     cimglist_instance);
60700       T min_value = *ptr_max, max_value = min_value;
60701       cimglist_for(*this,l) {
60702         const CImg<T>& img = _data[l];
60703         cimg_for(img,ptrs,T) {
60704           const T val = *ptrs;
60705           if (val>max_value) { max_value = val; ptr_max = ptrs; }
60706           if (val<min_value) min_value = val;
60707         }
60708       }
60709       min_val = (t)min_value;
60710       return *ptr_max;
60711     }
60712 
60713     //@}
60714     //---------------------------
60715     //
60716     //! \name List Manipulation
60717     //@{
60718     //---------------------------
60719 
60720     //! Insert a copy of the image \c img into the current image list, at position \c pos.
60721     /**
60722         \param img Image to insert a copy to the list.
60723         \param pos Index of the insertion.
60724         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
60725     **/
60726     template<typename t>
60727     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
60728       const unsigned int npos = pos==~0U?_width:pos;
60729       if (npos>_width)
60730         throw CImgArgumentException(_cimglist_instance
60731                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
60732                                     "at position %u.",
60733                                     cimglist_instance,
60734                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
60735       if (is_shared)
60736         throw CImgArgumentException(_cimglist_instance
60737                                     "insert(): Invalid insertion request of specified shared image "
60738                                     "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
60739                                     cimglist_instance,
60740                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
60741 
60742       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
60743                                                                         (_allocated_width=16)]:0;
60744       if (!_data) { // Insert new element into empty list
60745         _data = new_data;
60746         *_data = img;
60747       } else {
60748         if (new_data) { // Insert with re-allocation
60749           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
60750           if (npos!=_width - 1)
60751             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60752           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
60753           delete[] _data;
60754           _data = new_data;
60755         } else if (npos!=_width - 1) // Insert without re-allocation
60756           std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60757         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
60758         _data[npos]._data = 0;
60759         _data[npos] = img;
60760       }
60761       return *this;
60762     }
60763 
60764     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
60765     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
60766       const unsigned int npos = pos==~0U?_width:pos;
60767       if (npos>_width)
60768         throw CImgArgumentException(_cimglist_instance
60769                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
60770                                     "at position %u.",
60771                                     cimglist_instance,
60772                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
60773       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
60774                                                                         (_allocated_width=16)]:0;
60775       if (!_data) { // Insert new element into empty list
60776         _data = new_data;
60777         if (is_shared && img) {
60778           _data->_width = img._width;
60779           _data->_height = img._height;
60780           _data->_depth = img._depth;
60781           _data->_spectrum = img._spectrum;
60782           _data->_is_shared = true;
60783           _data->_data = img._data;
60784         } else *_data = img;
60785       }
60786       else {
60787         if (new_data) { // Insert with re-allocation
60788           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
60789           if (npos!=_width - 1)
60790             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60791           if (is_shared && img) {
60792             new_data[npos]._width = img._width;
60793             new_data[npos]._height = img._height;
60794             new_data[npos]._depth = img._depth;
60795             new_data[npos]._spectrum = img._spectrum;
60796             new_data[npos]._is_shared = true;
60797             new_data[npos]._data = img._data;
60798           } else {
60799             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
60800             new_data[npos]._data = 0;
60801             new_data[npos] = img;
60802           }
60803           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
60804           delete[] _data;
60805           _data = new_data;
60806         } else { // Insert without re-allocation
60807           if (npos!=_width - 1)
60808             std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60809           if (is_shared && img) {
60810             _data[npos]._width = img._width;
60811             _data[npos]._height = img._height;
60812             _data[npos]._depth = img._depth;
60813             _data[npos]._spectrum = img._spectrum;
60814             _data[npos]._is_shared = true;
60815             _data[npos]._data = img._data;
60816           } else {
60817             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
60818             _data[npos]._data = 0;
60819             _data[npos] = img;
60820           }
60821         }
60822       }
60823       return *this;
60824     }
60825 
60826     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
60827     template<typename t>
60828     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
60829       return (+*this).insert(img,pos,is_shared);
60830     }
60831 
60832     //! Insert n empty images img into the current image list, at position \p pos.
60833     /**
60834        \param n Number of empty images to insert.
60835        \param pos Index of the insertion.
60836     **/
60837     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
60838       CImg<T> empty;
60839       if (!n) return *this;
60840       const unsigned int npos = pos==~0U?_width:pos;
60841       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
60842       return *this;
60843     }
60844 
60845     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
60846     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
60847       return (+*this).insert(n,pos);
60848     }
60849 
60850     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
60851     /**
60852        \param n Number of image copies to insert.
60853        \param img Image to insert by copy.
60854        \param pos Index of the insertion.
60855        \param is_shared Tells if inserted images are shared copies of \c img or not.
60856     **/
60857     template<typename t>
60858     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
60859                         const bool is_shared=false) {
60860       if (!n) return *this;
60861       const unsigned int npos = pos==~0U?_width:pos;
60862       insert(img,npos,is_shared);
60863       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
60864       return *this;
60865     }
60866 
60867     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
60868     template<typename t>
60869     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
60870                            const bool is_shared=false) const {
60871       return (+*this).insert(n,img,pos,is_shared);
60872     }
60873 
60874     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
60875     /**
60876       \param list Image list to insert.
60877       \param pos Index of the insertion.
60878       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
60879     **/
60880     template<typename t>
60881     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
60882       const unsigned int npos = pos==~0U?_width:pos;
60883       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
60884       else insert(CImgList<T>(list),npos,is_shared);
60885       return *this;
60886     }
60887 
60888     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
60889     template<typename t>
60890     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
60891       return (+*this).insert(list,pos,is_shared);
60892     }
60893 
60894     //! Insert n copies of the list \c list at position \c pos of the current list.
60895     /**
60896       \param n Number of list copies to insert.
60897       \param list Image list to insert.
60898       \param pos Index of the insertion.
60899       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
60900     **/
60901     template<typename t>
60902     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
60903                         const bool is_shared=false) {
60904       if (!n) return *this;
60905       const unsigned int npos = pos==~0U?_width:pos;
60906       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
60907       return *this;
60908     }
60909 
60910     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
60911     template<typename t>
60912     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
60913                            const bool is_shared=false) const {
60914       return (+*this).insert(n,list,pos,is_shared);
60915     }
60916 
60917     //! Remove all images between from indexes.
60918     /**
60919       \param pos1 Starting index of the removal.
60920       \param pos2 Ending index of the removal.
60921     **/
60922     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
60923       const unsigned int
60924         npos1 = pos1<pos2?pos1:pos2,
60925         tpos2 = pos1<pos2?pos2:pos1,
60926         npos2 = tpos2<_width?tpos2:_width - 1;
60927       if (npos1>=_width)
60928         throw CImgArgumentException(_cimglist_instance
60929                                     "remove(): Invalid remove request at positions %u->%u.",
60930                                     cimglist_instance,
60931                                     npos1,tpos2);
60932       else {
60933         if (tpos2>=_width)
60934           throw CImgArgumentException(_cimglist_instance
60935                                       "remove(): Invalid remove request at positions %u->%u.",
60936                                       cimglist_instance,
60937                                       npos1,tpos2);
60938 
60939         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
60940         const unsigned int nb = 1 + npos2 - npos1;
60941         if (!(_width-=nb)) return assign();
60942         if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
60943           if (npos1!=_width)
60944             std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
60945           std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
60946         } else { // Removing items with reallocation
60947           _allocated_width>>=4;
60948           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
60949           CImg<T> *const new_data = new CImg<T>[_allocated_width];
60950           if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
60951           if (npos1!=_width)
60952             std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
60953           if (_width!=_allocated_width)
60954             std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
60955           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
60956           delete[] _data;
60957           _data = new_data;
60958         }
60959       }
60960       return *this;
60961     }
60962 
60963     //! Remove all images between from indexes \newinstance.
60964     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
60965       return (+*this).remove(pos1,pos2);
60966     }
60967 
60968     //! Remove image at index \c pos from the image list.
60969     /**
60970       \param pos Index of the image to remove.
60971     **/
60972     CImgList<T>& remove(const unsigned int pos) {
60973       return remove(pos,pos);
60974     }
60975 
60976     //! Remove image at index \c pos from the image list \newinstance.
60977     CImgList<T> get_remove(const unsigned int pos) const {
60978       return (+*this).remove(pos);
60979     }
60980 
60981     //! Remove last image.
60982     /**
60983     **/
60984     CImgList<T>& remove() {
60985       return remove(_width - 1);
60986     }
60987 
60988     //! Remove last image \newinstance.
60989     CImgList<T> get_remove() const {
60990       return (+*this).remove();
60991     }
60992 
60993     //! Reverse list order.
60994     CImgList<T>& reverse() {
60995       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
60996       return *this;
60997     }
60998 
60999     //! Reverse list order \newinstance.
61000     CImgList<T> get_reverse() const {
61001       return (+*this).reverse();
61002     }
61003 
61004     //! Return a sublist.
61005     /**
61006       \param pos0 Starting index of the sublist.
61007       \param pos1 Ending index of the sublist.
61008     **/
61009     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
61010       return get_images(pos0,pos1).move_to(*this);
61011     }
61012 
61013     //! Return a sublist \newinstance.
61014     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
61015       if (pos0>pos1 || pos1>=_width)
61016         throw CImgArgumentException(_cimglist_instance
61017                                     "images(): Specified sub-list indices (%u->%u) are out of bounds.",
61018                                     cimglist_instance,
61019                                     pos0,pos1);
61020       CImgList<T> res(pos1 - pos0 + 1);
61021       cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
61022       return res;
61023     }
61024 
61025     //! Return a shared sublist.
61026     /**
61027       \param pos0 Starting index of the sublist.
61028       \param pos1 Ending index of the sublist.
61029     **/
61030     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
61031       if (pos0>pos1 || pos1>=_width)
61032         throw CImgArgumentException(_cimglist_instance
61033                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
61034                                     cimglist_instance,
61035                                     pos0,pos1);
61036       CImgList<T> res(pos1 - pos0 + 1);
61037       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
61038       return res;
61039     }
61040 
61041     //! Return a shared sublist \newinstance.
61042     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
61043       if (pos0>pos1 || pos1>=_width)
61044         throw CImgArgumentException(_cimglist_instance
61045                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
61046                                     cimglist_instance,
61047                                     pos0,pos1);
61048       CImgList<T> res(pos1 - pos0 + 1);
61049       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
61050       return res;
61051     }
61052 
61053     //! Return a single image which is the appending of all images of the current CImgList instance.
61054     /**
61055        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
61056        \param align Appending alignment.
61057     **/
61058     CImg<T> get_append(const char axis, const float align=0) const {
61059       if (is_empty()) return CImg<T>();
61060       if (_width==1) return +((*this)[0]);
61061       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
61062       CImg<T> res;
61063       switch (cimg::lowercase(axis)) {
61064       case 'x' : { // Along the X-axis
61065         cimglist_for(*this,l) {
61066           const CImg<T>& img = (*this)[l];
61067           if (img) {
61068             dx+=img._width;
61069             dy = std::max(dy,img._height);
61070             dz = std::max(dz,img._depth);
61071             dc = std::max(dc,img._spectrum);
61072           }
61073         }
61074         res.assign(dx,dy,dz,dc,(T)0);
61075         if (res) cimglist_for(*this,l) {
61076             const CImg<T>& img = (*this)[l];
61077             if (img) {
61078               if (img._height==1 && img._depth==1 && img._spectrum==1 &&
61079                   res._height==1 && res._depth==1 && res._spectrum==1)
61080                 std::memcpy(&res[pos],img._data,sizeof(T)*img._width);
61081               else
61082                 res.draw_image(pos,
61083                                (int)(align*(dy - img._height)),
61084                                (int)(align*(dz - img._depth)),
61085                                (int)(align*(dc - img._spectrum)),
61086                                img);
61087             }
61088             pos+=img._width;
61089           }
61090       } break;
61091       case 'y' : { // Along the Y-axis
61092         cimglist_for(*this,l) {
61093           const CImg<T>& img = (*this)[l];
61094           if (img) {
61095             dx = std::max(dx,img._width);
61096             dy+=img._height;
61097             dz = std::max(dz,img._depth);
61098             dc = std::max(dc,img._spectrum);
61099           }
61100         }
61101         res.assign(dx,dy,dz,dc,(T)0);
61102         if (res) cimglist_for(*this,l) {
61103             const CImg<T>& img = (*this)[l];
61104             if (img) {
61105               if (img._width==1 && img._depth==1 && img._spectrum==1 &&
61106                   res._width==1 && res._depth==1 && res._spectrum==1)
61107                 std::memcpy(&res[pos],img._data,sizeof(T)*img._height);
61108               else
61109                 res.draw_image((int)(align*(dx - img._width)),
61110                                pos,
61111                                (int)(align*(dz - img._depth)),
61112                                (int)(align*(dc - img._spectrum)),
61113                                img);
61114             }
61115             pos+=img._height;
61116           }
61117       } break;
61118       case 'z' : { // Along the Z-axis
61119         cimglist_for(*this,l) {
61120           const CImg<T>& img = (*this)[l];
61121           if (img) {
61122             dx = std::max(dx,img._width);
61123             dy = std::max(dy,img._height);
61124             dz+=img._depth;
61125             dc = std::max(dc,img._spectrum);
61126           }
61127         }
61128         res.assign(dx,dy,dz,dc,(T)0);
61129         if (res) cimglist_for(*this,l) {
61130             const CImg<T>& img = (*this)[l];
61131             if (img) {
61132               if (img._width==1 && img._height==1 && img._spectrum==1 &&
61133                   res._width==1 && res._height==1 && res._spectrum==1)
61134                 std::memcpy(&res[pos],img._data,sizeof(T)*img._depth);
61135               else
61136                 res.draw_image((int)(align*(dx - img._width)),
61137                                (int)(align*(dy - img._height)),
61138                                pos,
61139                                (int)(align*(dc - img._spectrum)),
61140                                img);
61141             }
61142             pos+=img._depth;
61143           }
61144       } break;
61145       default : { // Along the C-axis
61146         cimglist_for(*this,l) {
61147           const CImg<T>& img = (*this)[l];
61148           if (img) {
61149             dx = std::max(dx,img._width);
61150             dy = std::max(dy,img._height);
61151             dz = std::max(dz,img._depth);
61152             dc+=img._spectrum;
61153           }
61154         }
61155         res.assign(dx,dy,dz,dc,(T)0);
61156         if (res) cimglist_for(*this,l) {
61157             const CImg<T>& img = (*this)[l];
61158             if (img) {
61159               if (img._width==1 && img._height==1 && img._depth==1 &&
61160                   res._width==1 && res._height==1 && res._depth==1)
61161                 std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum);
61162               else
61163                 res.draw_image((int)(align*(dx - img._width)),
61164                                (int)(align*(dy - img._height)),
61165                                (int)(align*(dz - img._depth)),
61166                                pos,
61167                                img);
61168             }
61169             pos+=img._spectrum;
61170           }
61171       }
61172       }
61173       return res;
61174     }
61175 
61176     //! Return a list where each image has been split along the specified axis.
61177     /**
61178         \param axis Axis to split images along.
61179         \param nb Number of split parts for each image.
61180     **/
61181     CImgList<T>& split(const char axis, const int nb=-1) {
61182       return get_split(axis,nb).move_to(*this);
61183     }
61184 
61185     //! Return a list where each image has been split along the specified axis \newinstance.
61186     CImgList<T> get_split(const char axis, const int nb=-1) const {
61187       CImgList<T> res;
61188       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
61189       return res;
61190     }
61191 
61192     //! Insert image at the end of the list.
61193     /**
61194       \param img Image to insert.
61195     **/
61196     template<typename t>
61197     CImgList<T>& push_back(const CImg<t>& img) {
61198       return insert(img);
61199     }
61200 
61201     //! Insert image at the front of the list.
61202     /**
61203       \param img Image to insert.
61204     **/
61205     template<typename t>
61206     CImgList<T>& push_front(const CImg<t>& img) {
61207       return insert(img,0);
61208     }
61209 
61210     //! Insert list at the end of the current list.
61211     /**
61212       \param list List to insert.
61213     **/
61214     template<typename t>
61215     CImgList<T>& push_back(const CImgList<t>& list) {
61216       return insert(list);
61217     }
61218 
61219     //! Insert list at the front of the current list.
61220     /**
61221       \param list List to insert.
61222     **/
61223     template<typename t>
61224     CImgList<T>& push_front(const CImgList<t>& list) {
61225       return insert(list,0);
61226     }
61227 
61228     //! Remove last image.
61229     /**
61230     **/
61231     CImgList<T>& pop_back() {
61232       return remove(_width - 1);
61233     }
61234 
61235     //! Remove first image.
61236     /**
61237     **/
61238     CImgList<T>& pop_front() {
61239       return remove(0);
61240     }
61241 
61242     //! Remove image pointed by iterator.
61243     /**
61244       \param iter Iterator pointing to the image to remove.
61245     **/
61246     CImgList<T>& erase(const iterator iter) {
61247       return remove(iter - _data);
61248     }
61249 
61250     //@}
61251     //----------------------------------
61252     //
61253     //! \name Data Input
61254     //@{
61255     //----------------------------------
61256 
61257     //! Display a simple interactive interface to select images or sublists.
61258     /**
61259        \param disp Window instance to display selection and user interface.
61260        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
61261        \param axis Axis along whom images are appended for visualization.
61262        \param align Alignment setting when images have not all the same size.
61263        \param exit_on_anykey Exit function when any key is pressed.
61264        \return A one-column vector containing the selected image indexes.
61265     **/
61266     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
61267                           const char axis='x', const float align=0,
61268                           const bool exit_on_anykey=false) const {
61269       return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
61270     }
61271 
61272     //! Display a simple interactive interface to select images or sublists.
61273     /**
61274        \param title Title of a new window used to display selection and user interface.
61275        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
61276        \param axis Axis along whom images are appended for visualization.
61277        \param align Alignment setting when images have not all the same size.
61278        \param exit_on_anykey Exit function when any key is pressed.
61279        \return A one-column vector containing the selected image indexes.
61280     **/
61281     CImg<intT> get_select(const char *const title, const bool feature_type=true,
61282                           const char axis='x', const float align=0,
61283                           const bool exit_on_anykey=false) const {
61284       CImgDisplay disp;
61285       return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
61286     }
61287 
61288     CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
61289                        const char axis, const float align, const bool exit_on_anykey,
61290                        const unsigned int orig, const bool resize_disp,
61291                        const bool exit_on_rightbutton, const bool exit_on_wheel) const {
61292       if (is_empty())
61293         throw CImgInstanceException(_cimglist_instance
61294                                     "select(): Empty instance.",
61295                                     cimglist_instance);
61296 
61297       // Create image correspondence table and get list dimensions for visualization.
61298       CImgList<uintT> _indices;
61299       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
61300       cimglist_for(*this,l) {
61301         const CImg<T>& img = _data[l];
61302         const unsigned int
61303           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
61304           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
61305         if (w>max_width) max_width = w;
61306         if (h>max_height) max_height = h;
61307         sum_width+=w; sum_height+=h;
61308         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
61309         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
61310       }
61311       const CImg<uintT> indices0 = _indices>'x';
61312 
61313       // Create display window.
61314       if (!disp) {
61315         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
61316         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
61317         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
61318       } else {
61319         if (title) disp.set_title("%s",title);
61320         disp.move_inside_screen();
61321       }
61322       if (resize_disp) {
61323         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
61324         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
61325       }
61326 
61327       const unsigned int old_normalization = disp.normalization();
61328       bool old_is_resized = disp.is_resized();
61329       disp._normalization = 0;
61330       disp.show().set_key(0).show_mouse();
61331       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
61332 
61333       // Enter event loop.
61334       CImg<ucharT> visu0, visu;
61335       CImg<uintT> indices;
61336       CImg<intT> positions(_width,4,1,1,-1);
61337       int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1;
61338       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
61339       unsigned int key = 0, font_size = 32;
61340 
61341       while (!is_selected && !disp.is_closed() && !key) {
61342 
61343         // Create background image.
61344         if (!visu0) {
61345           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
61346           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
61347           unsigned int _ind = 0;
61348           const CImg<T> onexone(1,1,1,1,(T)0);
61349           if (axis=='x')
61350             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
61351             cimglist_for(*this,ind) {
61352               unsigned int x0 = 0;
61353               while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
61354               unsigned int x1 = x0;
61355               while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
61356               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
61357               CImg<ucharT> res;
61358               src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2).
61359                 move_to(res);
61360               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
61361               res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
61362               positions(ind,0) = positions(ind,2) = (int)x0;
61363               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
61364               positions(ind,2)+=res._width;
61365               positions(ind,3)+=res._height - 1;
61366               visu0.draw_image(positions(ind,0),positions(ind,1),res);
61367             }
61368           else
61369             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
61370             cimglist_for(*this,ind) {
61371               unsigned int y0 = 0;
61372               while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
61373               unsigned int y1 = y0;
61374               while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
61375               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
61376               CImg<ucharT> res;
61377               src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
61378                 move_to(res);
61379               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
61380               res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
61381               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
61382               positions(ind,1) = positions(ind,3) = (int)y0;
61383               positions(ind,2)+=res._width - 1;
61384               positions(ind,3)+=res._height;
61385               visu0.draw_image(positions(ind,0),positions(ind,1),res);
61386             }
61387           if (axis=='x') --positions(_ind,2); else --positions(_ind,3);
61388           update_display = true;
61389         }
61390 
61391         if (!visu || oindex0!=index0 || oindex1!=index1) {
61392           if (index0>=0 && index1>=0) {
61393             visu.assign(visu0,false);
61394             const int indm = std::min(index0,index1), indM = std::max(index0,index1);
61395             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
61396                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
61397                                     background_color,0.2f);
61398                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
61399                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
61400                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
61401                                       foreground_color,0.9f,0xAAAAAAAA);
61402               }
61403             if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down,
61404                                              orig + indm,orig + indM,indM - indm + 1);
61405             else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down,
61406                                   orig + index0,
61407                                   _data[index0]._width,
61408                                   _data[index0]._height,
61409                                   _data[index0]._depth,
61410                                   _data[index0]._spectrum);
61411             update_display = true;
61412           } else visu.assign();
61413         }
61414         if (!visu) { visu.assign(visu0,true); update_display = true; }
61415         if (update_display) { visu.display(disp); update_display = false; }
61416         disp.wait();
61417 
61418         // Manage user events.
61419         const int xm = disp.mouse_x(), ym = disp.mouse_y();
61420         int index = -1;
61421 
61422         if (xm>=0) {
61423           index = (int)indices(axis=='x'?xm:ym);
61424           if (disp.button()&1) {
61425             if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; }
61426             oindex1 = index1; index1 = index;
61427             if (!feature_type) is_selected = true;
61428           } else {
61429             if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; }
61430             else is_selected = true;
61431           }
61432         } else {
61433           if (is_clicked) {
61434             if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; }
61435             else index1 = -1;
61436           } else index0 = index1 = -1;
61437         }
61438 
61439         if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; }
61440         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; }
61441         if (disp.wheel() && exit_on_wheel) is_selected = true;
61442 
61443         CImg<charT> filename(32);
61444         switch (key = disp.key()) {
61445 #if cimg_OS!=2
61446         case cimg::keyCTRLRIGHT :
61447 #endif
61448         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
61449         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61450             disp.set_fullscreen(false).
61451               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
61452                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
61453               _is_resized = true;
61454             disp.set_key(key,false); key = 0; visu0.assign();
61455           } break;
61456         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61457             disp.set_fullscreen(false).
61458               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
61459             disp.set_key(key,false); key = 0; visu0.assign();
61460           } break;
61461         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61462             disp.set_fullscreen(false).
61463               resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
61464               _is_resized = true;
61465             disp.set_key(key,false); key = 0; visu0.assign();
61466           } break;
61467         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61468             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
61469             disp.set_key(key,false); key = 0; visu0.assign();
61470           } break;
61471         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61472             static unsigned int snap_number = 0;
61473             std::FILE *file;
61474             do {
61475               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
61476               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
61477             } while (file);
61478             if (visu0) {
61479               (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp);
61480               visu0.save(filename);
61481               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
61482             }
61483             disp.set_key(key,false).wait(); key = 0;
61484           } break;
61485         case cimg::keyO :
61486           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
61487             static unsigned int snap_number = 0;
61488             std::FILE *file;
61489             do {
61490 #ifdef cimg_use_zlib
61491               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
61492 #else
61493               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
61494 #endif
61495               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
61496             } while (file);
61497             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
61498             save(filename);
61499             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
61500             disp.set_key(key,false).wait(); key = 0;
61501           } break;
61502         }
61503         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
61504         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
61505         else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
61506         if (!exit_on_anykey && key && key!=cimg::keyESC &&
61507             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
61508           key = 0;
61509         }
61510       }
61511       CImg<intT> res(1,2,1,1,-1);
61512       if (is_selected) {
61513         if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1));
61514         else res.fill(index0);
61515       }
61516       if (!(disp.button()&2)) disp.set_button();
61517       disp._normalization = old_normalization;
61518       disp._is_resized = old_is_resized;
61519       disp.set_key(key);
61520       return res;
61521     }
61522 
61523     //! Load a list from a file.
61524     /**
61525      \param filename Filename to read data from.
61526     **/
61527     CImgList<T>& load(const char *const filename) {
61528       if (!filename)
61529         throw CImgArgumentException(_cimglist_instance
61530                                     "load(): Specified filename is (null).",
61531                                     cimglist_instance);
61532 
61533       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
61534         CImg<charT> filename_local(256);
61535         load(cimg::load_network(filename,filename_local));
61536         std::remove(filename_local);
61537         return *this;
61538       }
61539 
61540       const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
61541       const char *const ext = cimg::split_filename(filename);
61542       const unsigned int omode = cimg::exception_mode();
61543       cimg::exception_mode(0);
61544       bool is_loaded = true;
61545       try {
61546 #ifdef cimglist_load_plugin
61547         cimglist_load_plugin(filename);
61548 #endif
61549 #ifdef cimglist_load_plugin1
61550         cimglist_load_plugin1(filename);
61551 #endif
61552 #ifdef cimglist_load_plugin2
61553         cimglist_load_plugin2(filename);
61554 #endif
61555 #ifdef cimglist_load_plugin3
61556         cimglist_load_plugin3(filename);
61557 #endif
61558 #ifdef cimglist_load_plugin4
61559         cimglist_load_plugin4(filename);
61560 #endif
61561 #ifdef cimglist_load_plugin5
61562         cimglist_load_plugin5(filename);
61563 #endif
61564 #ifdef cimglist_load_plugin6
61565         cimglist_load_plugin6(filename);
61566 #endif
61567 #ifdef cimglist_load_plugin7
61568         cimglist_load_plugin7(filename);
61569 #endif
61570 #ifdef cimglist_load_plugin8
61571         cimglist_load_plugin8(filename);
61572 #endif
61573         if (!cimg::strcasecmp(ext,"tif") ||
61574             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
61575         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
61576         else if (!cimg::strcasecmp(ext,"cimg") ||
61577                  !cimg::strcasecmp(ext,"cimgz") ||
61578                  !*ext) load_cimg(filename);
61579         else if (!cimg::strcasecmp(ext,"rec") ||
61580                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
61581         else if (!cimg::strcasecmp(ext,"avi") ||
61582                  !cimg::strcasecmp(ext,"mov") ||
61583                  !cimg::strcasecmp(ext,"asf") ||
61584                  !cimg::strcasecmp(ext,"divx") ||
61585                  !cimg::strcasecmp(ext,"flv") ||
61586                  !cimg::strcasecmp(ext,"mpg") ||
61587                  !cimg::strcasecmp(ext,"m1v") ||
61588                  !cimg::strcasecmp(ext,"m2v") ||
61589                  !cimg::strcasecmp(ext,"m4v") ||
61590                  !cimg::strcasecmp(ext,"mjp") ||
61591                  !cimg::strcasecmp(ext,"mp4") ||
61592                  !cimg::strcasecmp(ext,"mkv") ||
61593                  !cimg::strcasecmp(ext,"mpe") ||
61594                  !cimg::strcasecmp(ext,"movie") ||
61595                  !cimg::strcasecmp(ext,"ogm") ||
61596                  !cimg::strcasecmp(ext,"ogg") ||
61597                  !cimg::strcasecmp(ext,"ogv") ||
61598                  !cimg::strcasecmp(ext,"qt") ||
61599                  !cimg::strcasecmp(ext,"rm") ||
61600                  !cimg::strcasecmp(ext,"vob") ||
61601                  !cimg::strcasecmp(ext,"webm") ||
61602                  !cimg::strcasecmp(ext,"wmv") ||
61603                  !cimg::strcasecmp(ext,"xvid") ||
61604                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
61605         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
61606         else is_loaded = false;
61607       } catch (CImgIOException&) { is_loaded = false; }
61608 
61609       // If nothing loaded, try to guess file format from magic number in file.
61610       if (!is_loaded && !is_stdin) {
61611         std::FILE *const file = cimg::std_fopen(filename,"rb");
61612         if (!file) {
61613           cimg::exception_mode(omode);
61614           throw CImgIOException(_cimglist_instance
61615                                 "load(): Failed to open file '%s'.",
61616                                 cimglist_instance,
61617                                 filename);
61618         }
61619 
61620         const char *const f_type = cimg::ftype(file,filename);
61621         cimg::fclose(file);
61622         is_loaded = true;
61623         try {
61624           if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
61625           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
61626           else is_loaded = false;
61627         } catch (CImgIOException&) { is_loaded = false; }
61628       }
61629 
61630       // If nothing loaded, try to load file as a single image.
61631       if (!is_loaded) {
61632         assign(1);
61633         try {
61634           _data->load(filename);
61635         } catch (CImgIOException&) {
61636           cimg::exception_mode(omode);
61637           throw CImgIOException(_cimglist_instance
61638                                 "load(): Failed to recognize format of file '%s'.",
61639                                 cimglist_instance,
61640                                 filename);
61641         }
61642       }
61643       cimg::exception_mode(omode);
61644       return *this;
61645     }
61646 
61647     //! Load a list from a file \newinstance.
61648     static CImgList<T> get_load(const char *const filename) {
61649       return CImgList<T>().load(filename);
61650     }
61651 
61652     //! Load a list from a .cimg file.
61653     /**
61654       \param filename Filename to read data from.
61655     **/
61656     CImgList<T>& load_cimg(const char *const filename) {
61657       return _load_cimg(0,filename);
61658     }
61659 
61660     //! Load a list from a .cimg file \newinstance.
61661     static CImgList<T> get_load_cimg(const char *const filename) {
61662       return CImgList<T>().load_cimg(filename);
61663     }
61664 
61665     //! Load a list from a .cimg file.
61666     /**
61667       \param file File to read data from.
61668     **/
61669     CImgList<T>& load_cimg(std::FILE *const file) {
61670       return _load_cimg(file,0);
61671     }
61672 
61673     //! Load a list from a .cimg file \newinstance.
61674     static CImgList<T> get_load_cimg(std::FILE *const file) {
61675       return CImgList<T>().load_cimg(file);
61676     }
61677 
61678     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
61679 #ifdef cimg_use_zlib
61680 #define _cimgz_load_cimg_case(Tss) { \
61681    Bytef *const cbuf = new Bytef[csiz]; \
61682    cimg::fread(cbuf,(size_t)csiz,nfile); \
61683    if (is_bool) { \
61684      CImg<ucharT> raw(W*H*D*C/8); \
61685      uLongf destlen = (uLongf)raw.size(); \
61686      uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
61687      img.assign(W,H,D,C); \
61688      img._uchar2bool(raw,raw.size(),false); \
61689    } else { \
61690      CImg<Tss> raw(W,H,D,C); \
61691      uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \
61692      uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
61693      if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
61694      raw.move_to(img); \
61695    } \
61696    delete[] cbuf; \
61697 }
61698 #else
61699 #define _cimgz_load_cimg_case(Tss) \
61700    throw CImgIOException(_cimglist_instance \
61701                          "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
61702                          cimglist_instance, \
61703                          filename?filename:"(FILE*)");
61704 #endif
61705 
61706 #define _cimg_load_cimg_case(Ts,Tss) \
61707       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
61708         const bool is_bool = cimg::type<Tss>::string()==cimg::type<bool>::string(); \
61709         for (unsigned int l = 0; l<N; ++l) { \
61710           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
61711           W = H = D = C = 0; csiz = 0; \
61712           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
61713             throw CImgIOException(_cimglist_instance \
61714                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
61715                                   cimglist_instance, \
61716                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
61717           if (W*H*D*C>0) { \
61718             CImg<T> &img = _data[l]; \
61719             if (err==5) _cimgz_load_cimg_case(Tss) \
61720             else { \
61721               img.assign(W,H,D,C); \
61722               T *ptrd = img._data; \
61723               if (is_bool) { \
61724                 CImg<ucharT> raw; \
61725                 for (ulongT to_read = img.size(); to_read; ) { \
61726                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
61727                   cimg::fread(raw._data,raw._width,nfile); \
61728                   CImg<T>(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\
61729                     _uchar2bool(raw,raw._width,false); \
61730                   to_read-=raw._width; \
61731                 } \
61732               } else { \
61733                 CImg<Tss> raw; \
61734                 for (ulongT to_read = img.size(); to_read; ) { \
61735                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
61736                   cimg::fread(raw._data,raw._width,nfile); \
61737                   if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
61738                   const Tss *ptrs = raw._data; \
61739                   for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
61740                   to_read-=raw._width; \
61741                 } \
61742               } \
61743             } \
61744           } \
61745         } \
61746         loaded = true; \
61747       }
61748 
61749       if (!filename && !file)
61750         throw CImgArgumentException(_cimglist_instance
61751                                     "load_cimg(): Specified filename is (null).",
61752                                     cimglist_instance);
61753 
61754       const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
61755       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
61756       bool loaded = false, endian = cimg::endianness();
61757       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
61758       *tmp = *str_pixeltype = *str_endian = 0;
61759       unsigned int j, N = 0, W, H, D, C;
61760       cimg_uint64 csiz;
61761       int i, err;
61762       do {
61763         j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
61764       } while (*tmp=='#' && i>=0);
61765       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
61766                         &N,str_pixeltype._data,str_endian._data);
61767       if (err<2) {
61768         if (!file) cimg::fclose(nfile);
61769         throw CImgIOException(_cimglist_instance
61770                               "load_cimg(): CImg header not found in file '%s'.",
61771                               cimglist_instance,
61772                               filename?filename:"(FILE*)");
61773       }
61774       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
61775       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
61776       assign(N);
61777       _cimg_load_cimg_case("bool",bool);
61778       _cimg_load_cimg_case("unsigned_char",unsigned char);
61779       _cimg_load_cimg_case("uchar",unsigned char);
61780       _cimg_load_cimg_case("char",char);
61781       _cimg_load_cimg_case("unsigned_short",unsigned short);
61782       _cimg_load_cimg_case("ushort",unsigned short);
61783       _cimg_load_cimg_case("short",short);
61784       _cimg_load_cimg_case("unsigned_int",unsigned int);
61785       _cimg_load_cimg_case("uint",unsigned int);
61786       _cimg_load_cimg_case("int",int);
61787       _cimg_load_cimg_case("unsigned_long",ulongT);
61788       _cimg_load_cimg_case("ulong",ulongT);
61789       _cimg_load_cimg_case("long",longT);
61790       _cimg_load_cimg_case("unsigned_int64",uint64T);
61791       _cimg_load_cimg_case("uint64",uint64T);
61792       _cimg_load_cimg_case("int64",int64T);
61793       _cimg_load_cimg_case("float",float);
61794       _cimg_load_cimg_case("double",double);
61795 
61796       if (!loaded) {
61797         if (!file) cimg::fclose(nfile);
61798         throw CImgIOException(_cimglist_instance
61799                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
61800                               cimglist_instance,
61801                               str_pixeltype._data,filename?filename:"(FILE*)");
61802       }
61803       if (!file) cimg::fclose(nfile);
61804       return *this;
61805     }
61806 
61807     //! Load a sublist list from a (non compressed) .cimg file.
61808     /**
61809       \param filename Filename to read data from.
61810       \param n0 Starting index of images to read (~0U for max).
61811       \param n1 Ending index of images to read (~0U for max).
61812       \param x0 Starting X-coordinates of image regions to read.
61813       \param y0 Starting Y-coordinates of image regions to read.
61814       \param z0 Starting Z-coordinates of image regions to read.
61815       \param c0 Starting C-coordinates of image regions to read.
61816       \param x1 Ending X-coordinates of image regions to read (~0U for max).
61817       \param y1 Ending Y-coordinates of image regions to read (~0U for max).
61818       \param z1 Ending Z-coordinates of image regions to read (~0U for max).
61819       \param c1 Ending C-coordinates of image regions to read (~0U for max).
61820     **/
61821     CImgList<T>& load_cimg(const char *const filename,
61822                            const unsigned int n0, const unsigned int n1,
61823                            const unsigned int x0, const unsigned int y0,
61824                            const unsigned int z0, const unsigned int c0,
61825                            const unsigned int x1, const unsigned int y1,
61826                            const unsigned int z1, const unsigned int c1) {
61827       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61828     }
61829 
61830     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
61831     static CImgList<T> get_load_cimg(const char *const filename,
61832                                      const unsigned int n0, const unsigned int n1,
61833                                      const unsigned int x0, const unsigned int y0,
61834                                      const unsigned int z0, const unsigned int c0,
61835                                      const unsigned int x1, const unsigned int y1,
61836                                      const unsigned int z1, const unsigned int c1) {
61837       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61838     }
61839 
61840     //! Load a sub-image list from a (non compressed) .cimg file \overloading.
61841     CImgList<T>& load_cimg(std::FILE *const file,
61842                            const unsigned int n0, const unsigned int n1,
61843                            const unsigned int x0, const unsigned int y0,
61844                            const unsigned int z0, const unsigned int c0,
61845                            const unsigned int x1, const unsigned int y1,
61846                            const unsigned int z1, const unsigned int c1) {
61847       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61848     }
61849 
61850     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
61851     static CImgList<T> get_load_cimg(std::FILE *const file,
61852                                      const unsigned int n0, const unsigned int n1,
61853                                      const unsigned int x0, const unsigned int y0,
61854                                      const unsigned int z0, const unsigned int c0,
61855                                      const unsigned int x1, const unsigned int y1,
61856                                      const unsigned int z1, const unsigned int c1) {
61857       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61858     }
61859 
61860     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
61861                             const unsigned int n0, const unsigned int n1,
61862                             const unsigned int x0, const unsigned int y0,
61863                             const unsigned int z0, const unsigned int c0,
61864                             const unsigned int x1, const unsigned int y1,
61865                             const unsigned int z1, const unsigned int c1) {
61866 #define _cimg_load_cimg_case2(Ts,Tss) \
61867       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
61868         for (unsigned int l = 0; l<=nn1; ++l) { \
61869           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
61870           W = H = D = C = 0; \
61871           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
61872             throw CImgIOException(_cimglist_instance \
61873                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
61874                                   cimglist_instance, \
61875                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
61876           if (W*H*D*C>0) { \
61877             if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
61878             else { \
61879               const unsigned int \
61880                 _nx1 = nx1==~0U?W - 1:nx1, \
61881                 _ny1 = ny1==~0U?H - 1:ny1, \
61882                 _nz1 = nz1==~0U?D - 1:nz1, \
61883                 _nc1 = nc1==~0U?C - 1:nc1; \
61884               if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
61885                 throw CImgArgumentException(_cimglist_instance \
61886                                             "load_cimg(): Invalid specified coordinates " \
61887                                             "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
61888                                             "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
61889                                             cimglist_instance, \
61890                                             n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
61891               CImg<Tss> raw(1 + _nx1 - nx0); \
61892               CImg<T> &img = _data[l - nn0]; \
61893               img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
61894               T *ptrd = img._data; \
61895               ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
61896               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
61897               for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
61898                 const ulongT skipzb = nz0*W*H*sizeof(Tss); \
61899                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
61900                 for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
61901                   const ulongT skipyb = ny0*W*sizeof(Tss); \
61902                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
61903                   for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
61904                     const ulongT skipxb = nx0*sizeof(Tss); \
61905                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
61906                     cimg::fread(raw._data,raw._width,nfile); \
61907                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
61908                     const Tss *ptrs = raw._data; \
61909                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
61910                     const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
61911                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
61912                   } \
61913                   const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
61914                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
61915                 } \
61916                 const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
61917                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
61918               } \
61919               const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
61920               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
61921             } \
61922           } \
61923         } \
61924         loaded = true; \
61925       }
61926 
61927       if (!filename && !file)
61928         throw CImgArgumentException(_cimglist_instance
61929                                     "load_cimg(): Specified filename is (null).",
61930                                     cimglist_instance);
61931       unsigned int
61932         nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
61933         nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
61934         ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
61935         nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
61936         nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
61937 
61938       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
61939       bool loaded = false, endian = cimg::endianness();
61940       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
61941       *tmp = *str_pixeltype = *str_endian = 0;
61942       unsigned int j, N, W, H, D, C;
61943       int i, err;
61944       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
61945       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
61946                         &N,str_pixeltype._data,str_endian._data);
61947       if (err<2) {
61948         if (!file) cimg::fclose(nfile);
61949         throw CImgIOException(_cimglist_instance
61950                               "load_cimg(): CImg header not found in file '%s'.",
61951                               cimglist_instance,
61952                               filename?filename:"(FILE*)");
61953       }
61954       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
61955       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
61956       nn1 = n1==~0U?N - 1:n1;
61957       if (nn1>=N)
61958         throw CImgArgumentException(_cimglist_instance
61959                                     "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
61960                                     "because file '%s' contains only %u images.",
61961                                     cimglist_instance,
61962                                     n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
61963       assign(1 + nn1 - n0);
61964       _cimg_load_cimg_case2("bool",bool);
61965       _cimg_load_cimg_case2("unsigned_char",unsigned char);
61966       _cimg_load_cimg_case2("uchar",unsigned char);
61967       _cimg_load_cimg_case2("char",char);
61968       _cimg_load_cimg_case2("unsigned_short",unsigned short);
61969       _cimg_load_cimg_case2("ushort",unsigned short);
61970       _cimg_load_cimg_case2("short",short);
61971       _cimg_load_cimg_case2("unsigned_int",unsigned int);
61972       _cimg_load_cimg_case2("uint",unsigned int);
61973       _cimg_load_cimg_case2("int",int);
61974       _cimg_load_cimg_case2("unsigned_long",ulongT);
61975       _cimg_load_cimg_case2("ulong",ulongT);
61976       _cimg_load_cimg_case2("long",longT);
61977       _cimg_load_cimg_case2("unsigned_int64",uint64T);
61978       _cimg_load_cimg_case2("uint64",uint64T);
61979       _cimg_load_cimg_case2("int64",int64T);
61980       _cimg_load_cimg_case2("float",float);
61981       _cimg_load_cimg_case2("double",double);
61982       if (!loaded) {
61983         if (!file) cimg::fclose(nfile);
61984         throw CImgIOException(_cimglist_instance
61985                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
61986                               cimglist_instance,
61987                               str_pixeltype._data,filename?filename:"(FILE*)");
61988       }
61989       if (!file) cimg::fclose(nfile);
61990       return *this;
61991     }
61992 
61993     //! Load a list from a PAR/REC (Philips) file.
61994     /**
61995       \param filename Filename to read data from.
61996     **/
61997     CImgList<T>& load_parrec(const char *const filename) {
61998       if (!filename)
61999         throw CImgArgumentException(_cimglist_instance
62000                                     "load_parrec(): Specified filename is (null).",
62001                                     cimglist_instance);
62002 
62003       CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
62004       *body = *filenamepar = *filenamerec = 0;
62005       const char *const ext = cimg::split_filename(filename,body);
62006       if (!std::strcmp(ext,"par")) {
62007         std::strncpy(filenamepar,filename,filenamepar._width - 1);
62008         cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
62009       }
62010       if (!std::strcmp(ext,"PAR")) {
62011         std::strncpy(filenamepar,filename,filenamepar._width - 1);
62012         cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
62013       }
62014       if (!std::strcmp(ext,"rec")) {
62015         std::strncpy(filenamerec,filename,filenamerec._width - 1);
62016         cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
62017       }
62018       if (!std::strcmp(ext,"REC")) {
62019         std::strncpy(filenamerec,filename,filenamerec._width - 1);
62020         cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
62021       }
62022       std::FILE *file = cimg::fopen(filenamepar,"r");
62023 
62024       // Parse header file
62025       CImgList<floatT> st_slices;
62026       CImgList<uintT> st_global;
62027       CImg<charT> line(256); *line = 0;
62028       int err;
62029       do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
62030       do {
62031         unsigned int sn,size_x,size_y,pixsize;
62032         float rs,ri,ss;
62033         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);
62034         if (err==7) {
62035           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
62036           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
62037           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
62038           else {
62039             CImg<uintT> &vec = st_global[i];
62040             if (size_x>vec[0]) vec[0] = size_x;
62041             if (size_y>vec[1]) vec[1] = size_y;
62042             vec[2] = sn;
62043           }
62044           st_slices[st_slices._width - 1][7] = (float)i;
62045         }
62046       } while (err==7);
62047 
62048       // Read data
62049       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
62050       cimglist_for(st_global,l) {
62051         const CImg<uintT>& vec = st_global[l];
62052         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
62053       }
62054 
62055       cimglist_for(st_slices,l) {
62056         const CImg<floatT>& vec = st_slices[l];
62057         const unsigned int
62058           sn = (unsigned int)vec[0] - 1,
62059           pixsize = (unsigned int)vec[1],
62060           size_x = (unsigned int)vec[2],
62061           size_y = (unsigned int)vec[3],
62062           imn = (unsigned int)vec[7];
62063         const float ri = vec[4], rs = vec[5], ss = vec[6];
62064         switch (pixsize) {
62065         case 8 : {
62066           CImg<ucharT> buf(size_x,size_y);
62067           cimg::fread(buf._data,size_x*size_y,file2);
62068           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62069           CImg<T>& img = (*this)[imn];
62070           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62071         } break;
62072         case 16 : {
62073           CImg<ushortT> buf(size_x,size_y);
62074           cimg::fread(buf._data,size_x*size_y,file2);
62075           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62076           CImg<T>& img = (*this)[imn];
62077           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62078         } break;
62079         case 32 : {
62080           CImg<uintT> buf(size_x,size_y);
62081           cimg::fread(buf._data,size_x*size_y,file2);
62082           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62083           CImg<T>& img = (*this)[imn];
62084           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62085         } break;
62086         default :
62087           cimg::fclose(file);
62088           cimg::fclose(file2);
62089           throw CImgIOException(_cimglist_instance
62090                                 "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
62091                                 cimglist_instance,
62092                                 pixsize,filename);
62093         }
62094       }
62095       cimg::fclose(file);
62096       cimg::fclose(file2);
62097       if (!_width)
62098         throw CImgIOException(_cimglist_instance
62099                               "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
62100                               cimglist_instance,
62101                               filename);
62102       return *this;
62103     }
62104 
62105     //! Load a list from a PAR/REC (Philips) file \newinstance.
62106     static CImgList<T> get_load_parrec(const char *const filename) {
62107       return CImgList<T>().load_parrec(filename);
62108     }
62109 
62110     //! Load a list from a YUV image sequence file.
62111     /**
62112         \param filename Filename to read data from.
62113         \param size_x Width of the images.
62114         \param size_y Height of the images.
62115         \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
62116         \param first_frame Index of first image frame to read.
62117         \param last_frame Index of last image frame to read.
62118         \param step_frame Step applied between each frame.
62119         \param yuv2rgb Apply YUV to RGB transformation during reading.
62120     **/
62121     CImgList<T>& load_yuv(const char *const filename,
62122                           const unsigned int size_x, const unsigned int size_y,
62123                           const unsigned int chroma_subsampling=444,
62124                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62125                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
62126       return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
62127                        first_frame,last_frame,step_frame,yuv2rgb);
62128     }
62129 
62130     //! Load a list from a YUV image sequence file \newinstance.
62131     static CImgList<T> get_load_yuv(const char *const filename,
62132                                     const unsigned int size_x, const unsigned int size_y=1,
62133                                     const unsigned int chroma_subsampling=444,
62134                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62135                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
62136       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
62137                                     first_frame,last_frame,step_frame,yuv2rgb);
62138     }
62139 
62140     //! Load a list from an image sequence YUV file \overloading.
62141     CImgList<T>& load_yuv(std::FILE *const file,
62142                           const unsigned int size_x, const unsigned int size_y,
62143                           const unsigned int chroma_subsampling=444,
62144                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62145                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
62146       return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
62147                        first_frame,last_frame,step_frame,yuv2rgb);
62148     }
62149 
62150     //! Load a list from an image sequence YUV file \newinstance.
62151     static CImgList<T> get_load_yuv(std::FILE *const file,
62152                                     const unsigned int size_x, const unsigned int size_y=1,
62153                                     const unsigned int chroma_subsampling=444,
62154                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62155                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
62156       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
62157                                     first_frame,last_frame,step_frame,yuv2rgb);
62158     }
62159 
62160     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
62161                            const unsigned int size_x, const unsigned int size_y,
62162                            const unsigned int chroma_subsampling,
62163                            const unsigned int first_frame, const unsigned int last_frame,
62164                            const unsigned int step_frame, const bool yuv2rgb) {
62165       if (!filename && !file)
62166         throw CImgArgumentException(_cimglist_instance
62167                                     "load_yuv(): Specified filename is (null).",
62168                                     cimglist_instance);
62169       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
62170         throw CImgArgumentException(_cimglist_instance
62171                                     "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.",
62172                                     cimglist_instance,
62173                                     chroma_subsampling,filename?filename:"(FILE*)");
62174       const unsigned int
62175         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
62176         cfy = chroma_subsampling==420?2:1,
62177         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
62178         nlast_frame = first_frame<last_frame?last_frame:first_frame,
62179         nstep_frame = step_frame?step_frame:1;
62180 
62181       if (!size_x || !size_y || size_x%cfx || size_y%cfy)
62182         throw CImgArgumentException(_cimglist_instance
62183                                     "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
62184                                     cimglist_instance,
62185                                     size_x,size_y,filename?filename:"(FILE*)");
62186 
62187       CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
62188       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
62189       bool stop_flag = false;
62190       int err;
62191       if (nfirst_frame) {
62192         err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
62193         if (err) {
62194           if (!file) cimg::fclose(nfile);
62195           throw CImgIOException(_cimglist_instance
62196                                 "load_yuv(): File '%s' doesn't contain frame number %u.",
62197                                 cimglist_instance,
62198                                 filename?filename:"(FILE*)",nfirst_frame);
62199         }
62200       }
62201       unsigned int frame;
62202       for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
62203         YUV.get_shared_channel(0).fill(0);
62204         // *TRY* to read the luminance part, do not replace by cimg::fread!
62205         err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
62206         if (err!=(int)(YUV._width*YUV._height)) {
62207           stop_flag = true;
62208           if (err>0)
62209             cimg::warn(_cimglist_instance
62210                        "load_yuv(): File '%s' contains incomplete data or given image dimensions "
62211                        "(%u,%u) are incorrect.",
62212                        cimglist_instance,
62213                        filename?filename:"(FILE*)",size_x,size_y);
62214         } else {
62215           UV.fill(0);
62216           // *TRY* to read the luminance part, do not replace by cimg::fread!
62217           err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
62218           if (err!=(int)(UV.size())) {
62219             stop_flag = true;
62220             if (err>0)
62221               cimg::warn(_cimglist_instance
62222                          "load_yuv(): File '%s' contains incomplete data or given image dimensions "
62223                          "(%u,%u) are incorrect.",
62224                          cimglist_instance,
62225                          filename?filename:"(FILE*)",size_x,size_y);
62226           } else {
62227             const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
62228             ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
62229             const unsigned int wd = YUV._width;
62230             switch (chroma_subsampling) {
62231             case 420 :
62232               cimg_forY(UV,y) {
62233                 cimg_forX(UV,x) {
62234                   const ucharT U = *(ptrs1++), V = *(ptrs2++);
62235                   ptrd1[wd] = U; *(ptrd1)++ = U;
62236                   ptrd1[wd] = U; *(ptrd1)++ = U;
62237                   ptrd2[wd] = V; *(ptrd2)++ = V;
62238                   ptrd2[wd] = V; *(ptrd2)++ = V;
62239                 }
62240                 ptrd1+=wd; ptrd2+=wd;
62241               }
62242               break;
62243             case 422 :
62244               cimg_forXY(UV,x,y) {
62245                 const ucharT U = *(ptrs1++), V = *(ptrs2++);
62246                 *(ptrd1++) = U; *(ptrd1++) = U;
62247                 *(ptrd2++) = V; *(ptrd2++) = V;
62248               }
62249               break;
62250             default :
62251               YUV.draw_image(0,0,0,1,UV);
62252             }
62253             if (yuv2rgb) YUV.YCbCrtoRGB();
62254             insert(YUV);
62255             if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
62256           }
62257         }
62258       }
62259       if (is_empty())
62260         throw CImgIOException(_cimglist_instance
62261                               "load_yuv() : Missing data in file '%s'.",
62262                               cimglist_instance,
62263                               filename?filename:"(FILE*)");
62264       if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
62265         cimg::warn(_cimglist_instance
62266                    "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
62267                    cimglist_instance,
62268                    nlast_frame,frame - 1,filename?filename:"(FILE*)");
62269 
62270       if (!file) cimg::fclose(nfile);
62271       return *this;
62272     }
62273 
62274     //! Load an image from a video file, using OpenCV library.
62275     /**
62276       \param filename Filename, as a C-string.
62277       \param first_frame Index of the first frame to read.
62278       \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U').
62279       \param step_frame Step value for frame reading.
62280       \note If step_frame==0, the current video stream is forced to be released (without any frames read).
62281     **/
62282     CImgList<T>& load_video(const char *const filename,
62283                             const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62284                             const unsigned int step_frame=1) {
62285 #ifndef cimg_use_opencv
62286       if (first_frame || last_frame!=~0U || step_frame>1)
62287         throw CImgArgumentException(_cimglist_instance
62288                                     "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
62289                                     "and 'step_frame' requires features from the OpenCV library "
62290                                     "('-Dcimg_use_opencv' must be defined).",
62291                                     cimglist_instance,filename);
62292       return load_ffmpeg_external(filename);
62293 #else
62294       static cv::VideoCapture *captures[32] = { 0 };
62295       static CImgList<charT> filenames(32);
62296       static CImg<uintT> positions(32,1,1,1,0);
62297       static int last_used_index = -1;
62298 
62299       // Detect if a video capture already exists for the specified filename.
62300       cimg::mutex(9);
62301       int index = -1;
62302       if (filename) {
62303         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
62304           index = last_used_index;
62305         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
62306             index = l; break;
62307           }
62308       } else index = last_used_index;
62309       cimg::mutex(9,0);
62310 
62311       // Release stream if needed.
62312       if (!step_frame || (index>=0 && positions[index]>first_frame)) {
62313         if (index>=0) {
62314           cimg::mutex(9);
62315           captures[index]->release();
62316           delete captures[index];
62317           captures[index] = 0;
62318           positions[index] = 0;
62319           filenames[index].assign();
62320           if (last_used_index==index) last_used_index = -1;
62321           index = -1;
62322           cimg::mutex(9,0);
62323         } else
62324           if (filename)
62325             cimg::warn(_cimglist_instance
62326                        "load_video() : File '%s', no opened video stream associated with filename found.",
62327                        cimglist_instance,filename);
62328           else
62329             cimg::warn(_cimglist_instance
62330                        "load_video() : No opened video stream found.",
62331                        cimglist_instance,filename);
62332         if (!step_frame) return *this;
62333       }
62334 
62335       // Find empty slot for capturing video stream.
62336       if (index<0) {
62337         if (!filename)
62338           throw CImgArgumentException(_cimglist_instance
62339                                       "load_video(): No already open video reader found. You must specify a "
62340                                       "non-(null) filename argument for the first call.",
62341                                       cimglist_instance);
62342         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
62343         if (index<0)
62344           throw CImgIOException(_cimglist_instance
62345                                 "load_video(): File '%s', no video reader slots available. "
62346                                 "You have to release some of your previously opened videos.",
62347                                 cimglist_instance,filename);
62348         cimg::mutex(9);
62349         captures[index] = new cv::VideoCapture(filename);
62350         positions[index] = 0;
62351         if (!captures[index]->isOpened()) {
62352           delete captures[index];
62353           captures[index] = 0;
62354           cimg::mutex(9,0);
62355           cimg::fclose(cimg::fopen(filename,"rb"));  // Check file availability
62356           throw CImgIOException(_cimglist_instance
62357                                 "load_video(): File '%s', unable to detect format of video file.",
62358                                 cimglist_instance,filename);
62359         }
62360         CImg<charT>::string(filename).move_to(filenames[index]);
62361         cimg::mutex(9,0);
62362       }
62363 
62364       cimg::mutex(9);
62365       const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count));
62366       cimg::mutex(9,0);
62367       assign();
62368 
62369       // Skip frames if requested.
62370       bool go_on = true;
62371       unsigned int &pos = positions[index];
62372       while (pos<first_frame) {
62373         cimg::mutex(9);
62374         if (!captures[index]->grab()) { cimg::mutex(9,0); go_on = false; break; }
62375         cimg::mutex(9,0);
62376         ++pos;
62377       }
62378 
62379       // Read and convert frames.
62380       const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
62381       while (go_on && pos<=_last_frame) {
62382         cv::Mat cvimg;
62383         cimg::mutex(9);
62384         if (captures[index]->read(cvimg)) { CImg<T>::_cvmat2cimg(cvimg).move_to(*this); ++pos; }
62385         else go_on = false;
62386         cimg::mutex(9,0);
62387         if (go_on)
62388           for (unsigned int i = 1; go_on && i<step_frame && pos<=_last_frame; ++i, ++pos) {
62389             cimg::mutex(9);
62390             if (!captures[index]->grab()) go_on = false;
62391             cimg::mutex(9,0);
62392           }
62393       }
62394 
62395       if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary
62396         cimg::mutex(9);
62397         captures[index]->release();
62398         delete captures[index];
62399         captures[index] = 0;
62400         filenames[index].assign();
62401         positions[index] = 0;
62402         index = -1;
62403         cimg::mutex(9,0);
62404       }
62405 
62406       cimg::mutex(9);
62407       last_used_index = index;
62408       cimg::mutex(9,0);
62409 
62410       if (is_empty())
62411         throw CImgIOException(_cimglist_instance
62412                               "load_video(): File '%s', unable to locate frame %u.",
62413                               cimglist_instance,filename,first_frame);
62414       return *this;
62415 #endif
62416     }
62417 
62418     //! Load an image from a video file, using OpenCV library \newinstance.
62419     static CImgList<T> get_load_video(const char *const filename,
62420                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62421                            const unsigned int step_frame=1) {
62422       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
62423     }
62424 
62425     //! Load an image from a video file using the external tool 'ffmpeg'.
62426     /**
62427       \param filename Filename to read data from.
62428     **/
62429     CImgList<T>& load_ffmpeg_external(const char *const filename) {
62430       if (!filename)
62431         throw CImgArgumentException(_cimglist_instance
62432                                     "load_ffmpeg_external(): Specified filename is (null).",
62433                                     cimglist_instance);
62434       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
62435       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
62436       std::FILE *file = 0;
62437       do {
62438         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
62439                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62440         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
62441         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
62442       } while (file);
62443       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
62444       cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"",
62445                     cimg::ffmpeg_path(),
62446                     CImg<charT>::string(filename)._system_strescape().data(),
62447                     CImg<charT>::string(filename_tmp2)._system_strescape().data());
62448       cimg::system(command, cimg::ffmpeg_path());
62449       const unsigned int omode = cimg::exception_mode();
62450       cimg::exception_mode(0);
62451       assign();
62452       unsigned int i = 1;
62453       for (bool stop_flag = false; !stop_flag; ++i) {
62454         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
62455         CImg<T> img;
62456         try { img.load_pnm(filename_tmp2); }
62457         catch (CImgException&) { stop_flag = true; }
62458         if (img) { img.move_to(*this); std::remove(filename_tmp2); }
62459       }
62460       cimg::exception_mode(omode);
62461       if (is_empty())
62462         throw CImgIOException(_cimglist_instance
62463                               "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
62464                               cimglist_instance,
62465                               filename);
62466       return *this;
62467     }
62468 
62469     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
62470     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
62471       return CImgList<T>().load_ffmpeg_external(filename);
62472     }
62473 
62474     //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
62475     /**
62476       \param filename Filename to read data from.
62477     **/
62478     CImgList<T>& load_gif_external(const char *const filename) {
62479       if (!filename)
62480         throw CImgArgumentException(_cimglist_instance
62481                                     "load_gif_external(): Specified filename is (null).",
62482                                     cimglist_instance);
62483       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
62484       if (!_load_gif_external(filename,false))
62485         if (!_load_gif_external(filename,true))
62486           try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
62487       if (is_empty())
62488         throw CImgIOException(_cimglist_instance
62489                               "load_gif_external(): Failed to open file '%s'.",
62490                               cimglist_instance,filename);
62491       return *this;
62492     }
62493 
62494     CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
62495       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
62496       std::FILE *file = 0;
62497       do {
62498         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
62499                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62500         if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
62501         else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
62502         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
62503       } while (file);
62504       if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
62505                                             cimg::graphicsmagick_path(),
62506                                             CImg<charT>::string(filename)._system_strescape().data(),
62507                                             CImg<charT>::string(filename_tmp)._system_strescape().data());
62508       else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"",
62509                          cimg::imagemagick_path(),
62510                          CImg<charT>::string(filename)._system_strescape().data(),
62511                          CImg<charT>::string(filename_tmp)._system_strescape().data());
62512       cimg::system(command, cimg::imagemagick_path());
62513       const unsigned int omode = cimg::exception_mode();
62514       cimg::exception_mode(0);
62515       assign();
62516 
62517       // Try to read a single frame gif.
62518       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
62519       CImg<T> img;
62520       try { img.load_png(filename_tmp2); }
62521       catch (CImgException&) { }
62522       if (img) { img.move_to(*this); std::remove(filename_tmp2); }
62523       else { // Try to read animated gif
62524         unsigned int i = 0;
62525         for (bool stop_flag = false; !stop_flag; ++i) {
62526           if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
62527           else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
62528           try { img.load_png(filename_tmp2); }
62529           catch (CImgException&) { stop_flag = true; }
62530           if (img) { img.move_to(*this); std::remove(filename_tmp2); }
62531         }
62532       }
62533       cimg::exception_mode(omode);
62534       return *this;
62535     }
62536 
62537     //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
62538     static CImgList<T> get_load_gif_external(const char *const filename) {
62539       return CImgList<T>().load_gif_external(filename);
62540     }
62541 
62542     //! Load a gzipped list, using external tool 'gunzip'.
62543     /**
62544       \param filename Filename to read data from.
62545     **/
62546     CImgList<T>& load_gzip_external(const char *const filename) {
62547       if (!filename)
62548         throw CImgIOException(_cimglist_instance
62549                               "load_gzip_external(): Specified filename is (null).",
62550                               cimglist_instance);
62551       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
62552       CImg<charT> command(1024), filename_tmp(256), body(256);
62553       const char
62554         *ext = cimg::split_filename(filename,body),
62555         *ext2 = cimg::split_filename(body,0);
62556       std::FILE *file = 0;
62557       do {
62558         if (!cimg::strcasecmp(ext,"gz")) {
62559           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
62560                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
62561           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
62562                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62563         } else {
62564           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
62565                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
62566           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
62567                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62568         }
62569         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
62570       } while (file);
62571       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
62572                     cimg::gunzip_path(),
62573                     CImg<charT>::string(filename)._system_strescape().data(),
62574                     CImg<charT>::string(filename_tmp)._system_strescape().data());
62575       cimg::system(command, cimg::gunzip_path());
62576       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
62577         cimg::fclose(cimg::fopen(filename,"r"));
62578         throw CImgIOException(_cimglist_instance
62579                               "load_gzip_external(): Failed to open file '%s'.",
62580                               cimglist_instance,
62581                               filename);
62582 
62583       } else cimg::fclose(file);
62584       load(filename_tmp);
62585       std::remove(filename_tmp);
62586       return *this;
62587     }
62588 
62589     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
62590     static CImgList<T> get_load_gzip_external(const char *const filename) {
62591       return CImgList<T>().load_gzip_external(filename);
62592     }
62593 
62594     //! Load images from a TIFF file.
62595     /**
62596         \param filename Filename to read data from.
62597         \param first_frame Index of first image frame to read.
62598         \param last_frame Index of last image frame to read.
62599         \param step_frame Step applied between each frame.
62600         \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
62601         \param[out] voxel_size Voxel size, as stored in the filename.
62602         \param[out] description Description, as stored in the filename.
62603     **/
62604     CImgList<T>& load_tiff(const char *const filename,
62605                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62606                            const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
62607                            float *const voxel_size=0, CImg<charT> *const description=0) {
62608       const unsigned int
62609         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
62610         nstep_frame = step_frame?step_frame:1;
62611       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
62612 #ifndef cimg_use_tiff
62613       cimg::unused(bits_per_value,voxel_size,description);
62614       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
62615         throw CImgArgumentException(_cimglist_instance
62616                                     "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
62617                                     cimglist_instance,
62618                                     filename);
62619 
62620       return assign(CImg<T>::get_load_tiff(filename));
62621 #else
62622 #if cimg_verbosity<3
62623         TIFFSetWarningHandler(0);
62624         TIFFSetErrorHandler(0);
62625 #endif
62626       TIFF *tif = TIFFOpen(filename,"r");
62627       if (tif) {
62628         unsigned int nb_images = 0;
62629         do ++nb_images; while (TIFFReadDirectory(tif));
62630         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
62631           cimg::warn(_cimglist_instance
62632                      "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
62633                      "file '%s' contains %u image(s).",
62634                      cimglist_instance,
62635                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
62636 
62637         if (nfirst_frame>=nb_images) return assign();
62638         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
62639         assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
62640         TIFFSetDirectory(tif,0);
62641         cimglist_for(*this,l)
62642           _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description);
62643         TIFFClose(tif);
62644       } else throw CImgIOException(_cimglist_instance
62645                                    "load_tiff(): Failed to open file '%s'.",
62646                                    cimglist_instance,
62647                                    filename);
62648       return *this;
62649 #endif
62650     }
62651 
62652     //! Load a multi-page TIFF file \newinstance.
62653     static CImgList<T> get_load_tiff(const char *const filename,
62654                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62655                                      const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
62656                                      float *const voxel_size=0, CImg<charT> *const description=0) {
62657       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
62658     }
62659 
62660     //@}
62661     //----------------------------------
62662     //
62663     //! \name Data Output
62664     //@{
62665     //----------------------------------
62666 
62667     //! Print information about the list on the standard output.
62668     /**
62669       \param title Label set to the information displayed.
62670       \param display_stats Tells if image statistics must be computed and displayed.
62671     **/
62672     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
62673       unsigned int msiz = 0;
62674       cimglist_for(*this,l) msiz+=_data[l].size();
62675       msiz*=sizeof(T);
62676       const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
62677       CImg<charT> _title(64);
62678       if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
62679       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
62680                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
62681                    cimg::t_bold,cimg::t_normal,(void*)this,
62682                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
62683                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
62684                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
62685                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
62686       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
62687       else std::fprintf(cimg::output(),".\n");
62688 
62689       char tmp[16] = { 0 };
62690       cimglist_for(*this,ll) {
62691         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
62692         std::fprintf(cimg::output(),"  ");
62693         _data[ll].print(tmp,display_stats);
62694         if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output(),"  ...\n"); }
62695       }
62696       std::fflush(cimg::output());
62697       return *this;
62698     }
62699 
62700     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
62701     /**
62702        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
62703        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
62704        \param align Appending alignment.
62705        \note This function displays the list images of the current CImgList instance into an existing
62706          CImgDisplay window.
62707        Images of the list are appended in a single temporary image for visualization purposes.
62708        The function returns immediately.
62709     **/
62710     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
62711       disp.display(*this,axis,align);
62712       return *this;
62713     }
62714 
62715     //! Display the current CImgList instance in a new display window.
62716     /**
62717         \param disp Display window.
62718         \param display_info Tells if image information are displayed on the standard output.
62719         \param axis Alignment axis for images viewing.
62720         \param align Appending alignment.
62721         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
62722         \param exit_on_anykey Exit function when any key is pressed.
62723         \note This function opens a new window with a specific title and displays the list images of the
62724           current CImgList instance into it.
62725         Images of the list are appended in a single temporary image for visualization purposes.
62726         The function returns when a key is pressed or the display window is closed by the user.
62727     **/
62728     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
62729                                const char axis='x', const float align=0,
62730                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
62731       bool is_exit = false;
62732       return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
62733     }
62734 
62735     //! Display the current CImgList instance in a new display window.
62736     /**
62737       \param title Title of the opening display window.
62738       \param display_info Tells if list information must be written on standard output.
62739       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
62740       \param align Appending alignment.
62741       \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
62742       \param exit_on_anykey Exit function when any key is pressed.
62743     **/
62744     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
62745                                const char axis='x', const float align=0,
62746                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
62747       CImgDisplay disp;
62748       bool is_exit = false;
62749       return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
62750     }
62751 
62752     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
62753                                 const bool display_info, const char axis, const float align, unsigned int *const XYZ,
62754                                 const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
62755                                 bool &is_exit) const {
62756       if (is_empty())
62757         throw CImgInstanceException(_cimglist_instance
62758                                     "display(): Empty instance.",
62759                                     cimglist_instance);
62760       if (!disp) {
62761         if (axis=='x') {
62762           unsigned int sum_width = 0, max_height = 0;
62763           cimglist_for(*this,l) {
62764             const CImg<T> &img = _data[l];
62765             const unsigned int
62766               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
62767               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
62768             sum_width+=w;
62769             if (h>max_height) max_height = h;
62770           }
62771           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
62772         } else {
62773           unsigned int max_width = 0, sum_height = 0;
62774           cimglist_for(*this,l) {
62775             const CImg<T> &img = _data[l];
62776             const unsigned int
62777               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
62778               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
62779             if (w>max_width) max_width = w;
62780             sum_height+=h;
62781           }
62782           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
62783         }
62784         if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
62785       } else if (title) disp.set_title("%s",title);
62786       else if (titles) disp.set_title("%s",titles->__display()._data);
62787       const CImg<char> dtitle = CImg<char>::string(disp.title());
62788       if (display_info) print(disp.title());
62789       disp.show().flush();
62790 
62791       if (_width==1) {
62792         const unsigned int dw = disp._width, dh = disp._height;
62793         if (!is_first_call)
62794           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
62795         disp.set_title("%s (%ux%ux%ux%u)",
62796                        dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
62797         _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
62798         if (disp.key()) is_exit = true;
62799         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
62800       } else {
62801         bool disp_resize = !is_first_call;
62802         while (!disp.is_closed() && !is_exit) {
62803           const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
62804           disp_resize = true;
62805           if (s[0]<0 && !disp.wheel()) { // No selections done
62806             if (disp.button()&2) { disp.flush(); break; }
62807             is_exit = true;
62808           } else if (disp.wheel()) { // Zoom in/out
62809             const int wheel = disp.wheel();
62810             disp.set_wheel();
62811             if (!is_first_call && wheel<0) break;
62812             if (wheel>0 && _width>=4) {
62813               const unsigned int
62814                 delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
62815                 ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
62816                 ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
62817               if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
62818                 const CImgList<T> sublist = get_shared_images(ind0,ind1);
62819                 CImgList<charT> t_sublist;
62820                 if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
62821                 sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
62822                                  orig + ind0,false,is_exit);
62823               }
62824             }
62825           } else if (s[0]!=0 || s[1]!=width() - 1) {
62826             const CImgList<T> sublist = get_shared_images(s[0],s[1]);
62827             CImgList<charT> t_sublist;
62828             if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
62829             sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
62830                              orig + s[0],false,is_exit);
62831           }
62832           disp.set_title("%s",dtitle.data());
62833         }
62834       }
62835       return *this;
62836     }
62837 
62838     // [internal] Return string to describe display title.
62839     CImg<charT> __display() const {
62840       CImg<charT> res, str;
62841       cimglist_for(*this,l) {
62842         CImg<charT>::string((char*)_data[l]).move_to(str);
62843         if (l!=width() - 1) {
62844           str.resize(str._width + 1,1,1,1,0);
62845           str[str._width - 2] = ',';
62846           str[str._width - 1] = ' ';
62847         }
62848         res.append(str,'x');
62849       }
62850       if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
62851       cimg::strellipsize(res,128,false);
62852       if (_width>1) {
62853         const unsigned int l = (unsigned int)std::strlen(res);
62854         if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
62855         cimg_snprintf(res._data + l,16," (#%u)",_width);
62856       }
62857       return res;
62858     }
62859 
62860     //! Save list into a file.
62861     /**
62862       \param filename Filename to write data to.
62863       \param number When positive, represents an index added to the filename. Otherwise, no number is added.
62864       \param digits Number of digits used for adding the number to the filename.
62865     **/
62866     const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
62867       if (!filename)
62868         throw CImgArgumentException(_cimglist_instance
62869                                     "save(): Specified filename is (null).",
62870                                     cimglist_instance);
62871       // Do not test for empty instances, since .cimg format is able to manage empty instances.
62872       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
62873       const char *const ext = cimg::split_filename(filename);
62874       CImg<charT> nfilename(1024);
62875       const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
62876         filename;
62877 
62878 #ifdef cimglist_save_plugin
62879       cimglist_save_plugin(fn);
62880 #endif
62881 #ifdef cimglist_save_plugin1
62882       cimglist_save_plugin1(fn);
62883 #endif
62884 #ifdef cimglist_save_plugin2
62885       cimglist_save_plugin2(fn);
62886 #endif
62887 #ifdef cimglist_save_plugin3
62888       cimglist_save_plugin3(fn);
62889 #endif
62890 #ifdef cimglist_save_plugin4
62891       cimglist_save_plugin4(fn);
62892 #endif
62893 #ifdef cimglist_save_plugin5
62894       cimglist_save_plugin5(fn);
62895 #endif
62896 #ifdef cimglist_save_plugin6
62897       cimglist_save_plugin6(fn);
62898 #endif
62899 #ifdef cimglist_save_plugin7
62900       cimglist_save_plugin7(fn);
62901 #endif
62902 #ifdef cimglist_save_plugin8
62903       cimglist_save_plugin8(fn);
62904 #endif
62905       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
62906       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
62907       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
62908       else if (!cimg::strcasecmp(ext,"avi") ||
62909                !cimg::strcasecmp(ext,"mov") ||
62910                !cimg::strcasecmp(ext,"asf") ||
62911                !cimg::strcasecmp(ext,"divx") ||
62912                !cimg::strcasecmp(ext,"flv") ||
62913                !cimg::strcasecmp(ext,"mpg") ||
62914                !cimg::strcasecmp(ext,"m1v") ||
62915                !cimg::strcasecmp(ext,"m2v") ||
62916                !cimg::strcasecmp(ext,"m4v") ||
62917                !cimg::strcasecmp(ext,"mjp") ||
62918                !cimg::strcasecmp(ext,"mp4") ||
62919                !cimg::strcasecmp(ext,"mkv") ||
62920                !cimg::strcasecmp(ext,"mpe") ||
62921                !cimg::strcasecmp(ext,"movie") ||
62922                !cimg::strcasecmp(ext,"ogm") ||
62923                !cimg::strcasecmp(ext,"ogg") ||
62924                !cimg::strcasecmp(ext,"ogv") ||
62925                !cimg::strcasecmp(ext,"qt") ||
62926                !cimg::strcasecmp(ext,"rm") ||
62927                !cimg::strcasecmp(ext,"vob") ||
62928                !cimg::strcasecmp(ext,"webm") ||
62929                !cimg::strcasecmp(ext,"wmv") ||
62930                !cimg::strcasecmp(ext,"xvid") ||
62931                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
62932 #ifdef cimg_use_tiff
62933       else if (!cimg::strcasecmp(ext,"tif") ||
62934           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
62935 #endif
62936       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
62937       else {
62938         if (_width==1) _data[0].save(fn,-1);
62939         else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
62940       }
62941       return *this;
62942     }
62943 
62944     //! Tell if an image list can be saved as one single file.
62945     /**
62946        \param filename Filename, as a C-string.
62947        \return \c true if the file format supports multiple images, \c false otherwise.
62948     **/
62949     static bool is_saveable(const char *const filename) {
62950       const char *const ext = cimg::split_filename(filename);
62951       if (!cimg::strcasecmp(ext,"cimgz") ||
62952 #ifdef cimg_use_tiff
62953           !cimg::strcasecmp(ext,"tif") ||
62954           !cimg::strcasecmp(ext,"tiff") ||
62955 #endif
62956           !cimg::strcasecmp(ext,"yuv") ||
62957           !cimg::strcasecmp(ext,"avi") ||
62958           !cimg::strcasecmp(ext,"mov") ||
62959           !cimg::strcasecmp(ext,"asf") ||
62960           !cimg::strcasecmp(ext,"divx") ||
62961           !cimg::strcasecmp(ext,"flv") ||
62962           !cimg::strcasecmp(ext,"mpg") ||
62963           !cimg::strcasecmp(ext,"m1v") ||
62964           !cimg::strcasecmp(ext,"m2v") ||
62965           !cimg::strcasecmp(ext,"m4v") ||
62966           !cimg::strcasecmp(ext,"mjp") ||
62967           !cimg::strcasecmp(ext,"mp4") ||
62968           !cimg::strcasecmp(ext,"mkv") ||
62969           !cimg::strcasecmp(ext,"mpe") ||
62970           !cimg::strcasecmp(ext,"movie") ||
62971           !cimg::strcasecmp(ext,"ogm") ||
62972           !cimg::strcasecmp(ext,"ogg") ||
62973           !cimg::strcasecmp(ext,"ogv") ||
62974           !cimg::strcasecmp(ext,"qt") ||
62975           !cimg::strcasecmp(ext,"rm") ||
62976           !cimg::strcasecmp(ext,"vob") ||
62977           !cimg::strcasecmp(ext,"webm") ||
62978           !cimg::strcasecmp(ext,"wmv") ||
62979           !cimg::strcasecmp(ext,"xvid") ||
62980           !cimg::strcasecmp(ext,"mpeg")) return true;
62981       return false;
62982     }
62983 
62984     //! Save image sequence as a GIF animated file.
62985     /**
62986        \param filename Filename to write data to.
62987        \param fps Number of desired frames per second.
62988        \param nb_loops Number of loops (\c 0 for infinite looping).
62989     **/
62990     const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
62991                                          const unsigned int nb_loops=0) {
62992       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
62993       CImgList<charT> filenames;
62994       std::FILE *file = 0;
62995 
62996 #ifdef cimg_use_png
62997 #define _cimg_save_gif_extension "png"
62998 #else
62999 #define _cimg_save_gif_extension "ppm"
63000 #endif
63001 
63002       do {
63003         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63004                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63005         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
63006         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63007       } while (file);
63008       cimglist_for(*this,l) {
63009         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
63010         CImg<charT>::string(filename_tmp2).move_to(filenames);
63011         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
63012         else _data[l].save(filename_tmp2);
63013       }
63014       cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u",
63015                     cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops);
63016       CImg<ucharT>::string(command).move_to(filenames,0);
63017       cimg_snprintf(command,command._width,"\"%s\"",
63018                     CImg<charT>::string(filename)._system_strescape().data());
63019       CImg<ucharT>::string(command).move_to(filenames);
63020       CImg<charT> _command = filenames>'x';
63021       cimg_for(_command,p,char) if (!*p) *p = ' ';
63022       _command.back() = 0;
63023 
63024       cimg::system(_command, cimg::imagemagick_path());
63025       file = cimg::std_fopen(filename,"rb");
63026       if (!file)
63027         throw CImgIOException(_cimglist_instance
63028                               "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
63029                               cimglist_instance,
63030                               filename);
63031       else cimg::fclose(file);
63032       cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
63033       return *this;
63034     }
63035 
63036     //! Save list as a YUV image sequence file.
63037     /**
63038       \param filename Filename to write data to.
63039       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
63040       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
63041     **/
63042     const CImgList<T>& save_yuv(const char *const filename=0,
63043                                 const unsigned int chroma_subsampling=444,
63044                                 const bool is_rgb=true) const {
63045       return _save_yuv(0,filename,chroma_subsampling,is_rgb);
63046     }
63047 
63048     //! Save image sequence into a YUV file.
63049     /**
63050       \param file File to write data to.
63051       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
63052       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
63053     **/
63054     const CImgList<T>& save_yuv(std::FILE *const file,
63055                                 const unsigned int chroma_subsampling=444,
63056                                 const bool is_rgb=true) const {
63057       return _save_yuv(file,0,chroma_subsampling,is_rgb);
63058     }
63059 
63060     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
63061                                  const unsigned int chroma_subsampling,
63062                                  const bool is_rgb) const {
63063       if (!file && !filename)
63064         throw CImgArgumentException(_cimglist_instance
63065                                     "save_yuv(): Specified filename is (null).",
63066                                     cimglist_instance);
63067       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
63068         throw CImgArgumentException(_cimglist_instance
63069                                     "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
63070                                     cimglist_instance,
63071                                     chroma_subsampling,filename?filename:"(FILE*)");
63072       if (is_empty()) { cimg::fempty(file,filename); return *this; }
63073       const unsigned int
63074         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
63075         cfy = chroma_subsampling==420?2:1,
63076         w0 = (*this)[0]._width, h0 = (*this)[0]._height,
63077         width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
63078       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63079       cimglist_for(*this,l) {
63080         const CImg<T> &frame = (*this)[l];
63081         cimg_forZ(frame,z) {
63082           CImg<ucharT> YUV;
63083           if (sizeof(T)==1 && !is_rgb &&
63084               frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
63085             YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
63086           else {
63087             YUV = frame.get_slice(z);
63088             if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
63089             if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
63090             if (is_rgb) YUV.RGBtoYCbCr();
63091           }
63092           if (chroma_subsampling==444)
63093             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
63094           else {
63095             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
63096             CImg<ucharT> UV = YUV.get_channels(1,2);
63097             UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
63098             cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
63099           }
63100         }
63101       }
63102       if (!file) cimg::fclose(nfile);
63103       return *this;
63104     }
63105 
63106     //! Save list into a .cimg file.
63107     /**
63108        \param filename Filename to write data to.
63109        \param is_compressed Tells if data compression must be enabled.
63110     **/
63111     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
63112       return _save_cimg(0,filename,is_compressed);
63113     }
63114 
63115     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
63116       if (!file && !filename)
63117         throw CImgArgumentException(_cimglist_instance
63118                                     "save_cimg(): Specified filename is (null).",
63119                                     cimglist_instance);
63120 #ifndef cimg_use_zlib
63121       if (is_compressed)
63122         cimg::warn(_cimglist_instance
63123                    "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
63124                    "saving them uncompressed.",
63125                    cimglist_instance,
63126                    filename?filename:"(FILE*)");
63127 #endif
63128       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
63129       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63130       const bool is_bool = ptype==cimg::type<bool>::string();
63131       if (!is_bool && std::strstr(ptype,"unsigned")==ptype)
63132         std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
63133       else
63134         std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
63135 
63136       cimglist_for(*this,l) {
63137         const CImg<T>& img = _data[l];
63138         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
63139         if (img._data) {
63140           CImg<T> tmp;
63141           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
63142           const CImg<T>& ref = cimg::endianness()?tmp:img;
63143           bool failed_to_compress = true;
63144           if (is_compressed) {
63145 #ifdef cimg_use_zlib
63146             Bytef *cbuf = 0;
63147             uLongf csiz = 0;
63148 
63149             if (is_bool) { // Boolean data (bitwise)
63150               ulongT siz;
63151               const unsigned char *const buf = ref._bool2uchar(siz,false);
63152               csiz = siz + siz/100 + 16;
63153               cbuf = new Bytef[csiz];
63154               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz);
63155               if (!failed_to_compress) {
63156                 std::fprintf(nfile," #%lu\n",csiz);
63157                 cimg::fwrite(cbuf,csiz,nfile);
63158               }
63159               delete[] buf;
63160             } else { // Non-boolean data
63161               const ulongT siz = sizeof(T)*ref.size();
63162               csiz = siz + siz/100 + 16;
63163               cbuf = new Bytef[csiz];
63164               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz);
63165               if (!failed_to_compress) {
63166                 std::fprintf(nfile," #%lu\n",csiz);
63167                 cimg::fwrite(cbuf,csiz,nfile);
63168               }
63169             }
63170             if (failed_to_compress)
63171               cimg::warn(_cimglist_instance
63172                          "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
63173                          cimglist_instance,
63174                          filename?filename:"(FILE*)");
63175             delete[] cbuf;
63176 #endif
63177           }
63178           if (failed_to_compress) { // Write non-compressed
63179             std::fputc('\n',nfile);
63180             if (is_bool) { // Boolean data (bitwise)
63181               ulongT siz;
63182               const unsigned char *const buf = ref._bool2uchar(siz,false);
63183               cimg::fwrite(buf,siz,nfile);
63184               delete[] buf;
63185             } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data
63186           }
63187         } else std::fputc('\n',nfile);
63188       }
63189       if (!file) cimg::fclose(nfile);
63190       return *this;
63191     }
63192 
63193     //! Save list into a .cimg file.
63194     /**
63195        \param file File to write data to.
63196        \param is_compressed Tells if data compression must be enabled.
63197     **/
63198     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
63199       return _save_cimg(file,0,is_compressed);
63200     }
63201 
63202     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
63203                                  const unsigned int n0,
63204                                  const unsigned int x0, const unsigned int y0,
63205                                  const unsigned int z0, const unsigned int c0) const {
63206 #define _cimg_save_cimg_case(Ts,Tss) \
63207       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
63208         for (unsigned int l = 0; l<lmax; ++l) { \
63209           j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
63210           W = H = D = C = 0; \
63211           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
63212             throw CImgIOException(_cimglist_instance \
63213                                   "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
63214                                   cimglist_instance, \
63215                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
63216           if (W*H*D*C>0) { \
63217             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
63218             else { \
63219               const CImg<T>& img = (*this)[l - n0]; \
63220               const T *ptrs = img._data; \
63221               const unsigned int \
63222                 x1 = x0 + img._width - 1, \
63223                 y1 = y0 + img._height - 1, \
63224                 z1 = z0 + img._depth - 1, \
63225                 c1 = c0 + img._spectrum - 1, \
63226                 nx1 = x1>=W?W - 1:x1, \
63227                 ny1 = y1>=H?H - 1:y1, \
63228                 nz1 = z1>=D?D - 1:z1, \
63229                 nc1 = c1>=C?C - 1:c1; \
63230               CImg<Tss> raw(1 + nx1 - x0); \
63231               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
63232               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
63233               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
63234                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
63235                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
63236                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
63237                   const unsigned int skipyb = y0*W*sizeof(Tss); \
63238                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
63239                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
63240                     const unsigned int skipxb = x0*sizeof(Tss); \
63241                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
63242                     raw.assign(ptrs, raw._width); \
63243                     ptrs+=img._width; \
63244                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
63245                     cimg::fwrite(raw._data,raw._width,nfile); \
63246                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
63247                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
63248                   } \
63249                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
63250                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
63251                 } \
63252                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
63253                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
63254               } \
63255               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
63256               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
63257             } \
63258           } \
63259         } \
63260         saved = true; \
63261       }
63262 
63263       if (!file && !filename)
63264         throw CImgArgumentException(_cimglist_instance
63265                                     "save_cimg(): Specified filename is (null).",
63266                                     cimglist_instance);
63267       if (is_empty())
63268         throw CImgInstanceException(_cimglist_instance
63269                                     "save_cimg(): Empty instance, for file '%s'.",
63270                                     cimglist_instance,
63271                                     filename?filename:"(FILE*)");
63272 
63273       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
63274       bool saved = false, endian = cimg::endianness();
63275       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
63276       *tmp = *str_pixeltype = *str_endian = 0;
63277       unsigned int j, N, W, H, D, C;
63278       int i, err;
63279       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
63280       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
63281       if (err<2) {
63282         if (!file) cimg::fclose(nfile);
63283         throw CImgIOException(_cimglist_instance
63284                               "save_cimg(): CImg header not found in file '%s'.",
63285                               cimglist_instance,
63286                               filename?filename:"(FILE*)");
63287       }
63288       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
63289       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
63290       const unsigned int lmax = std::min(N,n0 + _width);
63291       _cimg_save_cimg_case("bool",bool);
63292       _cimg_save_cimg_case("unsigned_char",unsigned char);
63293       _cimg_save_cimg_case("uchar",unsigned char);
63294       _cimg_save_cimg_case("char",char);
63295       _cimg_save_cimg_case("unsigned_short",unsigned short);
63296       _cimg_save_cimg_case("ushort",unsigned short);
63297       _cimg_save_cimg_case("short",short);
63298       _cimg_save_cimg_case("unsigned_int",unsigned int);
63299       _cimg_save_cimg_case("uint",unsigned int);
63300       _cimg_save_cimg_case("int",int);
63301       _cimg_save_cimg_case("unsigned_int64",uint64T);
63302       _cimg_save_cimg_case("uint64",uint64T);
63303       _cimg_save_cimg_case("int64",int64T);
63304       _cimg_save_cimg_case("float",float);
63305       _cimg_save_cimg_case("double",double);
63306       if (!saved) {
63307         if (!file) cimg::fclose(nfile);
63308         throw CImgIOException(_cimglist_instance
63309                               "save_cimg(): Unsupported data type '%s' for file '%s'.",
63310                               cimglist_instance,
63311                               filename?filename:"(FILE*)",str_pixeltype._data);
63312       }
63313       if (!file) cimg::fclose(nfile);
63314       return *this;
63315     }
63316 
63317     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
63318     /**
63319       \param filename Filename to write data to.
63320       \param n0 Starting index of images to write.
63321       \param x0 Starting X-coordinates of image regions to write.
63322       \param y0 Starting Y-coordinates of image regions to write.
63323       \param z0 Starting Z-coordinates of image regions to write.
63324       \param c0 Starting C-coordinates of image regions to write.
63325     **/
63326     const CImgList<T>& save_cimg(const char *const filename,
63327                                  const unsigned int n0,
63328                                  const unsigned int x0, const unsigned int y0,
63329                                  const unsigned int z0, const unsigned int c0) const {
63330       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
63331     }
63332 
63333     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
63334     /**
63335       \param file File to write data to.
63336       \param n0 Starting index of images to write.
63337       \param x0 Starting X-coordinates of image regions to write.
63338       \param y0 Starting Y-coordinates of image regions to write.
63339       \param z0 Starting Z-coordinates of image regions to write.
63340       \param c0 Starting C-coordinates of image regions to write.
63341     **/
63342     const CImgList<T>& save_cimg(std::FILE *const file,
63343                                  const unsigned int n0,
63344                                  const unsigned int x0, const unsigned int y0,
63345                                  const unsigned int z0, const unsigned int c0) const {
63346       return _save_cimg(file,0,n0,x0,y0,z0,c0);
63347     }
63348 
63349     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
63350                                 const unsigned int nb,
63351                                 const unsigned int dx, const unsigned int dy,
63352                                 const unsigned int dz, const unsigned int dc) {
63353       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63354       const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
63355       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
63356       for (unsigned int i=nb; i; --i) {
63357         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
63358         for (ulongT off = siz; off; --off) std::fputc(0,nfile);
63359       }
63360       if (!file) cimg::fclose(nfile);
63361     }
63362 
63363     //! Save empty (non-compressed) .cimg file with specified dimensions.
63364     /**
63365         \param filename Filename to write data to.
63366         \param nb Number of images to write.
63367         \param dx Width of images in the written file.
63368         \param dy Height of images in the written file.
63369         \param dz Depth of images in the written file.
63370         \param dc Spectrum of images in the written file.
63371     **/
63372     static void save_empty_cimg(const char *const filename,
63373                                 const unsigned int nb,
63374                                 const unsigned int dx, const unsigned int dy=1,
63375                                 const unsigned int dz=1, const unsigned int dc=1) {
63376       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
63377     }
63378 
63379     //! Save empty .cimg file with specified dimensions.
63380     /**
63381         \param file File to write data to.
63382         \param nb Number of images to write.
63383         \param dx Width of images in the written file.
63384         \param dy Height of images in the written file.
63385         \param dz Depth of images in the written file.
63386         \param dc Spectrum of images in the written file.
63387     **/
63388     static void save_empty_cimg(std::FILE *const file,
63389                                 const unsigned int nb,
63390                                 const unsigned int dx, const unsigned int dy=1,
63391                                 const unsigned int dz=1, const unsigned int dc=1) {
63392       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
63393     }
63394 
63395     //! Save list as a TIFF file.
63396     /**
63397       \param filename Filename to write data to.
63398       \param compression_type Compression mode used to write data.
63399       \param voxel_size Voxel size, to be stored in the filename.
63400       \param description Description, to be stored in the filename.
63401       \param use_bigtiff Allow to save big tiff files (>4Gb).
63402     **/
63403     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
63404                                  const float *const voxel_size=0, const char *const description=0,
63405                                  const bool use_bigtiff=true) const {
63406       if (!filename)
63407         throw CImgArgumentException(_cimglist_instance
63408                                     "save_tiff(): Specified filename is (null).",
63409                                     cimglist_instance);
63410       if (is_empty()) { cimg::fempty(0,filename); return *this; }
63411 
63412 #ifndef cimg_use_tiff
63413       if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
63414       else cimglist_for(*this,l) {
63415           CImg<charT> nfilename(1024);
63416           cimg::number_filename(filename,l,6,nfilename);
63417           _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
63418         }
63419 #else
63420       ulongT siz = 0;
63421       cimglist_for(*this,l) siz+=_data[l].size();
63422       const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images
63423       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
63424       if (tif) {
63425         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
63426           const CImg<T>& img = (*this)[l];
63427           cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
63428         }
63429         TIFFClose(tif);
63430       } else
63431         throw CImgIOException(_cimglist_instance
63432                               "save_tiff(): Failed to open stream for file '%s'.",
63433                               cimglist_instance,
63434                               filename);
63435 #endif
63436       return *this;
63437     }
63438 
63439     //! Save list as a gzipped file, using external tool 'gzip'.
63440     /**
63441       \param filename Filename to write data to.
63442     **/
63443     const CImgList<T>& save_gzip_external(const char *const filename) const {
63444       if (!filename)
63445         throw CImgIOException(_cimglist_instance
63446                               "save_gzip_external(): Specified filename is (null).",
63447                               cimglist_instance);
63448       CImg<charT> command(1024), filename_tmp(256), body(256);
63449       const char
63450         *ext = cimg::split_filename(filename,body),
63451         *ext2 = cimg::split_filename(body,0);
63452       std::FILE *file;
63453       do {
63454         if (!cimg::strcasecmp(ext,"gz")) {
63455           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
63456                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
63457           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
63458                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63459         } else {
63460           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
63461                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
63462           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
63463                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63464         }
63465         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
63466       } while (file);
63467 
63468       if (is_saveable(body)) {
63469         save(filename_tmp);
63470         cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
63471                       cimg::gzip_path(),
63472                       CImg<charT>::string(filename_tmp)._system_strescape().data(),
63473                       CImg<charT>::string(filename)._system_strescape().data());
63474         cimg::system(command, cimg::gzip_path());
63475         file = cimg::std_fopen(filename,"rb");
63476         if (!file)
63477           throw CImgIOException(_cimglist_instance
63478                                 "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
63479                                 cimglist_instance,
63480                                 filename);
63481         else cimg::fclose(file);
63482         std::remove(filename_tmp);
63483       } else {
63484         CImg<charT> nfilename(1024);
63485         cimglist_for(*this,l) {
63486           cimg::number_filename(body,l,6,nfilename);
63487           if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
63488           _data[l].save_gzip_external(nfilename);
63489         }
63490       }
63491       return *this;
63492     }
63493 
63494     //! Save image sequence (using the OpenCV library when available).
63495     /**
63496        \param filename Filename to write data to.
63497        \param fps Number of frames per second.
63498        \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
63499        \param keep_open Tells if the video writer associated to the specified filename
63500        must be kept open or not (to allow frames to be added in the same file afterwards).
63501     **/
63502     const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
63503                                   const char *codec=0, const bool keep_open=false) const {
63504 #ifndef cimg_use_opencv
63505       cimg::unused(codec,keep_open);
63506       return save_ffmpeg_external(filename,fps);
63507 #else
63508       try {
63509         static cv::VideoWriter *writers[32] = { 0 };
63510         static CImgList<charT> filenames(32);
63511         static CImg<intT> sizes(32,2,1,1,0);
63512         static int last_used_index = -1;
63513 
63514         // Detect if a video writer already exists for the specified filename.
63515         cimg::mutex(9);
63516         int index = -1;
63517         if (filename) {
63518           if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
63519             index = last_used_index;
63520           } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
63521               index = l; break;
63522             }
63523         } else index = last_used_index;
63524         cimg::mutex(9,0);
63525 
63526         // Find empty slot for capturing video stream.
63527         if (index<0) {
63528           if (!filename)
63529             throw CImgArgumentException(_cimglist_instance
63530                                         "save_video(): No already open video writer found. You must specify a "
63531                                         "non-(null) filename argument for the first call.",
63532                                         cimglist_instance);
63533           else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
63534           if (index<0)
63535             throw CImgIOException(_cimglist_instance
63536                                   "save_video(): File '%s', no video writer slots available. "
63537                                   "You have to release some of your previously opened videos.",
63538                                   cimglist_instance,filename);
63539           if (is_empty())
63540             throw CImgInstanceException(_cimglist_instance
63541                                         "save_video(): Instance list is empty.",
63542                                         cimglist_instance);
63543           const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
63544           if (!W || !H)
63545             throw CImgInstanceException(_cimglist_instance
63546                                         "save_video(): Frame [0] is an empty image.",
63547                                         cimglist_instance);
63548           const char
63549             *const _codec = codec && *codec?codec:"h264",
63550             codec0 = cimg::uppercase(_codec[0]),
63551             codec1 = _codec[0]?cimg::uppercase(_codec[1]):0,
63552             codec2 = _codec[1]?cimg::uppercase(_codec[2]):0,
63553             codec3 = _codec[2]?cimg::uppercase(_codec[3]):0;
63554           cimg::mutex(9);
63555           writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H));
63556           if (!writers[index]->isOpened()) {
63557             delete writers[index];
63558             writers[index] = 0;
63559             cimg::mutex(9,0);
63560             throw CImgIOException(_cimglist_instance
63561                                   "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
63562                                   cimglist_instance,filename,
63563                                   codec0,codec1,codec2,codec3);
63564           }
63565           CImg<charT>::string(filename).move_to(filenames[index]);
63566           sizes(index,0) = W;
63567           sizes(index,1) = H;
63568           cimg::mutex(9,0);
63569         }
63570 
63571         if (!is_empty()) {
63572           const unsigned int W = sizes(index,0), H = sizes(index,1);
63573           cimg::mutex(9);
63574           cimglist_for(*this,l) {
63575             CImg<T> &src = _data[l];
63576             if (src.is_empty())
63577               cimg::warn(_cimglist_instance
63578                          "save_video(): Skip empty frame %d for file '%s'.",
63579                          cimglist_instance,l,filename);
63580             if (src._depth>1 || src._spectrum>3)
63581               cimg::warn(_cimglist_instance
63582                          "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
63583                          "Some image data may be ignored when writing frame into video file '%s'.",
63584                          cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
63585             if (src._width==W && src._height==H && src._spectrum==3)
63586               writers[index]->write(CImg<ucharT>(src)._cimg2cvmat());
63587             else {
63588               CImg<ucharT> _src(src,false);
63589               _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
63590               _src.resize(W,H,1,3,_src._spectrum==1);
63591               writers[index]->write(_src._cimg2cvmat());
63592             }
63593           }
63594           cimg::mutex(9,0);
63595         }
63596 
63597         cimg::mutex(9);
63598         if (!keep_open) {
63599           delete writers[index];
63600           writers[index] = 0;
63601           filenames[index].assign();
63602           sizes(index,0) = sizes(index,1) = 0;
63603           last_used_index = -1;
63604         } else last_used_index = index;
63605         cimg::mutex(9,0);
63606       } catch (CImgIOException &e) {
63607         if (!keep_open) return save_ffmpeg_external(filename,fps);
63608         throw e;
63609       }
63610       return *this;
63611 #endif
63612     }
63613 
63614     //! Save image sequence, using the external tool 'ffmpeg'.
63615     /**
63616       \param filename Filename to write data to.
63617       \param fps Number of frames per second.
63618       \param codec Type of compression.
63619       \param bitrate Output bitrate
63620     **/
63621     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
63622                                             const char *const codec=0, const unsigned int bitrate=2048) const {
63623       if (!filename)
63624         throw CImgArgumentException(_cimglist_instance
63625                                     "save_ffmpeg_external(): Specified filename is (null).",
63626                                     cimglist_instance);
63627       if (is_empty()) { cimg::fempty(0,filename); return *this; }
63628 
63629       const char
63630         *const ext = cimg::split_filename(filename),
63631         *const _codec = codec?codec:
63632         !cimg::strcasecmp(ext,"flv")?"flv":
63633         !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video";
63634 
63635       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
63636       CImgList<charT> filenames;
63637       std::FILE *file = 0;
63638       cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
63639         throw CImgInstanceException(_cimglist_instance
63640                                     "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
63641                                     cimglist_instance,
63642                                     filename);
63643       do {
63644         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63645                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63646         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
63647         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63648       } while (file);
63649       cimglist_for(*this,l) {
63650         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
63651         CImg<charT>::string(filename_tmp2).move_to(filenames);
63652         CImg<T> tmp = _data[l].get_shared();
63653         if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows
63654           tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false);
63655         if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color
63656           tmp.assign(tmp.get_resize(-100,-100,1,3),false);
63657         tmp.save_pnm(filename_tmp2);
63658       }
63659       cimg_snprintf(command,command._width,
63660                     "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
63661                     cimg::ffmpeg_path(),
63662                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
63663                     _codec,bitrate,fps,
63664                     CImg<charT>::string(filename)._system_strescape().data());
63665       cimg::system(command, cimg::ffmpeg_path());
63666       file = cimg::std_fopen(filename,"rb");
63667       if (!file)
63668         throw CImgIOException(_cimglist_instance
63669                               "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
63670                               cimglist_instance,
63671                               filename);
63672       else cimg::fclose(file);
63673       cimglist_for(*this,l) std::remove(filenames[l]);
63674       return *this;
63675     }
63676 
63677     //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
63678     /**
63679        \param is_compressed tells if zlib compression must be used for serialization
63680        (this requires 'cimg_use_zlib' been enabled).
63681     **/
63682     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
63683 #ifndef cimg_use_zlib
63684       if (is_compressed)
63685         cimg::warn(_cimglist_instance
63686                    "get_serialize(): Unable to compress data unless zlib is enabled, "
63687                    "storing them uncompressed.",
63688                    cimglist_instance);
63689 #endif
63690       CImgList<ucharT> stream;
63691       CImg<charT> tmpstr(128);
63692       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
63693       if (std::strstr(ptype,"unsigned")==ptype)
63694         cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
63695       else
63696         cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
63697       CImg<ucharT>::string(tmpstr,false).move_to(stream);
63698       cimglist_for(*this,l) {
63699         const CImg<T>& img = _data[l];
63700         cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
63701         CImg<ucharT>::string(tmpstr,false).move_to(stream);
63702         if (img._data) {
63703           CImg<T> tmp;
63704           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
63705           const CImg<T>& ref = cimg::endianness()?tmp:img;
63706           bool failed_to_compress = true;
63707           if (is_compressed) {
63708 #ifdef cimg_use_zlib
63709             const ulongT siz = sizeof(T)*ref.size();
63710             uLongf csiz = (ulongT)compressBound(siz);
63711             Bytef *const cbuf = new Bytef[csiz];
63712             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
63713               cimg::warn(_cimglist_instance
63714                          "get_serialize(): Failed to save compressed data, saving them uncompressed.",
63715                          cimglist_instance);
63716             else {
63717               cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
63718               CImg<ucharT>::string(tmpstr,false).move_to(stream);
63719               CImg<ucharT>(cbuf,csiz).move_to(stream);
63720               delete[] cbuf;
63721               failed_to_compress = false;
63722             }
63723 #endif
63724           }
63725           if (failed_to_compress) { // Write in a non-compressed way
63726             CImg<charT>::string("\n",false).move_to(stream);
63727             stream.insert(1);
63728             stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
63729           }
63730         } else CImg<charT>::string("\n",false).move_to(stream);
63731       }
63732       cimglist_apply(stream,unroll)('y');
63733       return stream>'y';
63734     }
63735 
63736     //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
63737     template<typename t>
63738     static CImgList<T> get_unserialize(const CImg<t>& buffer) {
63739 #ifdef cimg_use_zlib
63740 #define _cimgz_unserialize_case(Tss) { \
63741         Bytef *cbuf = 0; \
63742         if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type<bool>::string()) { \
63743           cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
63744           for (ulongT k = 0; k<csiz; ++k) *(_cbuf++) = (Bytef)*(stream++); \
63745           is_bytef = false; \
63746         } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
63747         raw.assign(W,H,D,C); \
63748         uLongf destlen = raw.size()*sizeof(Tss); \
63749         uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
63750         if (!is_bytef) delete[] cbuf; \
63751       }
63752 #else
63753 #define _cimgz_unserialize_case(Tss) \
63754       throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
63755                                   "unless zlib is enabled.", \
63756                                   pixel_type());
63757 #endif
63758 
63759 #define _cimg_unserialize_case(Ts,Tss) \
63760       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
63761         for (unsigned int l = 0; l<N; ++l) { \
63762           j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
63763           ++stream; tmp[j] = 0; \
63764           W = H = D = C = 0; csiz = 0; \
63765           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
63766             throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
63767                                         "image #%u in serialized buffer.", \
63768                                         pixel_type(),W,H,D,C,l); \
63769           if (W*H*D*C>0) { \
63770             CImg<Tss> raw; \
63771             CImg<T> &img = res._data[l]; \
63772             if (err==5) _cimgz_unserialize_case(Tss) \
63773             else { \
63774               raw.assign(W,H,D,C); \
63775               CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
63776               if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \
63777               else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
63778             } \
63779             if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
63780             raw.move_to(img); \
63781           } \
63782         } \
63783         loaded = true; \
63784       }
63785 
63786       if (buffer.is_empty())
63787         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
63788                                     pixel_type());
63789       CImgList<T> res;
63790       const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
63791       bool loaded = false, endian = cimg::endianness(), is_bytef = false;
63792       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
63793       *tmp = *str_pixeltype = *str_endian = 0;
63794       unsigned int j, N = 0, W, H, D, C;
63795       uint64T csiz;
63796       int i, err;
63797       cimg::unused(is_bytef);
63798       do {
63799         j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
63800         ++stream; tmp[j] = 0;
63801       } while (*tmp=='#' && stream<estream);
63802       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
63803                         &N,str_pixeltype._data,str_endian._data);
63804       if (err<2)
63805         throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
63806                                     pixel_type());
63807       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
63808       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
63809       res.assign(N);
63810       _cimg_unserialize_case("bool",bool);
63811       _cimg_unserialize_case("unsigned_char",unsigned char);
63812       _cimg_unserialize_case("uchar",unsigned char);
63813       _cimg_unserialize_case("char",char);
63814       _cimg_unserialize_case("unsigned_short",unsigned short);
63815       _cimg_unserialize_case("ushort",unsigned short);
63816       _cimg_unserialize_case("short",short);
63817       _cimg_unserialize_case("unsigned_int",unsigned int);
63818       _cimg_unserialize_case("uint",unsigned int);
63819       _cimg_unserialize_case("int",int);
63820       _cimg_unserialize_case("unsigned_int64",uint64T);
63821       _cimg_unserialize_case("uint64",uint64T);
63822       _cimg_unserialize_case("int64",int64T);
63823       _cimg_unserialize_case("float",float);
63824       _cimg_unserialize_case("double",double);
63825       if (!loaded)
63826         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
63827                                     "in serialized buffer.",
63828                                     pixel_type(),str_pixeltype._data);
63829       return res;
63830     }
63831 
63832     //@}
63833     //----------------------------------
63834     //
63835     //! \name Others
63836     //@{
63837     //----------------------------------
63838 
63839     //! Return a CImg pre-defined font with requested height.
63840     /**
63841        \param font_height Height of the desired font (exact match for 13,23,53,103).
63842        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
63843     **/
63844     static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) {
63845       if (!requested_height) return CImgList<ucharT>::const_empty();
63846       cimg::mutex(11);
63847       static const unsigned char font_resizemap[] = {
63848         0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30,
63849         32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52,
63850         54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72,
63851         73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
63852         90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
63853         107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
63854         123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
63855         138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
63856         152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165,
63857         166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179,
63858         180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192,
63859         193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205,
63860         206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218,
63861         219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231,
63862         231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243,
63863         244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 };
63864       static const char *const *font_data[] = {
63865         cimg::data_font_small,
63866         cimg::data_font_normal,
63867         cimg::data_font_large,
63868         cimg::data_font_huge };
63869       static const unsigned int
63870         font_width[] = { 10,26,52,104 },
63871         font_height[] = { 13,32,64,128 },
63872         font_M[] = { 86,91,91,47 },
63873         font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*),
63874                          sizeof(cimg::data_font_normal)/sizeof(char*),
63875                          sizeof(cimg::data_font_large)/sizeof(char*),
63876                          sizeof(cimg::data_font_huge)/sizeof(char*) };
63877       static const unsigned char font_is_binary[] = { 1,0,0,1 };
63878       static CImg<ucharT> font_base[4];
63879 
63880       unsigned int ind =
63881         requested_height<=font_height[0]?0U:
63882         requested_height<=font_height[1]?1U:
63883         requested_height<=font_height[2]?2U:3U;
63884 
63885       // Decompress nearest base font data if needed.
63886       CImg<ucharT> &basef = font_base[ind];
63887       if (!basef) {
63888         basef.assign(256*font_width[ind],font_height[ind]);
63889 
63890         unsigned char *ptrd = basef;
63891         const unsigned char *const ptrde = basef.end();
63892 
63893         // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb).
63894         CImg<char> dataf;
63895         for (unsigned int k = 0; k<font_chunk[ind]; ++k)
63896           dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x');
63897 
63898         // Uncompress font data (decode RLE).
63899         const unsigned int M = font_M[ind];
63900         if (font_is_binary[ind])
63901           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
63902             const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n;
63903             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
63904             else { std::memset(ptrd,v,ptrde - ptrd); break; }
63905           }
63906         else
63907           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
63908             int n = (int)*ptrs - M - 32, v = 0;
63909             if (n>=0) { v = 85*n; n = 1; }
63910             else {
63911               n = -n;
63912               v = (int)*(++ptrs) - M - 32;
63913               if (v<0) { v = 0; --ptrs; } else v*=85;
63914             }
63915             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
63916             else { std::memset(ptrd,v,ptrde - ptrd); break; }
63917           }
63918       }
63919 
63920       // Find optimal font cache location to return.
63921       static CImgList<ucharT> fonts[16];
63922       static bool is_variable_widths[16] = { 0 };
63923       ind = ~0U;
63924       for (int i = 0; i<16; ++i)
63925         if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) {
63926           ind = (unsigned int)i; break; // Found empty slot or cached font
63927         }
63928       if (ind==~0U) { // No empty slots nor existing font in cache
63929         fonts->assign();
63930         std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList<ucharT>));
63931         std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
63932         std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font
63933       }
63934       CImgList<ucharT> &font = fonts[ind];
63935 
63936       // Render requested font.
63937       if (!font) {
63938         is_variable_widths[ind] = is_variable_width;
63939         basef.get_split('x',256).move_to(font);
63940 
63941 //        cimg::tic();
63942 
63943         if (requested_height!=font[0]._height)
63944           cimglist_for(font,l) {
63945             font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5);
63946             cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr];
63947           }
63948 
63949 //        cimg::toc();
63950 //        std::exit(0);
63951 
63952         if (is_variable_width) { // Crop font
63953           cimglist_for(font,l) {
63954             CImg<ucharT>& letter = font[l];
63955             int xmin = letter.width(), xmax = 0;
63956             cimg_forX(letter,x) { // Find xmin
63957               cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; }
63958               if (xmin!=letter.width()) break;
63959             }
63960             cimg_rofX(letter,x) { // Find xmax
63961               cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; }
63962               if (xmax) break;
63963             }
63964             if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1);
63965           }
63966           font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0);
63967           if (' ' + 256<font.size()) font[' ' + 256].resize(font[(int)'f']._width,-100,-100,-100,0);
63968         }
63969         font.insert(256,0);
63970         cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
63971       }
63972       cimg::mutex(11,0);
63973       return font;
63974     }
63975 
63976     //! Compute a 1D Fast Fourier Transform, along specified axis.
63977     /**
63978        \param axis Axis along which the Fourier transform is computed.
63979        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
63980     **/
63981     CImgList<T>& FFT(const char axis, const bool invert=false) {
63982       if (is_empty()) return *this;
63983       if (_width==1) insert(1);
63984       if (_width>2)
63985         cimg::warn(_cimglist_instance
63986                    "FFT(): Instance has more than 2 images",
63987                    cimglist_instance);
63988       CImg<T>::FFT(_data[0],_data[1],axis,invert);
63989       return *this;
63990     }
63991 
63992     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
63993     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
63994       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
63995     }
63996 
63997     //! Compute n-D Fast Fourier Transform.
63998     /**
63999       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
64000     **/
64001     CImgList<T>& FFT(const bool invert=false) {
64002       if (is_empty()) return *this;
64003       if (_width==1) insert(1);
64004       if (_width>2)
64005         cimg::warn(_cimglist_instance
64006                    "FFT(): Instance has more than 2 images",
64007                    cimglist_instance);
64008 
64009       CImg<T>::FFT(_data[0],_data[1],invert);
64010       return *this;
64011     }
64012 
64013     //! Compute n-D Fast Fourier Transform \newinstance.
64014     CImgList<Tfloat> get_FFT(const bool invert=false) const {
64015       return CImgList<Tfloat>(*this,false).FFT(invert);
64016     }
64017 
64018     //! Reverse primitives orientations of a 3D object.
64019     /**
64020     **/
64021     CImgList<T>& reverse_object3d() {
64022       cimglist_for(*this,l) {
64023         CImg<T>& p = _data[l];
64024         switch (p.size()) {
64025         case 2 : case 3: cimg::swap(p[0],p[1]); break;
64026         case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
64027         case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
64028         case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
64029         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;
64030         }
64031       }
64032       return *this;
64033     }
64034 
64035     //! Reverse primitives orientations of a 3D object \newinstance.
64036     CImgList<T> get_reverse_object3d() const {
64037       return (+*this).reverse_object3d();
64038     }
64039 
64040     //@}
64041   }; // struct CImgList { ...
64042 
64043   // Completion of previously declared functions
64044   //--------------------------------------------
64045   namespace cimg {
64046 
64047     // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
64048     // (throw a CImgIOException when macro 'cimg_use_r' is defined).
64049     inline FILE* _stdin(const bool throw_exception) {
64050 #ifndef cimg_use_r
64051       cimg::unused(throw_exception);
64052       return stdin;
64053 #else
64054       if (throw_exception) {
64055         cimg::exception_mode(0);
64056         throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
64057                               "('cimg_use_r' is defined).");
64058       }
64059       return 0;
64060 #endif
64061     }
64062 
64063     inline FILE* _stdout(const bool throw_exception) {
64064 #ifndef cimg_use_r
64065       cimg::unused(throw_exception);
64066       return stdout;
64067 #else
64068       if (throw_exception) {
64069         cimg::exception_mode(0);
64070         throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
64071                               "('cimg_use_r' is defined).");
64072       }
64073       return 0;
64074 #endif
64075     }
64076 
64077     inline FILE* _stderr(const bool throw_exception) {
64078 #ifndef cimg_use_r
64079       cimg::unused(throw_exception);
64080       return stderr;
64081 #else
64082       if (throw_exception) {
64083         cimg::exception_mode(0);
64084         throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
64085                               "('cimg_use_r' is defined).");
64086       }
64087       return 0;
64088 #endif
64089     }
64090 
64091     // Open a file (similar to std:: fopen(), but with wide character support on Windows).
64092     inline std::FILE *std_fopen(const char *const path, const char *const mode) {
64093       std::FILE *const res = std::fopen(path,mode);
64094       if (res) return res;
64095 #if cimg_OS==2
64096       // Try alternative method, with wide-character string.
64097       int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
64098       if (err) {
64099         CImg<wchar_t> wpath((unsigned int)err);
64100         err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
64101         if (err) { // Convert 'mode' to a wide-character string
64102           err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
64103           if (err) {
64104             CImg<wchar_t> wmode((unsigned int)err);
64105             if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err))
64106               return _wfopen(wpath,wmode);
64107           }
64108         }
64109       }
64110 #endif
64111       return 0;
64112     }
64113 
64114     //! Search path of an executable (Windows only).
64115 #if cimg_OS==2
64116     inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
64117       char *ptr = 0;
64118       DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr);
64119       return err!=0;
64120     }
64121 #endif
64122 
64123     //! Get the file or directory attributes with support for UTF-8 paths (Windows only).
64124 #if cimg_OS==2
64125     inline DWORD win_getfileattributes(const char *const path) {
64126       DWORD res = GetFileAttributesA(path);
64127       if (res==INVALID_FILE_ATTRIBUTES) {
64128         // Try alternative method, with wide-character string.
64129         int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
64130         if (err) {
64131           CImg<wchar_t> wpath((unsigned int)err);
64132           if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath);
64133         }
64134       }
64135       return res;
64136     }
64137 #endif
64138 
64139     //! Get/set path to the <i>Program Files/</i> directory (Windows only).
64140     /**
64141        \param user_path Specified path, or \c 0 to get the path currently used.
64142        \param reinit_path Force path to be recalculated (may take some time).
64143        \return Path containing the program files.
64144     **/
64145 #if cimg_OS==2
64146     inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
64147       static CImg<char> s_path;
64148       cimg::mutex(7);
64149       if (reinit_path) s_path.assign();
64150       if (user_path) {
64151         if (!s_path) s_path.assign(1024);
64152         std::strncpy(s_path,user_path,1023);
64153       } else if (!s_path) {
64154         s_path.assign(MAX_PATH);
64155         *s_path = 0;
64156         // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
64157 #if !defined(__INTEL_COMPILER)
64158         if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
64159           const char *const pfPath = std::getenv("PROGRAMFILES");
64160           if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
64161           else std::strcpy(s_path,"C:\\PROGRA~1");
64162         }
64163 #else
64164         std::strcpy(s_path,"C:\\PROGRA~1");
64165 #endif
64166       }
64167       cimg::mutex(7,0);
64168       return s_path;
64169     }
64170 #endif
64171 
64172     //! Get/set path to the \c curl binary.
64173     /**
64174        \param user_path Specified path, or \c 0 to get the path currently used.
64175        \param reinit_path Force path to be recalculated (may take some time).
64176        \return Path containing the \c curl binary.
64177     **/
64178     inline const char *curl_path(const char *const user_path, const bool reinit_path) {
64179       static CImg<char> s_path;
64180       cimg::mutex(7);
64181       if (reinit_path) s_path.assign();
64182       if (user_path) {
64183         if (!s_path) s_path.assign(1024);
64184         std::strncpy(s_path,user_path,1023);
64185       } else if (!s_path) {
64186         s_path.assign(1024);
64187         bool path_found = false;
64188         std::FILE *file = 0;
64189 #if cimg_OS==2
64190         if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true;
64191         if (!path_found) {
64192           std::strcpy(s_path,".\\curl.exe");
64193           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64194         }
64195         if (!path_found) std::strcpy(s_path,"curl.exe");
64196 #else
64197         if (!path_found) {
64198           std::strcpy(s_path,"./curl");
64199           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64200         }
64201         if (!path_found) std::strcpy(s_path,"curl");
64202 #endif
64203         winformat_string(s_path);
64204       }
64205       cimg::mutex(7,0);
64206       return s_path;
64207     }
64208 
64209     //! Get/set path to the \c dcraw binary.
64210     /**
64211        \param user_path Specified path, or \c 0 to get the path currently used.
64212        \param reinit_path Force path to be recalculated (may take some time).
64213        \return Path containing the \c dcraw binary.
64214     **/
64215     inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
64216       static CImg<char> s_path;
64217       cimg::mutex(7);
64218       if (reinit_path) s_path.assign();
64219       if (user_path) {
64220         if (!s_path) s_path.assign(1024);
64221         std::strncpy(s_path,user_path,1023);
64222       } else if (!s_path) {
64223         s_path.assign(1024);
64224         bool path_found = false;
64225         std::FILE *file = 0;
64226 #if cimg_OS==2
64227         if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true;
64228         if (!path_found) {
64229           std::strcpy(s_path,".\\dcraw.exe");
64230           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64231         }
64232         if (!path_found) std::strcpy(s_path,"dcraw.exe");
64233 #else
64234         if (!path_found) {
64235           std::strcpy(s_path,"./dcraw");
64236           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64237         }
64238         if (!path_found) std::strcpy(s_path,"dcraw");
64239 #endif
64240         winformat_string(s_path);
64241       }
64242       cimg::mutex(7,0);
64243       return s_path;
64244     }
64245 
64246     //! Get/set path to the FFMPEG's \c ffmpeg binary.
64247     /**
64248        \param user_path Specified path, or \c 0 to get the path currently used.
64249        \param reinit_path Force path to be recalculated (may take some time).
64250        \return Path containing the \c ffmpeg binary.
64251     **/
64252     inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
64253       static CImg<char> s_path;
64254       cimg::mutex(7);
64255       if (reinit_path) s_path.assign();
64256       if (user_path) {
64257         if (!s_path) s_path.assign(1024);
64258         std::strncpy(s_path,user_path,1023);
64259       } else if (!s_path) {
64260         s_path.assign(1024);
64261         bool path_found = false;
64262         std::FILE *file = 0;
64263 #if cimg_OS==2
64264         if (win_searchpath("ffmpeg.exe",s_path,s_path._width)) path_found = true;
64265         if (!path_found) {
64266           std::strcpy(s_path,".\\ffmpeg.exe");
64267           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64268         }
64269         if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
64270 #else
64271         if (!path_found) {
64272           std::strcpy(s_path,"./ffmpeg");
64273           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64274         }
64275         if (!path_found) std::strcpy(s_path,"ffmpeg");
64276 #endif
64277         winformat_string(s_path);
64278       }
64279       cimg::mutex(7,0);
64280       return s_path;
64281     }
64282 
64283     //! Get/set path to the GraphicsMagick's \c gm binary.
64284     /**
64285        \param user_path Specified path, or \c 0 to get the path currently used.
64286        \param reinit_path Force path to be recalculated (may take some time).
64287        \return Path containing the \c gm binary.
64288     **/
64289     inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
64290       static CImg<char> s_path;
64291       cimg::mutex(7);
64292       if (reinit_path) s_path.assign();
64293       if (user_path) {
64294         if (!s_path) s_path.assign(1024);
64295         std::strncpy(s_path,user_path,1023);
64296       } else if (!s_path) {
64297         s_path.assign(1024);
64298         bool path_found = false;
64299         std::FILE *file = 0;
64300 #if cimg_OS==2
64301         if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true;
64302         const char *const pf_path = win_programfiles_path();
64303         if (!path_found) {
64304           std::strcpy(s_path,".\\gm.exe");
64305           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64306         }
64307         for (int k = 32; k>=10 && !path_found; --k) {
64308           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
64309           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64310         }
64311         for (int k = 9; k>=0 && !path_found; --k) {
64312           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
64313           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64314         }
64315         for (int k = 32; k>=0 && !path_found; --k) {
64316           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
64317           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64318         }
64319         for (int k = 32; k>=10 && !path_found; --k) {
64320           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64321           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64322         }
64323         for (int k = 9; k>=0 && !path_found; --k) {
64324           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64325           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64326         }
64327         for (int k = 32; k>=0 && !path_found; --k) {
64328           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64329           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64330         }
64331         for (int k = 32; k>=10 && !path_found; --k) {
64332           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
64333           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64334         }
64335         for (int k = 9; k>=0 && !path_found; --k) {
64336           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
64337           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64338         }
64339         for (int k = 32; k>=0 && !path_found; --k) {
64340           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
64341           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64342         }
64343         for (int k = 32; k>=10 && !path_found; --k) {
64344           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
64345           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64346         }
64347         for (int k = 9; k>=0 && !path_found; --k) {
64348           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
64349           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64350         }
64351         for (int k = 32; k>=0 && !path_found; --k) {
64352           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
64353           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64354         }
64355         for (int k = 32; k>=10 && !path_found; --k) {
64356           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
64357           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64358         }
64359         for (int k = 9; k>=0 && !path_found; --k) {
64360           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
64361           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64362         }
64363         for (int k = 32; k>=0 && !path_found; --k) {
64364           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
64365           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64366         }
64367         for (int k = 32; k>=10 && !path_found; --k) {
64368           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
64369           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64370         }
64371         for (int k = 9; k>=0 && !path_found; --k) {
64372           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
64373           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64374         }
64375         for (int k = 32; k>=0 && !path_found; --k) {
64376           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
64377           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64378         }
64379         if (!path_found) std::strcpy(s_path,"gm.exe");
64380 #else
64381         if (!path_found) {
64382           std::strcpy(s_path,"./gm");
64383           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64384         }
64385         if (!path_found) std::strcpy(s_path,"gm");
64386 #endif
64387         winformat_string(s_path);
64388       }
64389       cimg::mutex(7,0);
64390       return s_path;
64391     }
64392 
64393     //! Get/set path to the \c gunzip binary.
64394     /**
64395        \param user_path Specified path, or \c 0 to get the path currently used.
64396        \param reinit_path Force path to be recalculated (may take some time).
64397        \return Path containing the \c gunzip binary.
64398     **/
64399     inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
64400       static CImg<char> s_path;
64401       cimg::mutex(7);
64402       if (reinit_path) s_path.assign();
64403       if (user_path) {
64404         if (!s_path) s_path.assign(1024);
64405         std::strncpy(s_path,user_path,1023);
64406       } else if (!s_path) {
64407         s_path.assign(1024);
64408         bool path_found = false;
64409         std::FILE *file = 0;
64410 #if cimg_OS==2
64411         if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true;
64412         if (!path_found) {
64413           std::strcpy(s_path,".\\gunzip.exe");
64414           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64415         }
64416         if (!path_found) std::strcpy(s_path,"gunzip.exe");
64417 #else
64418         if (!path_found) {
64419           std::strcpy(s_path,"./gunzip");
64420           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64421         }
64422         if (!path_found) std::strcpy(s_path,"gunzip");
64423 #endif
64424         winformat_string(s_path);
64425       }
64426       cimg::mutex(7,0);
64427       return s_path;
64428     }
64429 
64430     //! Get/set path to the \c gzip binary.
64431     /**
64432        \param user_path Specified path, or \c 0 to get the path currently used.
64433        \param reinit_path Force path to be recalculated (may take some time).
64434        \return Path containing the \c gzip binary.
64435     **/
64436     inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
64437       static CImg<char> s_path;
64438       cimg::mutex(7);
64439       if (reinit_path) s_path.assign();
64440       if (user_path) {
64441         if (!s_path) s_path.assign(1024);
64442         std::strncpy(s_path,user_path,1023);
64443       } else if (!s_path) {
64444         s_path.assign(1024);
64445         bool path_found = false;
64446         std::FILE *file = 0;
64447 #if cimg_OS==2
64448         if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true;
64449         if (!path_found) {
64450           std::strcpy(s_path,".\\gzip.exe");
64451           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64452         }
64453         if (!path_found) std::strcpy(s_path,"gzip.exe");
64454 #else
64455         if (!path_found) {
64456           std::strcpy(s_path,"./gzip");
64457           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64458         }
64459         if (!path_found) std::strcpy(s_path,"gzip");
64460 #endif
64461         winformat_string(s_path);
64462       }
64463       cimg::mutex(7,0);
64464       return s_path;
64465     }
64466 
64467     //! Get/set path to the ImageMagick's \c convert binary.
64468     /**
64469        \param user_path Specified path, or \c 0 to get the path currently used.
64470        \param reinit_path Force path to be recalculated (may take some time).
64471        \return Path containing the \c convert binary.
64472     **/
64473     inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
64474       static CImg<char> s_path;
64475       cimg::mutex(7);
64476       if (reinit_path) s_path.assign();
64477       if (user_path) {
64478         if (!s_path) s_path.assign(1024);
64479         std::strncpy(s_path,user_path,1023);
64480       } else if (!s_path) {
64481         s_path.assign(1024);
64482         bool path_found = false;
64483         std::FILE *file = 0;
64484 #if cimg_OS==2
64485         if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
64486         const char *const pf_path = win_programfiles_path();
64487         for (int l = 0; l<2 && !path_found; ++l) {
64488           const char *const s_exe = l?"convert":"magick";
64489           cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
64490           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64491           for (int k = 32; k>=10 && !path_found; --k) {
64492             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
64493             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64494           }
64495           for (int k = 9; k>=0 && !path_found; --k) {
64496             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
64497             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64498           }
64499           for (int k = 32; k>=0 && !path_found; --k) {
64500             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
64501             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64502           }
64503           for (int k = 32; k>=10 && !path_found; --k) {
64504             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
64505             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64506           }
64507           for (int k = 9; k>=0 && !path_found; --k) {
64508             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
64509             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64510           }
64511           for (int k = 32; k>=0 && !path_found; --k) {
64512             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
64513             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64514           }
64515           for (int k = 32; k>=10 && !path_found; --k) {
64516             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
64517             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64518           }
64519           for (int k = 9; k>=0 && !path_found; --k) {
64520             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
64521             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64522           }
64523           for (int k = 32; k>=0 && !path_found; --k) {
64524             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
64525             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64526           }
64527           for (int k = 32; k>=10 && !path_found; --k) {
64528             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64529             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64530           }
64531           for (int k = 9; k>=0 && !path_found; --k) {
64532             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64533             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64534           }
64535           for (int k = 32; k>=0 && !path_found; --k) {
64536             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64537             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64538           }
64539           for (int k = 32; k>=10 && !path_found; --k) {
64540             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
64541             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64542           }
64543           for (int k = 9; k>=0 && !path_found; --k) {
64544             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
64545             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64546           }
64547           for (int k = 32; k>=0 && !path_found; --k) {
64548             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
64549             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64550           }
64551           for (int k = 32; k>=10 && !path_found; --k) {
64552             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64553             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64554           }
64555           for (int k = 9; k>=0 && !path_found; --k) {
64556             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64557             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64558           }
64559           for (int k = 32; k>=0 && !path_found; --k) {
64560             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
64561             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64562           }
64563           if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
64564         }
64565 #else
64566         std::strcpy(s_path,"./magick");
64567         if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64568         if (!path_found) {
64569           std::strcpy(s_path,"./convert");
64570           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64571         }
64572         if (!path_found) std::strcpy(s_path,"convert");
64573 #endif
64574         winformat_string(s_path);
64575       }
64576       cimg::mutex(7,0);
64577       return s_path;
64578     }
64579 
64580     //! Get/set path to the Medcon's \c medcon binary.
64581     /**
64582        \param user_path Specified path, or \c 0 to get the path currently used.
64583        \param reinit_path Force path to be recalculated (may take some time).
64584        \return Path containing the \c medcon binary.
64585     **/
64586     inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
64587       static CImg<char> s_path;
64588       cimg::mutex(7);
64589       if (reinit_path) s_path.assign();
64590       if (user_path) {
64591         if (!s_path) s_path.assign(1024);
64592         std::strncpy(s_path,user_path,1023);
64593       } else if (!s_path) {
64594         s_path.assign(1024);
64595         bool path_found = false;
64596         std::FILE *file = 0;
64597 #if cimg_OS==2
64598         if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true;
64599         const char *const pf_path = win_programfiles_path();
64600         if (!path_found) {
64601           std::strcpy(s_path,".\\medcon.exe");
64602           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64603         }
64604         if (!path_found) {
64605           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
64606           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64607         }
64608         if (!path_found) {
64609           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
64610           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64611         }
64612         if (!path_found) {
64613           std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
64614           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64615         }
64616         if (!path_found) std::strcpy(s_path,"medcon.exe");
64617 #else
64618         if (!path_found) {
64619           std::strcpy(s_path,"./medcon");
64620           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64621         }
64622         if (!path_found) std::strcpy(s_path,"medcon");
64623 #endif
64624         winformat_string(s_path);
64625       }
64626       cimg::mutex(7,0);
64627       return s_path;
64628     }
64629 
64630     //! Get/set path to store temporary files.
64631     /**
64632        \param user_path Specified path, or \c 0 to get the path currently used.
64633        \param reinit_path Force path to be recalculated (may take some time).
64634        \return Path where temporary files can be saved.
64635     **/
64636     inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
64637 #define _cimg_test_temporary_path(p) \
64638       if (!path_found) { \
64639         cimg_snprintf(s_path,s_path._width,"%s",p); \
64640         cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
64641         if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
64642       }
64643       static CImg<char> s_path;
64644       cimg::mutex(7);
64645       if (reinit_path) s_path.assign();
64646       if (user_path) {
64647         if (!s_path) s_path.assign(1024);
64648         std::strncpy(s_path,user_path,1023);
64649       } else if (!s_path) {
64650         s_path.assign(1024);
64651         bool path_found = false;
64652         CImg<char> tmp(1024), filename_tmp(256);
64653         std::FILE *file = 0;
64654         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
64655         char *tmpPath = std::getenv("TMP");
64656         if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
64657         if (tmpPath) _cimg_test_temporary_path(tmpPath);
64658 #if cimg_OS==2
64659         _cimg_test_temporary_path("C:\\WINNT\\Temp");
64660         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
64661         _cimg_test_temporary_path("C:\\Temp");
64662         _cimg_test_temporary_path("C:");
64663         _cimg_test_temporary_path("D:\\WINNT\\Temp");
64664         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
64665         _cimg_test_temporary_path("D:\\Temp");
64666         _cimg_test_temporary_path("D:");
64667 #else
64668         _cimg_test_temporary_path("/tmp");
64669         _cimg_test_temporary_path("/var/tmp");
64670 #endif
64671         if (!path_found) {
64672           *s_path = 0;
64673           std::strncpy(tmp,filename_tmp,tmp._width - 1);
64674           if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
64675         }
64676         if (!path_found) {
64677           cimg::mutex(7,0);
64678           throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
64679         }
64680       }
64681       cimg::mutex(7,0);
64682       return s_path;
64683     }
64684 
64685     //! Get/set path to the \c wget binary.
64686     /**
64687        \param user_path Specified path, or \c 0 to get the path currently used.
64688        \param reinit_path Force path to be recalculated (may take some time).
64689        \return Path containing the \c wget binary.
64690     **/
64691     inline const char *wget_path(const char *const user_path, const bool reinit_path) {
64692       static CImg<char> s_path;
64693       cimg::mutex(7);
64694       if (reinit_path) s_path.assign();
64695       if (user_path) {
64696         if (!s_path) s_path.assign(1024);
64697         std::strncpy(s_path,user_path,1023);
64698       } else if (!s_path) {
64699         s_path.assign(1024);
64700         bool path_found = false;
64701         std::FILE *file = 0;
64702 #if cimg_OS==2
64703         if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true;
64704         if (!path_found) {
64705           std::strcpy(s_path,".\\wget.exe");
64706           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64707         }
64708         if (!path_found) std::strcpy(s_path,"wget.exe");
64709 #else
64710         if (!path_found) {
64711           std::strcpy(s_path,"./wget");
64712           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64713         }
64714         if (!path_found) std::strcpy(s_path,"wget");
64715 #endif
64716         winformat_string(s_path);
64717       }
64718       cimg::mutex(7,0);
64719       return s_path;
64720     }
64721 
64722 
64723     // [internal] Sorting function, used by cimg::files().
64724     inline int _sort_files(const void* a, const void* b) {
64725       const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
64726       return std::strcmp(sa._data,sb._data);
64727     }
64728 
64729     //! Generate a numbered version of a filename.
64730     inline char* number_filename(const char *const filename, const int number,
64731                                  const unsigned int digits, char *const str) {
64732       if (!filename) { if (str) *str = 0; return 0; }
64733       const unsigned int siz = (unsigned int)std::strlen(filename);
64734       CImg<char> format(16), body(siz + 32);
64735       const char *const ext = cimg::split_filename(filename,body);
64736       if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits);
64737       else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits);
64738       cimg_snprintf(str,1024,format._data,body._data,number,ext);
64739       return str;
64740     }
64741 
64742     //! Return list of files/directories in specified directory.
64743     /**
64744        \param path Path to the directory. Set to 0 for current directory.
64745        \param is_pattern Tell if specified path has a matching pattern in it.
64746        \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
64747        \param include_path Tell if \c path must be included in resulting filenames.
64748        \return A list of filenames.
64749     **/
64750     inline CImgList<char> files(const char *const path, const bool is_pattern=false,
64751                                 const unsigned int mode=2, const bool include_path=false) {
64752       if (!path || !*path) return files("*",true,mode,include_path);
64753       CImgList<char> res;
64754 
64755       // If path is a valid folder name, ignore argument 'is_pattern'.
64756       const bool _is_pattern = is_pattern && !cimg::is_directory(path);
64757       bool is_root = false, is_current = false;
64758       cimg::unused(is_root,is_current);
64759 
64760       // Clean format of input path.
64761       CImg<char> pattern, _path = CImg<char>::string(path);
64762 #if cimg_OS==2
64763       for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
64764 #endif
64765       char *pd = _path;
64766       for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
64767       *pd = 0;
64768       unsigned int lp = (unsigned int)std::strlen(_path);
64769       if (!_is_pattern && lp && _path[lp - 1]=='/') {
64770         _path[lp - 1] = 0; --lp;
64771 #if cimg_OS!=2
64772         is_root = !*_path;
64773 #endif
64774       }
64775 
64776       // Separate folder path and matching pattern.
64777       if (_is_pattern) {
64778         const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
64779         CImg<char>::string(_path).move_to(pattern);
64780         if (bpos) {
64781           _path[bpos - 1] = 0; // End 'path' at last slash
64782 #if cimg_OS!=2
64783           is_root = !*_path;
64784 #endif
64785         } else { // No path to folder specified, assuming current folder
64786           is_current = true; *_path = 0;
64787         }
64788         lp = (unsigned int)std::strlen(_path);
64789       }
64790 
64791       // Windows version.
64792 #if cimg_OS==2
64793       if (!_is_pattern) {
64794         pattern.assign(lp + 3);
64795         std::memcpy(pattern,_path,lp);
64796         pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
64797       }
64798       WIN32_FIND_DATAA file_data;
64799       const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
64800       if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
64801       do {
64802         const char *const filename = file_data.cFileName;
64803         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
64804           const unsigned int lf = (unsigned int)std::strlen(filename);
64805           const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
64806           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
64807             if (include_path) {
64808               CImg<char> full_filename((lp?lp+1:0) + lf + 1);
64809               if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
64810               std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
64811               full_filename.move_to(res);
64812             } else CImg<char>(filename,lf + 1).move_to(res);
64813           }
64814         }
64815       } while (FindNextFileA(dir,&file_data));
64816       FindClose(dir);
64817 
64818       // Unix version (posix).
64819 #elif cimg_OS == 1
64820       DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
64821       if (!dir) return CImgList<char>::const_empty();
64822       struct dirent *ent;
64823       while ((ent=readdir(dir))!=0) {
64824         const char *const filename = ent->d_name;
64825         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
64826           const unsigned int lf = (unsigned int)std::strlen(filename);
64827           CImg<char> full_filename(lp + lf + 2);
64828 
64829           if (!is_current) {
64830             full_filename.assign(lp + lf + 2);
64831             if (lp) std::memcpy(full_filename,_path,lp);
64832             full_filename[lp] = '/';
64833             std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
64834           } else full_filename.assign(filename,lf + 1);
64835 
64836           struct stat st;
64837           if (stat(full_filename,&st)==-1) continue;
64838           const bool is_directory = (st.st_mode & S_IFDIR)!=0;
64839           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
64840             if (include_path) {
64841               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
64842                 full_filename.move_to(res);
64843             } else {
64844               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
64845                 CImg<char>(filename,lf + 1).move_to(res);
64846             }
64847           }
64848         }
64849       }
64850       closedir(dir);
64851 #endif
64852 
64853       // Sort resulting list by lexicographic order.
64854       if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
64855 
64856       return res;
64857     }
64858 
64859     //! Try to guess format from an image file.
64860     /**
64861        \param file Input file (can be \c 0 if \c filename is set).
64862        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
64863        \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
64864     **/
64865     inline const char *ftype(std::FILE *const file, const char *const filename) {
64866       if (!file && !filename)
64867         throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
64868       static const char
64869         *const _pnm = "pnm",
64870         *const _pfm = "pfm",
64871         *const _bmp = "bmp",
64872         *const _gif = "gif",
64873         *const _jpg = "jpg",
64874         *const _off = "off",
64875         *const _pan = "pan",
64876         *const _png = "png",
64877         *const _tif = "tif",
64878         *const _inr = "inr",
64879         *const _dcm = "dcm";
64880       const char *f_type = 0;
64881       CImg<char> header;
64882       const unsigned int omode = cimg::exception_mode();
64883       cimg::exception_mode(0);
64884       try {
64885         header._load_raw(file,filename,512,1,1,1,false,false,0);
64886         const unsigned char *const uheader = (unsigned char*)header._data;
64887         if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF
64888         else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE
64889           f_type = _inr;
64890         else if (!std::strncmp(header,"PANDORE",7)) // PANDORE
64891           f_type = _pan;
64892         else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM
64893           f_type = _dcm;
64894         else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG
64895           f_type = _jpg;
64896         else if (header[0]=='B' && header[1]=='M') // BMP
64897           f_type = _bmp;
64898         else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' &&
64899                  (header[4]=='7' || header[4]=='9')) // GIF
64900           f_type = _gif;
64901         else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&
64902                  uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG
64903           f_type = _png;
64904         else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF
64905           f_type = _tif;
64906         else { // PNM or PFM
64907           CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
64908           cimglist_for(_header,l) {
64909             if (_header(l,0)=='#') continue;
64910             if (_header[l]._width==2 && _header(l,0)=='P') {
64911               const char c = _header(l,1);
64912               if (c=='f' || c=='F') { f_type = _pfm; break; }
64913               if (c>='1' && c<='9') { f_type = _pnm; break; }
64914             }
64915             f_type = 0; break;
64916           }
64917         }
64918       } catch (CImgIOException&) { }
64919       cimg::exception_mode(omode);
64920       return f_type;
64921     }
64922 
64923     //! Load file from network as a local temporary file.
64924     /**
64925        \param url URL of the filename, as a C-string.
64926        \param[out] filename_local C-string containing the path to a local copy of \c filename.
64927        \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
64928        \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
64929        \param referer Referer used, as a C-string.
64930        \return Value of \c filename_local.
64931        \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
64932     **/
64933     inline char *load_network(const char *const url, char *const filename_local,
64934                               const unsigned int timeout, const bool try_fallback,
64935                               const char *const referer) {
64936       if (!url)
64937         throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
64938       if (!filename_local)
64939         throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
64940       if (!network_mode())
64941         throw CImgIOException("cimg::load_network(): Loading files from network is disabled.");
64942 
64943       const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
64944       CImg<char> ext = CImg<char>::string(_ext);
64945       std::FILE *file = 0;
64946       *filename_local = 0;
64947       if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
64948       else cimg::strwindows_reserved(ext);
64949       do {
64950         cimg_snprintf(filename_local,256,"%s%c%s%s",
64951                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
64952         if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
64953       } while (file);
64954 
64955 #ifdef cimg_use_curl
64956       const unsigned int omode = cimg::exception_mode();
64957       cimg::exception_mode(0);
64958       try {
64959         CURL *curl = 0;
64960         CURLcode res;
64961         curl = curl_easy_init();
64962         if (curl) {
64963           file = cimg::fopen(filename_local,"wb");
64964           curl_easy_setopt(curl,CURLOPT_URL,url);
64965           curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
64966           curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
64967           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
64968           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
64969           curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
64970           if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
64971           if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
64972           if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
64973           res = curl_easy_perform(curl);
64974           curl_easy_cleanup(curl);
64975           cimg::fseek(file,0,SEEK_END); // Check if file size is 0
64976           const cimg_ulong siz = cimg::ftell(file);
64977           cimg::fclose(file);
64978           if (siz>0 && res==CURLE_OK) {
64979             cimg::exception_mode(omode);
64980             return filename_local;
64981           } else std::remove(filename_local);
64982         }
64983       } catch (...) { }
64984       cimg::exception_mode(omode);
64985       if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
64986 #endif
64987 
64988       CImg<char> command((unsigned int)std::strlen(url) + 64);
64989       cimg::unused(try_fallback);
64990 
64991       // Try with 'curl' first.
64992       if (timeout) {
64993         if (referer)
64994           cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
64995                         cimg::curl_path(),referer,timeout,filename_local,
64996                         CImg<char>::string(url)._system_strescape().data());
64997         else
64998           cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"",
64999                         cimg::curl_path(),timeout,filename_local,
65000                         CImg<char>::string(url)._system_strescape().data());
65001       } else {
65002         if (referer)
65003           cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"",
65004                         cimg::curl_path(),referer,filename_local,
65005                         CImg<char>::string(url)._system_strescape().data());
65006         else
65007           cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"",
65008                         cimg::curl_path(),filename_local,
65009                         CImg<char>::string(url)._system_strescape().data());
65010       }
65011       cimg::system(command, cimg::curl_path());
65012 
65013       if (!(file=cimg::std_fopen(filename_local,"rb"))) {
65014 
65015         // Try with 'wget' otherwise.
65016         if (timeout) {
65017           if (referer)
65018             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65019                           cimg::wget_path(),referer,timeout,filename_local,
65020                           CImg<char>::string(url)._system_strescape().data());
65021           else
65022             cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65023                           cimg::wget_path(),timeout,filename_local,
65024                           CImg<char>::string(url)._system_strescape().data());
65025         } else {
65026           if (referer)
65027             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65028                           cimg::wget_path(),referer,filename_local,
65029                           CImg<char>::string(url)._system_strescape().data());
65030           else
65031             cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65032                           cimg::wget_path(),filename_local,
65033                           CImg<char>::string(url)._system_strescape().data());
65034         }
65035         cimg::system(command, cimg::wget_path());
65036 
65037         if (!(file=cimg::std_fopen(filename_local,"rb")))
65038           throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
65039                                 "'wget' or 'curl'.",url);
65040         cimg::fclose(file);
65041 
65042         // Try gunzip it.
65043         cimg_snprintf(command,command._width,"%s.gz",filename_local);
65044         std::rename(filename_local,command);
65045         cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"",
65046                       gunzip_path(),filename_local);
65047         cimg::system(command, gunzip_path());
65048         file = cimg::std_fopen(filename_local,"rb");
65049         if (!file) {
65050           cimg_snprintf(command,command._width,"%s.gz",filename_local);
65051           std::rename(command,filename_local);
65052           file = cimg::std_fopen(filename_local,"rb");
65053         }
65054       }
65055       cimg::fseek(file,0,SEEK_END); // Check if file size is 0
65056       if (std::ftell(file)<=0)
65057         throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
65058                               "'wget' or 'curl'.",url);
65059       cimg::fclose(file);
65060       return filename_local;
65061     }
65062 
65063     // Implement a tic/toc mechanism to display elapsed time of algorithms.
65064     inline cimg_uint64 tictoc(const bool is_tic) {
65065       cimg::mutex(2);
65066       static CImg<cimg_uint64> times(64);
65067       static unsigned int pos = 0;
65068       const cimg_uint64 t1 = cimg::time();
65069       if (is_tic) {
65070         // Tic
65071         times[pos++] = t1;
65072         if (pos>=times._width)
65073           throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
65074         cimg::mutex(2,0);
65075         return t1;
65076       }
65077 
65078       // Toc
65079       if (!pos)
65080         throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
65081       const cimg_uint64
65082         t0 = times[--pos],
65083         dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
65084       const unsigned int
65085         edays = (unsigned int)(dt/86400000.),
65086         ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
65087         emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
65088         esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
65089         ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
65090       if (!edays && !ehours && !emin && !esec)
65091         std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
65092                      cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
65093       else {
65094         if (!edays && !ehours && !emin)
65095           std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
65096                        cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
65097         else {
65098           if (!edays && !ehours)
65099             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
65100                          cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
65101           else{
65102             if (!edays)
65103               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
65104                            cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
65105             else{
65106               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
65107                            cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
65108             }
65109           }
65110         }
65111       }
65112       cimg::mutex(2,0);
65113       return dt;
65114     }
65115 
65116     // Return a temporary string describing the size of a memory buffer.
65117     inline const char *strbuffersize(const cimg_ulong size) {
65118       static CImg<char> res(256);
65119       cimg::mutex(5);
65120       if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
65121       else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
65122       else if (size<1024*1024*1024LU) {
65123         const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
65124       } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
65125       cimg::mutex(5,0);
65126       return res;
65127     }
65128 
65129     //! Display a simple dialog box, and wait for the user's response.
65130     /**
65131        \param title Title of the dialog window.
65132        \param msg Main message displayed inside the dialog window.
65133        \param button1_label Label of the 1st button.
65134        \param button2_label Label of the 2nd button (\c 0 to hide button).
65135        \param button3_label Label of the 3rd button (\c 0 to hide button).
65136        \param button4_label Label of the 4th button (\c 0 to hide button).
65137        \param button5_label Label of the 5th button (\c 0 to hide button).
65138        \param button6_label Label of the 6th button (\c 0 to hide button).
65139        \param logo Image logo displayed at the left of the main message.
65140        \param is_centered Tells if the dialog window must be centered on the screen.
65141        \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
65142        \note
65143        - Up to 6 buttons can be defined in the dialog window.
65144        - The function returns when a user clicked one of the button or closed the dialog window.
65145        - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box.
65146        At least one button must be specified.
65147     **/
65148     template<typename t>
65149     inline int dialog(const char *const title, const char *const msg,
65150                       const char *const button1_label, const char *const button2_label,
65151                       const char *const button3_label, const char *const button4_label,
65152                       const char *const button5_label, const char *const button6_label,
65153                       const CImg<t>& logo, const bool is_centered=false) {
65154 #if cimg_display==0
65155       cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
65156                    logo._data,is_centered);
65157       throw CImgIOException("cimg::dialog(): No display available.");
65158 #else
65159       static const unsigned char
65160         black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
65161 
65162       // Create buttons and canvas graphics
65163       CImgList<unsigned char> buttons, cbuttons, sbuttons;
65164       if (button1_label) {
65165         CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
65166         if (button2_label) {
65167           CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
65168           if (button3_label) {
65169             CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
65170             if (button4_label) {
65171               CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
65172               if (button5_label) {
65173                 CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
65174                 if (button6_label) {
65175                   CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
65176                 }}}}}}
65177       if (!buttons._width)
65178         throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
65179       cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
65180 
65181       unsigned int bw = 0, bh = 0;
65182       cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
65183       bw+=8; bh+=8;
65184       if (bw<64) bw = 64;
65185       if (bw>128) bw = 128;
65186       if (bh<24) bh = 24;
65187       if (bh>48) bh = 48;
65188 
65189       CImg<unsigned char> button(bw,bh,1,3);
65190       button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
65191       button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
65192       button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
65193       button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
65194       CImg<unsigned char> sbutton(bw,bh,1,3);
65195       sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
65196       sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
65197       sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
65198       sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
65199       sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
65200       sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
65201       sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
65202         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
65203       sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
65204         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
65205       CImg<unsigned char> cbutton(bw,bh,1,3);
65206       cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
65207         draw_rectangle(2,2,bw - 3,bh - 3,gray);
65208       cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
65209         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
65210       cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
65211         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
65212 
65213       cimglist_for(buttons,ll) {
65214         CImg<unsigned char>(cbutton).
65215           draw_image(1 + (bw  -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
65216           move_to(cbuttons);
65217         CImg<unsigned char>(sbutton).
65218           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
65219           move_to(sbuttons);
65220         CImg<unsigned char>(button).
65221           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
65222           move_to(buttons[ll]);
65223       }
65224 
65225       CImg<unsigned char> canvas;
65226       if (msg)
65227         ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
65228 
65229       const unsigned int
65230         bwall = (buttons._width - 1)*(12 + bw) + bw,
65231         w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
65232         h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
65233         lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
65234         ly = (h - 12 - bh - logo._height)/2,
65235         tx = lx + logo._width + 12,
65236         ty = (h - 12 - bh - canvas._height)/2,
65237         bx = (w - bwall)/2,
65238         by = h - 12 - bh;
65239 
65240       if (canvas._data)
65241         canvas = CImg<unsigned char>(w,h,1,3).
65242           draw_rectangle(0,0,w - 1,h - 1,gray).
65243           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
65244           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
65245           draw_image(tx,ty,canvas);
65246       else
65247         canvas = CImg<unsigned char>(w,h,1,3).
65248           draw_rectangle(0,0,w - 1,h - 1,gray).
65249           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
65250           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
65251       if (logo._data) canvas.draw_image(lx,ly,logo);
65252 
65253       unsigned int xbuttons[6] = { 0 };
65254       cimglist_for(buttons,lll) {
65255         xbuttons[lll] = bx + (bw + 12)*lll;
65256         canvas.draw_image(xbuttons[lll],by,buttons[lll]);
65257       }
65258 
65259       // Open window and enter events loop
65260       CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
65261       if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
65262                                  (CImgDisplay::screen_height() - disp.height())/2);
65263       bool stop_flag = false, refresh = false;
65264       int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
65265       while (!disp.is_closed() && !stop_flag) {
65266         if (refresh) {
65267           if (clicked>=0)
65268             CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
65269           else {
65270             if (selected>=0)
65271               CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
65272             else canvas.display(disp);
65273           }
65274           refresh = false;
65275         }
65276         disp.wait(15);
65277         if (disp.is_resized()) disp.resize(disp,false);
65278 
65279         if (disp.button()&1)  {
65280           oclicked = clicked;
65281           clicked = -1;
65282           cimglist_for(buttons,l)
65283             if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
65284                 disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
65285               clicked = selected = l;
65286               refresh = true;
65287             }
65288           if (clicked!=oclicked) refresh = true;
65289         } else if (clicked>=0) stop_flag = true;
65290 
65291         if (disp.key()) {
65292           oselected = selected;
65293           switch (disp.key()) {
65294           case cimg::keyESC : selected = -1; stop_flag = true; break;
65295           case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
65296           case cimg::keyTAB :
65297           case cimg::keyARROWRIGHT :
65298           case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
65299           case cimg::keyARROWLEFT :
65300           case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
65301           }
65302           disp.set_key();
65303           if (selected!=oselected) refresh = true;
65304         }
65305       }
65306       if (!disp) selected = -1;
65307       return selected;
65308 #endif
65309     }
65310 
65311     //! Display a simple dialog box, and wait for the user's response \specialization.
65312     inline int dialog(const char *const title, const char *const msg,
65313                       const char *const button1_label, const char *const button2_label,
65314                       const char *const button3_label, const char *const button4_label,
65315                       const char *const button5_label, const char *const button6_label,
65316                       const bool is_centered) {
65317       return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
65318                     CImg<unsigned char>::_logo40x38(),is_centered);
65319     }
65320 
65321     //! Evaluate math expression.
65322     /**
65323        \param expression C-string describing the formula to evaluate.
65324        \param x Value of the pre-defined variable \c x.
65325        \param y Value of the pre-defined variable \c y.
65326        \param z Value of the pre-defined variable \c z.
65327        \param c Value of the pre-defined variable \c c.
65328        \return Result of the formula evaluation.
65329        \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
65330        \par Example
65331        \code
65332        const double
65333        res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2),  // will return '1'
65334        res2 = cimg::eval(0,1,1);                    // will return '1' too
65335        \endcode
65336     **/
65337     inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
65338       static const CImg<float> empty;
65339       return empty.eval(expression,x,y,z,c);
65340     }
65341 
65342     template<typename t>
65343     inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
65344       static const CImg<float> empty;
65345       return empty.eval(expression,xyzc);
65346     }
65347 
65348   } // namespace cimg { ...
65349 } // namespace cimg_library { ...
65350 
65351 //! Short alias name.
65352 namespace cil = cimg_library_suffixed;
65353 
65354 #ifdef _cimg_redefine_False
65355 #define False 0
65356 #endif
65357 #ifdef _cimg_redefine_True
65358 #define True 1
65359 #endif
65360 #ifdef _cimg_redefine_Status
65361 #define Status int
65362 #endif
65363 #ifdef _cimg_redefine_Success
65364 #define Success 0
65365 #endif
65366 #ifdef _cimg_redefine_min
65367 #define min(a,b) (((a)<(b))?(a):(b))
65368 #endif
65369 #ifdef _cimg_redefine_max
65370 #define max(a,b) (((a)>(b))?(a):(b))
65371 #endif
65372 #ifdef _cimg_redefine_PI
65373 #define PI 3.141592653589793238462643383
65374 #endif
65375 #ifdef _MSC_VER
65376 #pragma warning(pop)
65377 #endif
65378 
65379 #endif
65380 
65381 // Local Variables:
65382 // mode: c++
65383 // End:
65384