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 300
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 #if defined(__FreeBSD__) || defined(__DragonFly__)
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,(longT)*ptr & (longT)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)((longT)*ptrd & (longT)*(ptrs++));
13762         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd & (longT)*(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,(longT)*ptr | (longT)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)((longT)*ptrd | (longT)*(ptrs++));
13828         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd | (longT)*(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,(longT)*ptr ^ (longT)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)((longT)*ptrd ^ (longT)*(ptrs++));
13900         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd ^ (longT)*(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_scalar(arg) (memtype[arg]<2) // Is scalar value?
16850 #define _cimg_mp_is_const_scalar(arg) (memtype[arg]==1) // Is const scalar?
16851 #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
16852 #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
16853 #define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)?
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_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char)
16859 #define _cimg_mp_check_const_index(arg) check_const_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_const_scalar(val) _cimg_mp_return(const_scalar((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_const_scalar(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'.",
17048                                       pixel_type(),_cimg_mp_calling_function,
17049                                       (ss - 4)>expr._data?ss - 4:expr._data);
17050         }
17051         char c1, c2;
17052 
17053         // Simplify expression when possible.
17054         do {
17055           c2 = 0;
17056           if (ss<se) {
17057             while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';'
17058             while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';'
17059           }
17060           while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses
17061             ++ss; --se; c2 = 1;
17062           }
17063           if (*ss=='_' && ss + 1<se && ss[1]=='(') { // Remove leading '_(something)' comment.
17064             const unsigned int clevel = level[ss - expr._data];
17065             ss+=2;
17066             while (ss<se && (*ss!=')' || level[ss - expr._data]!=clevel)) ++ss;
17067             if (ss<se) ++ss;
17068             if (ss>=se) return _cimg_mp_slot_nan;
17069             c2 = 1;
17070           }
17071         } while (c2 && ss<se);
17072 
17073         if (se<=ss || !*ss) {
17074           cimg::strellipsize(expr,64);
17075           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17076                                       "CImg<%s>::%s: %s%s Missing %s, in expression '%s'.",
17077                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
17078                                       *s_op=='F'?"argument":"item",
17079                                       ss_op);
17080         }
17081 
17082         static const size_t siz_ref = 7*sizeof(unsigned int);
17083         const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
17084         const unsigned int depth1 = depth + 1;
17085         unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
17086         char
17087           *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
17088           *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
17089           *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
17090           *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
17091         double val = 0, val1, val2;
17092         mp_func op;
17093         return_new_comp = false;
17094 
17095         // Bits of 'bloc_flags' tell about in which code bloc we currently are:
17096         // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t().
17097         const bool is_inside_critical = (bool)(bloc_flags&1);
17098 
17099         // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
17100         // linked to the returned memory slot (reference that cannot be determined at compile time).
17101         // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
17102         //                   3 = image value (coordinates) | 4 = image value as a vector (offsets) |
17103         //                   5 = image value as a vector (coordinates) }.
17104         // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
17105         // When p_ref[0]==0, p_ref is actually unlinked.
17106         // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
17107         // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
17108         // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
17109         // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
17110         // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
17111         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; }
17112 
17113         const char saved_char = *se; *se = 0;
17114         const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
17115         bool is_sth, is_relative;
17116         CImg<uintT> ref;
17117         CImg<charT> variable_name;
17118         CImgList<ulongT> l_opcode;
17119 
17120         // Look for a single value or a pre-defined variable.
17121         int nb = 0;
17122         s = ss + (*ss=='+' || *ss=='-'?1:0);
17123         if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
17124           is_sth = *ss=='-';
17125           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
17126           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
17127           if (nb==1 && is_sth) val = -val;
17128         } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number
17129           is_sth = *ss=='-';
17130           if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) {
17131             nb = 1;
17132             val = (double)arg1;
17133             if (is_sth) val = -val;
17134           }
17135         }
17136         if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
17137         if (nb==1) _cimg_mp_const_scalar(val);
17138         if (nb==2 && sep=='%') _cimg_mp_const_scalar(val/100);
17139 
17140         if (ss1==se) switch (*ss) { // One-char reserved variable
17141           case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c);
17142           case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20);
17143           case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27);
17144           case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19);
17145           case 'k' :
17146             if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']);
17147             pos = get_mem_img_index();
17148             if (pos!=~0U) _cimg_mp_return(pos);
17149             _cimg_mp_return_nan();
17150           case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26);
17151           case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22);
17152           case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21);
17153           case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t);
17154           case 'n' :
17155             if (reserved_label[(int)'n']!=~0U) _cimg_mp_return(reserved_label[(int)'n']);
17156 #if cimg_use_openmp!=0
17157             _cimg_mp_const_scalar((double)omp_get_max_threads());
17158 #else
17159             _cimg_mp_return(1);
17160 #endif
17161           case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18);
17162           case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x);
17163           case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y);
17164           case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z);
17165           case 'u' :
17166             if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']);
17167             _cimg_mp_scalar2(mp_u,0,1);
17168           case 'g' :
17169             if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']);
17170             _cimg_mp_scalar0(mp_g);
17171           case 'i' :
17172             if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']);
17173             _cimg_mp_scalar0(mp_i);
17174           case 'I' :
17175             _cimg_mp_op("Variable 'I'");
17176             if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']);
17177             if (!imgin._spectrum) _cimg_mp_return(0);
17178             need_input_copy = true;
17179             pos = vector(imgin._spectrum);
17180             CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
17181             return_new_comp = true;
17182             _cimg_mp_return(pos);
17183           case 'R' :
17184             if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']);
17185             need_input_copy = true;
17186             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
17187           case 'G' :
17188             if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']);
17189             need_input_copy = true;
17190             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
17191           case 'B' :
17192             if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']);
17193             need_input_copy = true;
17194             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
17195           case 'A' :
17196             if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']);
17197             need_input_copy = true;
17198             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
17199           }
17200         else if (ss2==se) { // Two-chars reserved variable
17201           arg1 = arg2 = ~0U;
17202           if (*ss=='w' && *ss1=='h') // wh
17203             _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
17204           if (*ss=='p' && *ss1=='i') // pi
17205             _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
17206           if (*ss=='i') {
17207             if (*ss1>='0' && *ss1<='9') { // i0...i9
17208               pos = 20 + *ss1 - '0';
17209               if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
17210               need_input_copy = true;
17211               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0);
17212             }
17213             switch (*ss1) {
17214             case 'm' : arg1 = 4; arg2 = 0; break; // im
17215             case 'M' : arg1 = 5; arg2 = 1; break; // iM
17216             case 'a' : arg1 = 6; arg2 = 2; break; // ia
17217             case 'v' : arg1 = 7; arg2 = 3; break; // iv
17218             case 's' : arg1 = 8; arg2 = 12; break; // is
17219             case 'p' : arg1 = 9; arg2 = 13; break; // ip
17220             case 'c' : // ic
17221               if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
17222               if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0;
17223               _cimg_mp_return(mem_img_median);
17224               break;
17225             case 'n' : // in
17226               if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
17227               if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude()):0;
17228               _cimg_mp_return(mem_img_norm);
17229             }
17230           }
17231           else if (*ss1=='m') switch (*ss) {
17232             case 'x' : arg1 = 12; arg2 = 4; break; // xm
17233             case 'y' : arg1 = 13; arg2 = 5; break; // ym
17234             case 'z' : arg1 = 14; arg2 = 6; break; // zm
17235             case 'c' : arg1 = 15; arg2 = 7; break; // cm
17236             }
17237           else if (*ss1=='M') switch (*ss) {
17238             case 'x' : arg1 = 16; arg2 = 8; break; // xM
17239             case 'y' : arg1 = 17; arg2 = 9; break; // yM
17240             case 'z' : arg1 = 18; arg2 = 10; break; // zM
17241             case 'c' : arg1 = 19; arg2 = 11; break; // cM
17242             }
17243           if (arg1!=~0U) {
17244             if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
17245             if (!img_stats) {
17246               img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
17247               mem_img_stats.assign(1,14,1,1,~0U);
17248             }
17249             if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = const_scalar(img_stats[arg2]);
17250             _cimg_mp_return(mem_img_stats[arg2]);
17251           }
17252         } else if (ss3==se) { // Three-chars reserved variable
17253           if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
17254             _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
17255         } else if (ss4==se) { // Four-chars reserved variable
17256           if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
17257             _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
17258         }
17259 
17260         pos = ~0U;
17261         is_sth = false;
17262 
17263         for (s0 = ss, s = ss1; s<se1; ++s)
17264           if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
17265             is_end_code = false;
17266             arg1 = compile(s0,s++,depth,0,bloc_flags);
17267             if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17268             is_sth = true;
17269             while (*s && (cimg::is_blank(*s) || *s==';')) ++s;
17270             s0 = s;
17271           }
17272         if (is_sth) {
17273           is_end_code = false;
17274           arg1 = compile(s0,se,depth,p_ref,bloc_flags);
17275           if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17276           _cimg_mp_return(pos!=~0U?pos:_cimg_mp_slot_nan);
17277         }
17278 
17279         // Declare / assign variable, vector value or image value.
17280         for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
17281           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
17282               *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
17283               *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
17284               level[s - expr._data]==clevel) {
17285             variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
17286             cimg::strpare(variable_name,false,true);
17287             const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
17288             char *const ve1 = ss + l_variable_name - 1;
17289             _cimg_mp_op("Operator '='");
17290 
17291             // Assign image value (direct).
17292             if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
17293                 (reserved_label[(int)*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[(int)*ss]))) {
17294               is_relative = *ss=='j' || *ss=='J';
17295 
17296               if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
17297                 if (!is_inside_critical) is_parallelizable = false;
17298                 if (*ss2=='#') { // Index specified
17299                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17300                   p1 = compile(ss3,s0++,depth1,0,bloc_flags);
17301                   _cimg_mp_check_list(true);
17302                 } else { p1 = ~0U; s0 = ss2; }
17303                 arg1 = compile(s0,ve1,depth1,0,bloc_flags); // Offset
17304                 _cimg_mp_check_type(arg1,0,1,0);
17305                 arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17306                 _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,0);
17307                 if (_cimg_mp_is_vector(arg2)) {
17308                   if (p1!=~0U) {
17309                     _cimg_mp_check_const_index(p1);
17310                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17311                     p2 = listin[p3]._spectrum;
17312                   } else p2 = imgin._spectrum;
17313                   if (!p2) _cimg_mp_return(0);
17314                   _cimg_mp_check_type(arg2,2,2,p2);
17315                 } else p2 = 0;
17316 
17317                 if (p_ref) {
17318                   *p_ref = _cimg_mp_is_vector(arg2)?4:2;
17319                   p_ref[1] = p1;
17320                   p_ref[2] = (unsigned int)is_relative;
17321                   p_ref[3] = arg1;
17322                   if (_cimg_mp_is_vector(arg2))
17323                     set_reserved_vector(arg2); // Prevent from being used in further optimization
17324                   else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17325                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
17326                 }
17327 
17328                 if (p1!=~0U) {
17329                   if (!listout) _cimg_mp_return(arg2);
17330                   if (*ss>='i')
17331                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17332                                         arg2,p1,arg1).move_to(code);
17333                   else if (_cimg_mp_is_scalar(arg2))
17334                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17335                                         arg2,p1,arg1).move_to(code);
17336                   else
17337                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17338                                         arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
17339                 } else {
17340                   if (!imgout) _cimg_mp_return(arg2);
17341                   if (*ss>='i')
17342                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17343                                         arg2,arg1).move_to(code);
17344                   else if (_cimg_mp_is_scalar(arg2))
17345                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17346                                         arg2,arg1).move_to(code);
17347                   else
17348                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17349                                         arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
17350                 }
17351                 _cimg_mp_return(arg2);
17352               }
17353 
17354               if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
17355                 if (!is_inside_critical) is_parallelizable = false;
17356                 if (*ss2=='#') { // Index specified
17357                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17358                   p1 = compile(ss3,s0++,depth1,0,bloc_flags);
17359                   _cimg_mp_check_list(true);
17360                 } else { p1 = ~0U; s0 = ss2; }
17361                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17362                 arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17363                 arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17364                 arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17365                 arg5 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17366                 _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,0);
17367                 if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
17368                   s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17369                   arg1 = compile(s0,s1,depth1,0,bloc_flags);
17370                   if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17371                     p2 = _cimg_mp_size(arg1); // Vector size
17372                     ++arg1;
17373                     if (p2>1) {
17374                       arg2 = arg1 + 1;
17375                       if (p2>2) {
17376                         arg3 = arg2 + 1;
17377                         if (p2>3) arg4 = arg3 + 1;
17378                       }
17379                     }
17380                   } else if (s1<ve1) { // Y
17381                     s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17382                     arg2 = compile(s1,s2,depth1,0,bloc_flags);
17383                     if (s2<ve1) { // Z
17384                       s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17385                       arg3 = compile(s2,s3,depth1,0,bloc_flags);
17386                       if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,bloc_flags); // C
17387                     }
17388                   }
17389                 }
17390 
17391                 if (_cimg_mp_is_vector(arg5)) {
17392                   if (p1!=~0U) {
17393                     _cimg_mp_check_const_index(p1);
17394                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17395                     p2 = listin[p3]._spectrum;
17396                   } else p2 = imgin._spectrum;
17397                   if (!p2) _cimg_mp_return(0);
17398                   _cimg_mp_check_type(arg5,2,2,p2);
17399                 } else p2 = 0;
17400 
17401 
17402                 if (p_ref) {
17403                   *p_ref = _cimg_mp_is_vector(arg5)?5:3;
17404                   p_ref[1] = p1;
17405                   p_ref[2] = (unsigned int)is_relative;
17406                   p_ref[3] = arg1;
17407                   p_ref[4] = arg2;
17408                   p_ref[5] = arg3;
17409                   p_ref[6] = arg4;
17410                   if (_cimg_mp_is_vector(arg5))
17411                     set_reserved_vector(arg5); // Prevent from being used in further optimization
17412                   else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -1;
17413                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1;
17414                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
17415                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17416                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
17417                   if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
17418                 }
17419                 if (p1!=~0U) {
17420                   if (!listout) _cimg_mp_return(arg5);
17421                   if (*ss>='i')
17422                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17423                                         arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
17424                   else if (_cimg_mp_is_scalar(arg5))
17425                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17426                                         arg5,p1,arg1,arg2,arg3).move_to(code);
17427                   else
17428                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17429                                         arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17430                 } else {
17431                   if (!imgout) _cimg_mp_return(arg5);
17432                   if (*ss>='i')
17433                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17434                                         arg5,arg1,arg2,arg3,arg4).move_to(code);
17435                   else if (_cimg_mp_is_scalar(arg5))
17436                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17437                                         arg5,arg1,arg2,arg3).move_to(code);
17438                   else
17439                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17440                                         arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17441                 }
17442                 _cimg_mp_return(arg5);
17443               }
17444             }
17445 
17446             // Assign vector value (direct).
17447             if (l_variable_name>3 && *ve1==']' && *ss!='[') {
17448               s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
17449               if (s0>ss && is_varname(ss,s0 - ss)) {
17450                 variable_name[s0 - ss] = 0; // Remove brackets in variable name
17451                 get_variable_pos(variable_name,arg1,arg2);
17452                 arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot
17453                 if (arg1==~0U || _cimg_mp_is_scalar(arg1))
17454                   compile(ss,s0,depth1,0,bloc_flags); // Variable does not exist or is not a vector -> error
17455 
17456                 arg2 = compile(++s0,ve1,depth1,0,bloc_flags); // Index
17457                 arg3 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17458                 _cimg_mp_check_type(arg3,2,1,0);
17459 
17460                 if (_cimg_mp_is_const_scalar(arg2)) { // Constant index -> return corresponding variable slot directly
17461                   nb = (int)mem[arg2];
17462                   if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
17463                     arg1+=nb + 1;
17464                     CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17465                     _cimg_mp_return(arg1);
17466                   }
17467                   compile(ss,s,depth1,0,bloc_flags); // Out-of-bounds reference -> error
17468                 }
17469 
17470                 // Case of non-constant index -> return assigned value + linked reference
17471                 if (p_ref) {
17472                   *p_ref = 1;
17473                   p_ref[1] = arg1;
17474                   p_ref[2] = arg2;
17475                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization
17476                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
17477                 }
17478                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2).
17479                   move_to(code);
17480                 _cimg_mp_return(arg3);
17481               }
17482             }
17483 
17484             // Assign user-defined macro.
17485             if (l_variable_name>2 && *ve1==')' && *ss!='(') {
17486               s0 = ve1; while (s0>ss && *s0!='(') --s0;
17487               if (is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) &&
17488                   std::strncmp(variable_name,"print(",6)) { // Valid macro name
17489                 s0 = variable_name._data + (s0 - ss);
17490                 *s0 = 0;
17491                 s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
17492                 CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
17493                 ++s; while (*s && cimg::is_blank(*s)) ++s;
17494                 CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
17495 
17496                 p1 = 1; // Index of current parsed argument
17497                 for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
17498                   if (p1>24) {
17499                     _cimg_mp_strerr;
17500                     cimg::strellipsize(variable_name,64);
17501                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17502                                                 "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
17503                                                 "definition '%s()', in expression '%s'.",
17504                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17505                                                 variable_name._data,s0);
17506                   }
17507                   while (*s && cimg::is_blank(*s)) ++s;
17508                   if (*s==')' && p1==1) break; // Function has no arguments
17509 
17510                   s2 = s; // Start of the argument name
17511                   is_sth = true; // is_valid_argument_name?
17512                   if (*s>='0' && *s<='9') is_sth = false;
17513                   else for (ns = s; ns<s1 && *ns!=',' && !cimg::is_blank(*ns); ++ns)
17514                          if (!is_varchar(*ns)) { is_sth = false; break; }
17515                   s3 = ns; // End of the argument name
17516                   while (*ns && cimg::is_blank(*ns)) ++ns;
17517                   if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
17518                     _cimg_mp_strerr;
17519                     cimg::strellipsize(variable_name,64);
17520                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17521                                                 "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
17522                                                 "macro '%s()', in expression '%s'.",
17523                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17524                                                 is_sth?"Empty":"Invalid",p1,
17525                                                 variable_name._data,s0);
17526                   }
17527                   if (ns==s1 || *ns==',') { // New argument found
17528                     *s3 = 0;
17529                     p2 = (unsigned int)(s3 - s2); // Argument length
17530                     for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
17531                       if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
17532                             (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
17533                         if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
17534                           *(ps - 1) = (char)p1;
17535                           if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
17536                             std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
17537                             macro_body[0]._width-=p2 + 1;
17538                           } else { // Has pre number sign only
17539                             std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17540                             macro_body[0]._width-=p2;
17541                           }
17542                         } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
17543                           *(ps++) = (char)p1;
17544                           std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17545                           macro_body[0]._width-=p2;
17546                         } else { // Not near a number sign
17547                           if (p2<3) {
17548                             ps-=(ulongT)macro_body[0]._data;
17549                             macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
17550                             ps+=(ulongT)macro_body[0]._data;
17551                           } else macro_body[0]._width-=p2 - 3;
17552                           std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
17553                           *(ps++) = '(';
17554                           *(ps++) = (char)p1;
17555                           *(ps++) = ')';
17556                         }
17557                       } else ++ps;
17558                     }
17559                   }
17560                 }
17561 
17562                 // Store number of arguments.
17563                 macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
17564 
17565                 // Detect parts of function body inside a string.
17566                 is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
17567                 _cimg_mp_return_nan();
17568               }
17569             }
17570 
17571             // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
17572             const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
17573             s0 = variable_name._data;
17574             if (is_const) {
17575               s0+=6; while (cimg::is_blank(*s0)) ++s0;
17576               variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
17577             }
17578             if (is_varname(variable_name)) { // Valid variable name
17579 
17580               // Assign variable (direct).
17581               get_variable_pos(variable_name,arg1,arg2);
17582               arg3 = compile(s + 1,se,depth1,0,bloc_flags);
17583               is_sth = return_new_comp; // is arg3 a new blank object?
17584               if (is_const) _cimg_mp_check_const_scalar(arg3,2,0);
17585               arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
17586 
17587               if (arg1==~0U) { // Create new variable
17588                 if (_cimg_mp_is_vector(arg3)) { // Vector variable
17589                   arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3);
17590                   set_reserved_vector(arg1); // Prevent from being used in further optimization
17591                 } else { // Scalar variable
17592                   if (is_const) arg1 = arg3;
17593                   else {
17594                     arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3);
17595                     memtype[arg1] = -1;
17596                   }
17597                 }
17598 
17599                 if (arg2!=~0U) reserved_label[arg2] = arg1;
17600                   else {
17601                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
17602                     variable_pos[variable_def._width] = arg1;
17603                     variable_name.move_to(variable_def);
17604                   }
17605 
17606               } else { // Variable already exists -> assign a new value
17607                 if (is_const || _cimg_mp_is_const_scalar(arg1)) {
17608                   _cimg_mp_strerr;
17609                   cimg::strellipsize(variable_name,64);
17610                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
17611                                               "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
17612                                               "in expression '%s'.",
17613                                               pixel_type(),_cimg_mp_calling_function,s_op,
17614                                               _cimg_mp_is_const_scalar(arg1)?"":"non-",
17615                                               variable_name._data,
17616                                               !_cimg_mp_is_const_scalar(arg1) && is_const?" as a const variable":"",
17617                                               s0);
17618                 }
17619                 _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
17620                 if (_cimg_mp_is_vector(arg1)) { // Vector
17621                   if (_cimg_mp_is_vector(arg3)) // From vector
17622                     CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)).
17623                       move_to(code);
17624                   else // From scalar
17625                     CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3).
17626                       move_to(code);
17627                 } else // Scalar
17628                   CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17629               }
17630               return_new_comp = false;
17631               _cimg_mp_return(arg1);
17632             }
17633 
17634             // Assign lvalue (variable name was not valid for a direct assignment).
17635             arg1 = ~0U;
17636             is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
17637             if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
17638 
17639             if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
17640               ref.assign(7);
17641               arg1 = compile(ss,s,depth1,ref,bloc_flags); // Lvalue slot
17642               arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to assign
17643 
17644               if (*ref==1) { // Vector value (scalar): V[k] = scalar
17645                 _cimg_mp_check_type(arg2,2,1,0);
17646                 arg3 = ref[1]; // Vector slot
17647                 arg4 = ref[2]; // Index
17648                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17649                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17650                   move_to(code);
17651                 _cimg_mp_return(arg2);
17652               }
17653 
17654               if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
17655                 if (!is_inside_critical) is_parallelizable = false;
17656                 _cimg_mp_check_type(arg2,2,1,0);
17657                 p1 = ref[1]; // Index
17658                 is_relative = (bool)ref[2];
17659                 arg3 = ref[3]; // Offset
17660                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17661                 if (p1!=~0U) {
17662                   if (!listout) _cimg_mp_return(arg2);
17663                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17664                                       arg2,p1,arg3).move_to(code);
17665                 } else {
17666                   if (!imgout) _cimg_mp_return(arg2);
17667                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17668                                       arg2,arg3).move_to(code);
17669                 }
17670                 _cimg_mp_return(arg2);
17671               }
17672 
17673               if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
17674                 if (!is_inside_critical) is_parallelizable = false;
17675                 _cimg_mp_check_type(arg2,2,1,0);
17676                 p1 = ref[1]; // Index
17677                 is_relative = (bool)ref[2];
17678                 arg3 = ref[3]; // X
17679                 arg4 = ref[4]; // Y
17680                 arg5 = ref[5]; // Z
17681                 arg6 = ref[6]; // C
17682                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17683                 if (p1!=~0U) {
17684                   if (!listout) _cimg_mp_return(arg2);
17685                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17686                                       arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
17687                 } else {
17688                   if (!imgout) _cimg_mp_return(arg2);
17689                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17690                                       arg2,arg3,arg4,arg5,arg6).move_to(code);
17691                 }
17692                 _cimg_mp_return(arg2);
17693               }
17694 
17695               if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
17696                 if (!is_inside_critical) is_parallelizable = false;
17697                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17698                 p1 = ref[1]; // Index
17699                 is_relative = (bool)ref[2];
17700                 arg3 = ref[3]; // Offset
17701                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17702                 if (p1!=~0U) {
17703                   if (!listout) _cimg_mp_return(arg2);
17704                   if (_cimg_mp_is_scalar(arg2))
17705                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17706                                          arg2,p1,arg3).move_to(code);
17707                   else {
17708                     _cimg_mp_check_const_index(p1);
17709                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17710                                          arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
17711                   }
17712 
17713                 } else {
17714                   if (!imgout) _cimg_mp_return(arg2);
17715                   if (_cimg_mp_is_scalar(arg2))
17716                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17717                                         arg2,arg3).move_to(code);
17718                   else
17719                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17720                                         arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
17721                 }
17722                 _cimg_mp_return(arg2);
17723               }
17724 
17725               if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
17726                 if (!is_inside_critical) is_parallelizable = false;
17727                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17728                 p1 = ref[1]; // Index
17729                 is_relative = (bool)ref[2];
17730                 arg3 = ref[3]; // X
17731                 arg4 = ref[4]; // Y
17732                 arg5 = ref[5]; // Z
17733                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17734                 if (p1!=~0U) {
17735                   if (!listout) _cimg_mp_return(arg2);
17736                   if (_cimg_mp_is_scalar(arg2))
17737                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17738                                         arg2,p1,arg3,arg4,arg5).move_to(code);
17739                   else {
17740                     _cimg_mp_check_const_index(p1);
17741                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17742                                          arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17743                   }
17744 
17745                 } else {
17746                   if (!imgout) _cimg_mp_return(arg2);
17747                   if (_cimg_mp_is_scalar(arg2))
17748                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17749                                         arg2,arg3,arg4,arg5).move_to(code);
17750                   else
17751                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17752                                         arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17753                 }
17754                 _cimg_mp_return(arg2);
17755               }
17756 
17757               if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
17758                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17759                 if (_cimg_mp_is_vector(arg2)) // From vector
17760                   CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
17761                     move_to(code);
17762                 else // From scalar
17763                   CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
17764                     move_to(code);
17765                 _cimg_mp_return(arg1);
17766               }
17767 
17768               if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar
17769                 _cimg_mp_check_type(arg2,2,1,0);
17770                 CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
17771                 _cimg_mp_return(arg1);
17772               }
17773             }
17774 
17775             // No assignment expressions match -> error
17776             _cimg_mp_strerr;
17777             cimg::strellipsize(variable_name,64);
17778             throw CImgArgumentException("[" cimg_appname "_math_parser] "
17779                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17780                                         "in expression '%s'.",
17781                                         pixel_type(),_cimg_mp_calling_function,s_op,
17782                                         arg1!=~0U && _cimg_mp_is_const_scalar(arg1)?"const ":"",
17783                                         variable_name._data,s0);
17784           }
17785 
17786         // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
17787         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17788           if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
17789               level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
17790             _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
17791 
17792             ref.assign(7);
17793             arg1 = compile(ss,ns,depth1,ref,bloc_flags); // Vector slot
17794             arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Right operand
17795             _cimg_mp_check_type(arg1,1,2,2);
17796             _cimg_mp_check_type(arg2,2,3,2);
17797             if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
17798               if (*ps=='*')
17799                 CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
17800               else if (*ps=='/')
17801                 CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
17802               else
17803                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
17804             } else { // Complex **= scalar
17805               if (*ps=='*') {
17806                 if (arg2==1) _cimg_mp_return(arg1);
17807                 self_vector_s(arg1,mp_self_mul,arg2);
17808               } else if (*ps=='/') {
17809                 if (arg2==1) _cimg_mp_return(arg1);
17810                 self_vector_s(arg1,mp_self_div,arg2);
17811               } else {
17812                 if (arg2==1) _cimg_mp_return(arg1);
17813                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
17814               }
17815             }
17816 
17817             if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
17818               if (!is_inside_critical) is_parallelizable = false;
17819               p1 = ref[1]; // Index
17820               is_relative = (bool)ref[2];
17821               arg3 = ref[3]; // Offset
17822               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17823               if (p1!=~0U) {
17824                 if (!listout) _cimg_mp_return(arg1);
17825                 _cimg_mp_check_const_index(p1);
17826                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17827                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17828               } else {
17829                 if (!imgout) _cimg_mp_return(arg1);
17830                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17831                                      arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17832               }
17833 
17834             } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
17835               if (!is_inside_critical) is_parallelizable = false;
17836               p1 = ref[1]; // Index
17837               is_relative = (bool)ref[2];
17838               arg3 = ref[3]; // X
17839               arg4 = ref[4]; // Y
17840               arg5 = ref[5]; // Z
17841               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17842               if (p1!=~0U) {
17843                 if (!listout) _cimg_mp_return(arg1);
17844                 _cimg_mp_check_const_index(p1);
17845                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17846                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17847               } else {
17848                 if (!imgout) _cimg_mp_return(arg1);
17849                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17850                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17851               }
17852             }
17853 
17854             _cimg_mp_return(arg1);
17855           }
17856 
17857         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17858           if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || (*ps=='%' && s[1]!='=') ||
17859                           *ps=='&' || *ps=='^' || *ps=='|' ||
17860                           (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
17861               level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
17862             switch (*ps) {
17863             case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
17864             case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
17865             case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
17866             case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
17867             case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
17868             case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
17869             case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
17870             case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
17871             case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
17872             default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
17873             }
17874             s1 = *ps=='>' || *ps=='<'?ns:ps;
17875 
17876             ref.assign(7);
17877             arg1 = compile(ss,s1,depth1,ref,bloc_flags); // Variable slot
17878             arg2 = compile(s + 1,se,depth1,0,bloc_flags); // Value to apply
17879 
17880             // Check for particular case to be simplified.
17881             if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
17882             if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
17883 
17884             // Apply operator on a copy to prevent modifying a constant or a variable.
17885             if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
17886               if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
17887               else arg1 = scalar1(mp_copy,arg1);
17888             }
17889 
17890             if (*ref==1) { // Vector value (scalar): V[k] += scalar
17891               _cimg_mp_check_type(arg2,2,1,0);
17892               arg3 = ref[1]; // Vector slot
17893               arg4 = ref[2]; // Index
17894               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17895               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17896               CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17897                 move_to(code);
17898               _cimg_mp_return(arg1);
17899             }
17900 
17901             if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
17902               if (!is_inside_critical) is_parallelizable = false;
17903               _cimg_mp_check_type(arg2,2,1,0);
17904               p1 = ref[1]; // Index
17905               is_relative = (bool)ref[2];
17906               arg3 = ref[3]; // Offset
17907               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17908               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17909               if (p1!=~0U) {
17910                 if (!listout) _cimg_mp_return(arg1);
17911                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17912                                     arg1,p1,arg3).move_to(code);
17913               } else {
17914                 if (!imgout) _cimg_mp_return(arg1);
17915                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17916                                     arg1,arg3).move_to(code);
17917               }
17918               _cimg_mp_return(arg1);
17919             }
17920 
17921             if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
17922               if (!is_inside_critical) is_parallelizable = false;
17923               _cimg_mp_check_type(arg2,2,1,0);
17924               p1 = ref[1]; // Index
17925               is_relative = (bool)ref[2];
17926               arg3 = ref[3]; // X
17927               arg4 = ref[4]; // Y
17928               arg5 = ref[5]; // Z
17929               arg6 = ref[6]; // C
17930               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17931               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17932               if (p1!=~0U) {
17933                 if (!listout) _cimg_mp_return(arg1);
17934                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17935                                     arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
17936               } else {
17937                 if (!imgout) _cimg_mp_return(arg1);
17938                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17939                                     arg1,arg3,arg4,arg5,arg6).move_to(code);
17940               }
17941               _cimg_mp_return(arg1);
17942             }
17943 
17944             if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
17945               if (!is_inside_critical) is_parallelizable = false;
17946               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17947               p1 = ref[1]; // Index
17948               is_relative = (bool)ref[2];
17949               arg3 = ref[3]; // Offset
17950               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17951               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17952               if (p1!=~0U) {
17953                 if (!listout) _cimg_mp_return(arg1);
17954                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17955                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17956               } else {
17957                 if (!imgout) _cimg_mp_return(arg1);
17958                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17959                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17960               }
17961               _cimg_mp_return(arg1);
17962             }
17963 
17964             if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
17965               if (!is_inside_critical) is_parallelizable = false;
17966               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17967               p1 = ref[1]; // Index
17968               is_relative = (bool)ref[2];
17969               arg3 = ref[3]; // X
17970               arg4 = ref[4]; // Y
17971               arg5 = ref[5]; // Z
17972               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17973               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17974               if (p1!=~0U) {
17975                 if (!listout) _cimg_mp_return(arg1);
17976                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17977                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17978               } else {
17979                 if (!imgout) _cimg_mp_return(arg1);
17980                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17981                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17982               }
17983               _cimg_mp_return(arg1);
17984             }
17985 
17986             if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
17987               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17988               if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
17989               else self_vector_s(arg1,op,arg2); // Vector += scalar
17990               _cimg_mp_return(arg1);
17991             }
17992 
17993             if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar
17994               _cimg_mp_check_type(arg2,2,1,0);
17995               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17996               _cimg_mp_return(arg1);
17997             }
17998 
17999             variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
18000             cimg::strpare(variable_name,false,true);
18001             _cimg_mp_strerr;
18002             cimg::strellipsize(variable_name,64);
18003             throw CImgArgumentException("[" cimg_appname "_math_parser] "
18004                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
18005                                         "in expression '%s'.",
18006                                         pixel_type(),_cimg_mp_calling_function,s_op,
18007                                         _cimg_mp_is_const_scalar(arg1)?"const ":"",
18008                                         variable_name._data,s0);
18009           }
18010 
18011         for (s = ss1; s<se1; ++s)
18012           if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
18013             _cimg_mp_op("Operator '?:'");
18014             s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
18015             arg1 = compile(ss,s,depth1,0,bloc_flags);
18016             _cimg_mp_check_type(arg1,1,1,0);
18017             if (_cimg_mp_is_const_scalar(arg1)) {
18018               if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,bloc_flags);
18019               else return *s1!=':'?0:compile(++s1,se,depth1,0,bloc_flags);
18020             }
18021             p2 = code._width;
18022             arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,bloc_flags);
18023             p3 = code._width;
18024             arg3 = *s1==':'?compile(++s1,se,depth1,0,bloc_flags):
18025               _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
18026             _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
18027             arg4 = _cimg_mp_size(arg2);
18028             if (arg4) pos = vector(arg4); else pos = scalar();
18029             CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
18030                                 p3 - p2,code._width - p3,arg4).move_to(code,p2);
18031             return_new_comp = true;
18032             _cimg_mp_return(pos);
18033           }
18034 
18035         for (s = se3, ns = se2; s>ss; --s, --ns)
18036           if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
18037             _cimg_mp_op("Operator '||'");
18038             arg1 = compile(ss,s,depth1,0,bloc_flags);
18039             _cimg_mp_check_type(arg1,1,1,0);
18040             if (arg1>0 && arg1<=16) _cimg_mp_return(1);
18041             p2 = code._width;
18042             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18043             _cimg_mp_check_type(arg2,2,1,0);
18044             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18045               _cimg_mp_const_scalar(mem[arg1] || mem[arg2]);
18046             if (!arg1) _cimg_mp_return(arg2);
18047             pos = scalar();
18048             CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
18049               move_to(code,p2);
18050             return_new_comp = true;
18051             _cimg_mp_return(pos);
18052           }
18053 
18054         for (s = se3, ns = se2; s>ss; --s, --ns)
18055           if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
18056             _cimg_mp_op("Operator '&&'");
18057             arg1 = compile(ss,s,depth1,0,bloc_flags);
18058             _cimg_mp_check_type(arg1,1,1,0);
18059             if (!arg1) _cimg_mp_return(0);
18060             p2 = code._width;
18061             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18062             _cimg_mp_check_type(arg2,2,1,0);
18063             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18064               _cimg_mp_const_scalar(mem[arg1] && mem[arg2]);
18065             if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
18066             pos = scalar();
18067             CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
18068               move_to(code,p2);
18069             return_new_comp = true;
18070             _cimg_mp_return(pos);
18071           }
18072 
18073         for (s = se2; s>ss; --s)
18074           if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
18075             _cimg_mp_op("Operator '|'");
18076             arg1 = compile(ss,s,depth1,0,bloc_flags);
18077             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18078             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18079             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
18080             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18081               if (!arg2) _cimg_mp_return(arg1);
18082               _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
18083             }
18084             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18085               if (!arg1) _cimg_mp_return(arg2);
18086               _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
18087             }
18088             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18089               _cimg_mp_const_scalar((longT)mem[arg1] | (longT)mem[arg2]);
18090             if (!arg2) _cimg_mp_return(arg1);
18091             if (!arg1) _cimg_mp_return(arg2);
18092             _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
18093           }
18094 
18095         for (s = se2; s>ss; --s)
18096           if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
18097             _cimg_mp_op("Operator '&'");
18098             arg1 = compile(ss,s,depth1,0,bloc_flags);
18099             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18100             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18101             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
18102             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
18103             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
18104             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18105               _cimg_mp_const_scalar((longT)mem[arg1] & (longT)mem[arg2]);
18106             if (!arg1 || !arg2) _cimg_mp_return(0);
18107             _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
18108           }
18109 
18110         for (s = se3, ns = se2; s>ss; --s, --ns)
18111           if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
18112             _cimg_mp_op("Operator '!='");
18113             arg1 = compile(ss,s,depth1,0,bloc_flags);
18114             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18115             if (arg1==arg2) _cimg_mp_return(0);
18116             p1 = _cimg_mp_size(arg1);
18117             p2 = _cimg_mp_size(arg2);
18118             if (p1 || p2) {
18119               if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
18120               pos = scalar();
18121               CImg<ulongT>::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18122               return_new_comp = true;
18123               _cimg_mp_return(pos);
18124             }
18125             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18126               _cimg_mp_const_scalar(mem[arg1]!=mem[arg2]);
18127             _cimg_mp_scalar2(mp_neq,arg1,arg2);
18128           }
18129 
18130         for (s = se3, ns = se2; s>ss; --s, --ns)
18131           if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
18132             _cimg_mp_op("Operator '=='");
18133             arg1 = compile(ss,s,depth1,0,bloc_flags);
18134             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18135             if (arg1==arg2) _cimg_mp_return(1);
18136             p1 = _cimg_mp_size(arg1);
18137             p2 = _cimg_mp_size(arg2);
18138             if (p1 || p2) {
18139               if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
18140               pos = scalar();
18141               CImg<ulongT>::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18142               return_new_comp = true;
18143               _cimg_mp_return(pos);
18144             }
18145             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18146               _cimg_mp_const_scalar(mem[arg1]==mem[arg2]);
18147             _cimg_mp_scalar2(mp_eq,arg1,arg2);
18148           }
18149 
18150         for (s = se3, ns = se2; s>ss; --s, --ns)
18151           if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
18152             _cimg_mp_op("Operator '<='");
18153             arg1 = compile(ss,s,depth1,0,bloc_flags);
18154             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18155             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18156             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
18157             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
18158             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
18159             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18160               _cimg_mp_const_scalar(mem[arg1]<=mem[arg2]);
18161             if (arg1==arg2) _cimg_mp_return(1);
18162             _cimg_mp_scalar2(mp_lte,arg1,arg2);
18163           }
18164 
18165         for (s = se3, ns = se2; s>ss; --s, --ns)
18166           if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
18167             _cimg_mp_op("Operator '>='");
18168             arg1 = compile(ss,s,depth1,0,bloc_flags);
18169             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18170             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18171             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
18172             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
18173             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
18174             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18175               _cimg_mp_const_scalar(mem[arg1]>=mem[arg2]);
18176             if (arg1==arg2) _cimg_mp_return(1);
18177             _cimg_mp_scalar2(mp_gte,arg1,arg2);
18178           }
18179 
18180         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18181           if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
18182             _cimg_mp_op("Operator '<'");
18183             arg1 = compile(ss,s,depth1,0,bloc_flags);
18184             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18185             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18186             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
18187             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
18188             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
18189             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18190               _cimg_mp_const_scalar(mem[arg1]<mem[arg2]);
18191             if (arg1==arg2) _cimg_mp_return(0);
18192             _cimg_mp_scalar2(mp_lt,arg1,arg2);
18193           }
18194 
18195         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18196           if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>')
18197             _cimg_mp_op("Operator '>'");
18198             arg1 = compile(ss,s,depth1,0,bloc_flags);
18199             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18200             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18201             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
18202             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
18203             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
18204             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18205               _cimg_mp_const_scalar(mem[arg1]>mem[arg2]);
18206             if (arg1==arg2) _cimg_mp_return(0);
18207             _cimg_mp_scalar2(mp_gt,arg1,arg2);
18208           }
18209 
18210         for (s = se3, ns = se2; s>ss; --s, --ns)
18211           if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
18212             _cimg_mp_op("Operator '<<'");
18213             arg1 = compile(ss,s,depth1,0,bloc_flags);
18214             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18215             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18216             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18217               _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
18218             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18219               if (!arg2) _cimg_mp_return(arg1);
18220               _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
18221             }
18222             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18223               _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
18224             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18225               _cimg_mp_const_scalar((longT)mem[arg1]<<(unsigned int)mem[arg2]);
18226             if (!arg1) _cimg_mp_return(0);
18227             if (!arg2) _cimg_mp_return(arg1);
18228             _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
18229           }
18230 
18231         for (s = se3, ns = se2; s>ss; --s, --ns)
18232           if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
18233             _cimg_mp_op("Operator '>>'");
18234             arg1 = compile(ss,s,depth1,0,bloc_flags);
18235             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18236             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18237             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18238               _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
18239             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18240               if (!arg2) _cimg_mp_return(arg1);
18241               _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
18242             }
18243             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18244               _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
18245             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18246               _cimg_mp_const_scalar((longT)mem[arg1]>>(unsigned int)mem[arg2]);
18247             if (!arg1) _cimg_mp_return(0);
18248             if (!arg2) _cimg_mp_return(arg1);
18249             _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
18250           }
18251 
18252         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18253           if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18254               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18255               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18256                                                                                      *(ps - 1)<='9')))) &&
18257               level[s - expr._data]==clevel) { // Addition ('+')
18258             _cimg_mp_op("Operator '+'");
18259             arg1 = compile(ss,s,depth1,0,bloc_flags);
18260             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18261             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18262             if (!arg2) _cimg_mp_return(arg1);
18263             if (!arg1) _cimg_mp_return(arg2);
18264             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
18265             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
18266             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
18267             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18268               _cimg_mp_const_scalar(mem[arg1] + mem[arg2]);
18269             if (code) { // Try to spot linear case 'a*b + c'
18270               CImg<ulongT> &pop = code.back();
18271               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18272                 arg3 = (unsigned int)pop[1];
18273                 arg4 = (unsigned int)pop[2];
18274                 arg5 = (unsigned int)pop[3];
18275                 code.remove();
18276                 CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18277                 _cimg_mp_return(arg3);
18278               }
18279             }
18280             if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
18281             if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
18282             _cimg_mp_scalar2(mp_add,arg1,arg2);
18283           }
18284 
18285         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18286           if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18287               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18288               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18289                                                                                      *(ps - 1)<='9')))) &&
18290               level[s - expr._data]==clevel) { // Subtraction ('-')
18291             _cimg_mp_op("Operator '-'");
18292             arg1 = compile(ss,s,depth1,0,bloc_flags);
18293             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18294             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18295             if (!arg2) _cimg_mp_return(arg1);
18296             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
18297             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
18298             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18299               if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
18300               _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
18301             }
18302             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18303               _cimg_mp_const_scalar(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_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18339               _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
18340             if (!arg1 || !arg2) _cimg_mp_return(0);
18341             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18342           }
18343 
18344         for (s = se3, ns = se2; s>ss; --s, --ns)
18345           if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
18346             _cimg_mp_op("Operator '//'");
18347             arg1 = compile(ss,s,depth1,0,bloc_flags);
18348             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18349             _cimg_mp_check_type(arg1,1,3,2);
18350             _cimg_mp_check_type(arg2,2,3,2);
18351             if (arg2==1) _cimg_mp_return(arg1);
18352             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18353               pos = vector(2);
18354               CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
18355               return_new_comp = true;
18356               _cimg_mp_return(pos);
18357             }
18358             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18359             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18360               pos = vector(2);
18361               CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
18362               return_new_comp = true;
18363               _cimg_mp_return(pos);
18364             }
18365             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18366               _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
18367             if (!arg1) _cimg_mp_return(0);
18368             _cimg_mp_scalar2(mp_div,arg1,arg2);
18369           }
18370 
18371         for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
18372             _cimg_mp_op("Operator '*'");
18373             arg1 = compile(ss,s,depth1,0,bloc_flags);
18374             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18375             p2 = _cimg_mp_size(arg2);
18376             if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication
18377               pos = vector(p2);
18378               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
18379               return_new_comp = true;
18380               _cimg_mp_return(pos);
18381             }
18382             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18383             if (arg2==1) _cimg_mp_return(arg1);
18384             if (arg1==1) _cimg_mp_return(arg2);
18385             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
18386             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
18387             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
18388             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18389               _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
18390 
18391             if (code) { // Try to spot double multiplication 'a*b*c'
18392               CImg<ulongT> &pop = code.back();
18393               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18394                 arg3 = (unsigned int)pop[1];
18395                 arg4 = (unsigned int)pop[2];
18396                 arg5 = (unsigned int)pop[3];
18397                 code.remove();
18398                 CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18399                 _cimg_mp_return(arg3);
18400               }
18401             }
18402             if (!arg1 || !arg2) _cimg_mp_return(0);
18403             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18404           }
18405 
18406         for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
18407             _cimg_mp_op("Operator '/'");
18408             arg1 = compile(ss,s,depth1,0,bloc_flags);
18409             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18410             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18411             if (arg2==1) _cimg_mp_return(arg1);
18412             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
18413             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18414             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
18415             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18416               _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
18417             if (!arg1) _cimg_mp_return(0);
18418             _cimg_mp_scalar2(mp_div,arg1,arg2);
18419           }
18420 
18421         for (s = se2, ns = se1; s>ss; --s, --ns)
18422           if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
18423             _cimg_mp_op("Operator '%'");
18424             arg1 = compile(ss,s,depth1,0,bloc_flags);
18425             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18426             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18427             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
18428             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
18429             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
18430             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18431               _cimg_mp_const_scalar(cimg::mod(mem[arg1],mem[arg2]));
18432             _cimg_mp_scalar2(mp_modulo,arg1,arg2);
18433           }
18434 
18435         if (se1>ss) {
18436           if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
18437             _cimg_mp_op("Operator '+'");
18438             _cimg_mp_return(compile(ss1,se,depth1,0,bloc_flags));
18439           }
18440 
18441           if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
18442             _cimg_mp_op("Operator '-'");
18443             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18444             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
18445             if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(-mem[arg1]);
18446             _cimg_mp_scalar1(mp_minus,arg1);
18447           }
18448 
18449           if (*ss=='!') { // Logical not ('!')
18450             _cimg_mp_op("Operator '!'");
18451             if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
18452               arg1 = compile(ss2,se,depth1,0,bloc_flags);
18453               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
18454               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]);
18455               _cimg_mp_scalar1(mp_bool,arg1);
18456             }
18457             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18458             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
18459             if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(!mem[arg1]);
18460             _cimg_mp_scalar1(mp_logical_not,arg1);
18461           }
18462 
18463           if (*ss=='~') { // Bitwise not ('~')
18464             _cimg_mp_op("Operator '~'");
18465             arg1 = compile(ss1,se,depth1,0,bloc_flags);
18466             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
18467             if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(~(unsigned int)mem[arg1]);
18468             _cimg_mp_scalar1(mp_bitwise_not,arg1);
18469           }
18470         }
18471 
18472         for (s = se3, ns = se2; s>ss; --s, --ns)
18473           if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
18474             _cimg_mp_op("Operator '^^'");
18475             arg1 = compile(ss,s,depth1,0,bloc_flags);
18476             arg2 = compile(s + 2,se,depth1,0,bloc_flags);
18477             _cimg_mp_check_type(arg1,1,3,2);
18478             _cimg_mp_check_type(arg2,2,3,2);
18479             if (arg2==1) _cimg_mp_return(arg1);
18480             pos = vector(2);
18481             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18482               CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
18483             else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
18484               CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
18485             else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18486               CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
18487             else
18488               CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
18489             return_new_comp = true;
18490             _cimg_mp_return(pos);
18491           }
18492 
18493         for (s = se2; s>ss; --s)
18494           if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
18495             _cimg_mp_op("Operator '^'");
18496             arg1 = compile(ss,s,depth1,0,bloc_flags);
18497             arg2 = compile(s + 1,se,depth1,0,bloc_flags);
18498             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18499             if (arg2==1) _cimg_mp_return(arg1);
18500             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
18501             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
18502             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
18503             if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
18504               _cimg_mp_const_scalar(std::pow(mem[arg1],mem[arg2]));
18505             switch (arg2) {
18506             case 0 : _cimg_mp_return(1);
18507             case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
18508             case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
18509             case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
18510             default :
18511               if (_cimg_mp_is_const_scalar(arg2)) {
18512                 if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
18513                 else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
18514               }
18515               _cimg_mp_scalar2(mp_pow,arg1,arg2);
18516             }
18517           }
18518 
18519         // Percentage computation.
18520         if (*se1=='%') {
18521           arg1 = compile(ss,se1,depth1,0,bloc_flags);
18522           arg2 = _cimg_mp_is_const_scalar(arg1)?0:const_scalar(100);
18523           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18524           if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]/100);
18525           _cimg_mp_scalar2(mp_div,arg1,arg2);
18526         }
18527 
18528         // Degree to radian postfix operator ('°' in UTF-8).
18529         if (se2>ss && (unsigned char)*se2==0xC2 && (unsigned char)*se1==0xB0) {
18530           arg1 = compile(ss,se2,depth1,0,bloc_flags);
18531           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
18532           if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
18533           _cimg_mp_scalar1(mp_deg2rad,arg1);
18534         }
18535 
18536         // Pre/post-decrement and increment.
18537         is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
18538         if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) {
18539           if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
18540             _cimg_mp_op("Operator '++'");
18541             op = mp_self_increment;
18542           } else {
18543             _cimg_mp_op("Operator '--'");
18544             op = mp_self_decrement;
18545           }
18546           ref.assign(7);
18547           arg1 = is_sth?compile(ss2,se,depth1,ref,bloc_flags):
18548             compile(ss,se2,depth1,ref,bloc_flags); // Variable slot
18549 
18550           // Apply operator on a copy to prevent modifying a constant or a variable.
18551           if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
18552             if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
18553             else arg1 = scalar1(mp_copy,arg1);
18554           }
18555 
18556           if (is_sth) pos = arg1; // Determine return index, depending on pre/post action
18557           else {
18558             if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
18559             else pos = scalar1(mp_copy,arg1);
18560           }
18561 
18562           if (*ref==1) { // Vector value (scalar): V[k]++
18563             arg3 = ref[1]; // Vector slot
18564             arg4 = ref[2]; // Index
18565             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18566             CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
18567             CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
18568               move_to(code);
18569             _cimg_mp_return(pos);
18570           }
18571 
18572           if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
18573             if (!is_inside_critical) is_parallelizable = false;
18574             p1 = ref[1]; // Index
18575             is_relative = (bool)ref[2];
18576             arg3 = ref[3]; // Offset
18577             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18578             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18579             if (p1!=~0U) {
18580               if (!listout) _cimg_mp_return(pos);
18581               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
18582                                   arg1,p1,arg3).move_to(code);
18583             } else {
18584               if (!imgout) _cimg_mp_return(pos);
18585               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
18586                                   arg1,arg3).move_to(code);
18587             }
18588             _cimg_mp_return(pos);
18589           }
18590 
18591           if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
18592             if (!is_inside_critical) is_parallelizable = false;
18593             p1 = ref[1]; // Index
18594             is_relative = (bool)ref[2];
18595             arg3 = ref[3]; // X
18596             arg4 = ref[4]; // Y
18597             arg5 = ref[5]; // Z
18598             arg6 = ref[6]; // C
18599             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18600             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18601             if (p1!=~0U) {
18602               if (!listout) _cimg_mp_return(pos);
18603               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
18604                                   arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
18605             } else {
18606               if (!imgout) _cimg_mp_return(pos);
18607               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
18608                                   arg1,arg3,arg4,arg5,arg6).move_to(code);
18609             }
18610             _cimg_mp_return(pos);
18611           }
18612 
18613           if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
18614             if (!is_inside_critical) is_parallelizable = false;
18615             p1 = ref[1]; // Index
18616             is_relative = (bool)ref[2];
18617             arg3 = ref[3]; // Offset
18618             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18619             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18620             if (p1!=~0U) {
18621               if (!listout) _cimg_mp_return(pos);
18622               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
18623                                   arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
18624             } else {
18625               if (!imgout) _cimg_mp_return(pos);
18626               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
18627                                   arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
18628             }
18629             _cimg_mp_return(pos);
18630           }
18631 
18632           if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
18633             if (!is_inside_critical) is_parallelizable = false;
18634             p1 = ref[1]; // Index
18635             is_relative = (bool)ref[2];
18636             arg3 = ref[3]; // X
18637             arg4 = ref[4]; // Y
18638             arg5 = ref[5]; // Z
18639             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18640             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18641             if (p1!=~0U) {
18642               if (!listout) _cimg_mp_return(pos);
18643               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
18644                                   arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18645             } else {
18646               if (!imgout) _cimg_mp_return(pos);
18647               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
18648                                   arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18649             }
18650             _cimg_mp_return(pos);
18651           }
18652 
18653           if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
18654             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18655             _cimg_mp_return(pos);
18656           }
18657 
18658           if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++
18659             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18660             _cimg_mp_return(pos);
18661           }
18662 
18663           if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
18664           else variable_name.assign(ss,(unsigned int)(se1 - ss));
18665           variable_name.back() = 0;
18666           cimg::strpare(variable_name,false,true);
18667           _cimg_mp_strerr;
18668           cimg::strellipsize(variable_name,64);
18669           throw CImgArgumentException("[" cimg_appname "_math_parser] "
18670                                       "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
18671                                       "in expression '%s'.",
18672                                       pixel_type(),_cimg_mp_calling_function,s_op,
18673                                       _cimg_mp_is_const_scalar(arg1)?"const ":"",
18674                                       variable_name._data,s0);
18675         }
18676 
18677         // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
18678         if (*se1==']') {
18679           _cimg_mp_op("Value accessor '[]'");
18680 
18681           // Find opening bracket for the offset.
18682           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18683           if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
18684           is_sth=s0>ss && *(s0-1)==']';  // Particular case s.a. '..[..][..]' ?
18685           is_relative = *ss=='j' || *ss=='J';
18686 
18687           if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' &&
18688               (reserved_label[(int)*ss]==~0U ||
18689                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector
18690             if (*ss2=='#') { // Index specified
18691               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18692               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18693               _cimg_mp_check_const_index(p1);
18694               _cimg_mp_check_list(false);
18695             } else { p1 = ~0U; s0 = ss2; }
18696             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18697             p2 = 1 + (p1!=~0U);
18698             arg1 = compile(s0,s1,depth1,0,bloc_flags); // Offset
18699             _cimg_mp_check_type(arg1,p2,1,0);
18700             arg2 = ~0U;
18701             if (s1<se1) {
18702               arg2 = compile(++s1,se1,depth1,0,bloc_flags); // Boundary
18703               _cimg_mp_check_type(arg2,p2 + 1,1,0);
18704             }
18705             if (p_ref && arg2==~0U) {
18706               *p_ref = 4;
18707               p_ref[1] = p1;
18708               p_ref[2] = (unsigned int)is_relative;
18709               p_ref[3] = arg1;
18710               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18711             }
18712             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
18713             if (p1==~0U) p2 = imgin._spectrum;
18714             else {
18715               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
18716               p2 = listin[p3]._spectrum;
18717             }
18718             if (!p2) _cimg_mp_return(0);
18719             pos = vector(p2);
18720             if (p1!=~0U) {
18721               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
18722                                   pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18723             } else {
18724               need_input_copy = true;
18725               CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
18726                                   pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18727             }
18728             return_new_comp = true;
18729             _cimg_mp_return(pos);
18730           }
18731 
18732           if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' &&
18733               (reserved_label[(int)*ss]==~0U ||
18734                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar
18735             if (*ss2=='#') { // Index specified
18736               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18737               p1 = compile(ss3,s0++,depth1,0,bloc_flags);
18738             } else { p1 = ~0U; s0 = ss2; }
18739             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18740             arg1 = compile(s0,s1,depth1,0,bloc_flags); // Offset
18741             arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):~0U; // Boundary
18742             if (p_ref && arg2==~0U) {
18743               *p_ref = 2;
18744               p_ref[1] = p1;
18745               p_ref[2] = (unsigned int)is_relative;
18746               p_ref[3] = arg1;
18747               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
18748               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
18749             }
18750             if (p1!=~0U) {
18751               if (!listin) _cimg_mp_return(0);
18752               pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18753             } else {
18754               if (!imgin) _cimg_mp_return(0);
18755               need_input_copy = true;
18756               pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18757             }
18758             memtype[pos] = -1; // Prevent from being used in further optimization
18759             _cimg_mp_return(pos);
18760           }
18761 
18762           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18763           if (s0>ss) { // Vector element
18764             arg1 = compile(ss,s0,depth1,0,bloc_flags);
18765             if (_cimg_mp_is_scalar(arg1)) {
18766               variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
18767               _cimg_mp_strerr;
18768               cimg::strellipsize(variable_name,64);
18769               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18770                                           "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
18771                                           "in expression '%s'.",
18772                                           pixel_type(),_cimg_mp_calling_function,s_op,
18773                                           variable_name._data,s0);
18774             }
18775             s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18776 
18777             if (s1<se1) { // Two or three arguments -> sub-vector extraction
18778               p1 = _cimg_mp_size(arg1);
18779               arg2 = compile(++s0,s1,depth1,0,bloc_flags); // Starting index
18780               s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18781               arg3 = compile(s1,s0,depth1,0,bloc_flags); // Length
18782               arg4 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):1; // Step
18783               _cimg_mp_check_const_scalar(arg3,2,3);
18784               arg3 = (unsigned int)mem[arg3];
18785               pos = vector(arg3);
18786               CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
18787               return_new_comp = true;
18788               _cimg_mp_return(pos);
18789             }
18790 
18791             // One argument -> vector value reference
18792             arg2 = compile(++s0,se1,depth1,0,bloc_flags);
18793             if (_cimg_mp_is_const_scalar(arg2)) { // Constant index
18794               nb = (int)mem[arg2];
18795               if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
18796               variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
18797               _cimg_mp_strerr;
18798               cimg::strellipsize(variable_name,64);
18799               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18800                                           "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
18801                                           "(vector '%s' has dimension %u), "
18802                                           "in expression '%s'.",
18803                                           pixel_type(),_cimg_mp_calling_function,
18804                                           variable_name._data,nb,
18805                                           variable_name._data,_cimg_mp_size(arg1),s0);
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_const_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_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar((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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
19101                 _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar((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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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 = const_scalar(_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_const_scalar(arg1)) _cimg_mp_const_scalar(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_const_scalar(arg1)) _cimg_mp_const_scalar(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'.",
19433                                             pixel_type(),_cimg_mp_calling_function,s_op,s0);
19434               }
19435 
19436               _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
19437               _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
19438               _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
19439               _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
19440               if (opcode[4]!=(ulongT)~0U) {
19441                 _cimg_mp_check_const_scalar((unsigned int)opcode[4],arg1,3);
19442                 opcode[4] = (ulongT)mem[opcode[4]];
19443               }
19444               if (opcode[5]!=(ulongT)~0U) {
19445                 _cimg_mp_check_const_scalar((unsigned int)opcode[5],arg1 + 1,3);
19446                 opcode[5] = (ulongT)mem[opcode[5]];
19447               }
19448               if (opcode[6]!=(ulongT)~0U) {
19449                 _cimg_mp_check_const_scalar((unsigned int)opcode[6],arg1 + 2,3);
19450                 opcode[6] = (ulongT)mem[opcode[6]];
19451               }
19452               if (opcode[7]!=(ulongT)~0U) {
19453                 _cimg_mp_check_const_scalar((unsigned int)opcode[7],arg1 + 3,3);
19454                 opcode[7] = (ulongT)mem[opcode[7]];
19455               }
19456               _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
19457 
19458               if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
19459                   opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
19460                 p2 = 0;
19461                 if (p1!=~0U) {
19462                   _cimg_mp_check_const_scalar(p1,1,1);
19463                   p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
19464                 }
19465                 const CImg<T> &img = p1!=~0U?listin[p2]:imgin;
19466                 if (!img) {
19467                   _cimg_mp_strerr;
19468                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
19469                                               "CImg<%s>::%s: %s: Cannot crop empty image when "
19470                                               "some xyzc-coordinates are unspecified, in expression '%s'.",
19471                                               pixel_type(),_cimg_mp_calling_function,s_op,s0);
19472                 }
19473                 if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
19474                 if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
19475                 if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
19476                 if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
19477               }
19478 
19479               pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
19480               CImg<ulongT>::vector((ulongT)mp_crop,
19481                                   pos,p1,
19482                                   *opcode,opcode[1],opcode[2],opcode[3],
19483                                   opcode[4],opcode[5],opcode[6],opcode[7],
19484                                   opcode[8]).move_to(code);
19485               return_new_comp = true;
19486               _cimg_mp_return(pos);
19487             }
19488 
19489             if (!std::strncmp(ss,"cross(",6)) { // Cross product
19490               _cimg_mp_op("Function 'cross()'");
19491               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19492               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
19493               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
19494               _cimg_mp_check_type(arg1,1,2,3);
19495               _cimg_mp_check_type(arg2,2,2,3);
19496               pos = vector(3);
19497               CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
19498               return_new_comp = true;
19499               _cimg_mp_return(pos);
19500             }
19501 
19502             if (!std::strncmp(ss,"cut(",4)) { // Cut
19503               _cimg_mp_op("Function 'cut()'");
19504               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19505               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
19506               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19507               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
19508               arg3 = compile(++s2,se1,depth1,0,bloc_flags);
19509               _cimg_mp_check_type(arg2,2,1,0);
19510               _cimg_mp_check_type(arg3,3,1,0);
19511               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
19512               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
19513                 val = mem[arg1];
19514                 val1 = mem[arg2];
19515                 val2 = mem[arg3];
19516                 _cimg_mp_const_scalar(val<val1?val1:val>val2?val2:val);
19517               }
19518               _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
19519             }
19520 
19521             if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate
19522               is_sth = *ss2=='n'; // is_convolve?
19523               _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'");
19524               op = is_sth?mp_convolve:mp_correlate;
19525               const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector
19526                                                 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA
19527                                                 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM
19528                                                 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode
19529                                                 ~0U,~0U,~0U, // [15]=xcenter, [16]=ycenter, [17]=zcenter
19530                                                 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart
19531                                                 ~0U,~0U,~0U, // [21]=xend, [22]=yend, [23]=zend
19532                                                 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride
19533                                                 1,1,1, // [27]=xdilation, [28]=ydilation, [29]=zdilation,
19534                                                 0 }; // [30]=interpolation_type
19535 
19536               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19537               CImg<ulongT>(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode);
19538 
19539               arg1 = 2;
19540               for (s = std::strchr(ss,'(') + 1; s<se && arg1<l_opcode[0]._height; ++s) {
19541                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19542                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19543                 l_opcode(0,arg1++) = compile(s,ns,depth1,0,bloc_flags);
19544                 s = ns;
19545               }
19546               l_opcode[0].move_to(opcode);
19547 
19548               if (arg1<12 || arg1>opcode._height) {
19549                 _cimg_mp_strerr;
19550                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19551                                             "CImg<%s>::%s: %s: %s arguments provided, in expression '%s'.",
19552                                             pixel_type(),_cimg_mp_calling_function,s_op,
19553                                             arg1<12?"Not enough":"Too much",s0);
19554               }
19555               _cimg_mp_check_type(opcode[2],1,2,0); // A
19556               _cimg_mp_check_const_scalar(opcode[3],2,3); // wA
19557               _cimg_mp_check_const_scalar(opcode[4],3,3); // hA
19558               _cimg_mp_check_const_scalar(opcode[5],4,3); // dA
19559               _cimg_mp_check_const_scalar(opcode[6],5,3); // sA
19560               _cimg_mp_check_type(opcode[7],6,2,0); // M
19561               _cimg_mp_check_const_scalar(opcode[8],7,3); // wM
19562               _cimg_mp_check_const_scalar(opcode[9],8,3); // hM
19563               _cimg_mp_check_const_scalar(opcode[10],9,3); // dM
19564               _cimg_mp_check_const_scalar(opcode[11],10,3); // sM
19565               _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions
19566               _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized
19567               _cimg_mp_check_const_scalar(opcode[14],13,1); // channel_mode
19568               if (opcode[15]!=~0U) _cimg_mp_check_type(opcode[15],14,1,0); // xcenter
19569               if (opcode[16]!=~0U) _cimg_mp_check_type(opcode[16],15,1,0); // ycenter
19570               if (opcode[17]!=~0U) _cimg_mp_check_type(opcode[17],16,1,0); // zcenter
19571               _cimg_mp_check_const_scalar(opcode[18],17,1); // xstart
19572               _cimg_mp_check_const_scalar(opcode[19],18,1); // ystart
19573               _cimg_mp_check_const_scalar(opcode[20],19,1); // zstart
19574               if (opcode[21]!=~0U) _cimg_mp_check_const_scalar(opcode[21],20,1); // xend
19575               if (opcode[22]!=~0U) _cimg_mp_check_const_scalar(opcode[22],21,1); // yend
19576               if (opcode[23]!=~0U) _cimg_mp_check_const_scalar(opcode[23],22,1); // zend
19577               _cimg_mp_check_const_scalar(opcode[24],23,0); // xstride
19578               _cimg_mp_check_const_scalar(opcode[25],24,0); // ystride
19579               _cimg_mp_check_const_scalar(opcode[26],25,0); // zstride
19580               _cimg_mp_check_type(opcode[27],26,1,0); // xdilation
19581               _cimg_mp_check_type(opcode[28],27,1,0); // ydilation
19582               _cimg_mp_check_type(opcode[29],28,1,0); // zdilation
19583               _cimg_mp_check_type(opcode[30],29,1,0); // interpolation_type
19584 
19585               const unsigned int
19586                 wA = (unsigned int)mem[opcode[3]],
19587                 hA = (unsigned int)mem[opcode[4]],
19588                 dA = (unsigned int)mem[opcode[5]],
19589                 sA = (unsigned int)mem[opcode[6]],
19590                 wM = (unsigned int)mem[opcode[8]],
19591                 hM = (unsigned int)mem[opcode[9]],
19592                 dM = (unsigned int)mem[opcode[10]],
19593                 sM = (unsigned int)mem[opcode[11]],
19594                 channel_mode = (unsigned int)mem[opcode[14]];
19595               const int
19596                 xstart = (int)mem[opcode[18]],
19597                 ystart = (int)mem[opcode[19]],
19598                 zstart = (int)mem[opcode[20]],
19599                 xend = opcode[21]!=~0U?(int)mem[opcode[21]]:wA - 1,
19600                 yend = opcode[22]!=~0U?(int)mem[opcode[22]]:hA - 1,
19601                 zend = opcode[23]!=~0U?(int)mem[opcode[23]]:dA - 1;
19602 
19603               if (xstart>xend || ystart>yend || zstart>zend) {
19604                 _cimg_mp_strerr;
19605                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19606                                             "CImg<%s>::%s: %s: Invalid xyz-start/end arguments "
19607                                             "(start = (%d,%d,%d), end = (%d,%d,%d)), in expression '%s'.",
19608                                             pixel_type(),_cimg_mp_calling_function,s_op,
19609                                             xstart,ystart,zstart,xend,yend,zend,s0);
19610               }
19611 
19612               const float
19613                 xstride = (float)mem[opcode[24]],
19614                 ystride = (float)mem[opcode[25]],
19615                 zstride = (float)mem[opcode[26]];
19616 
19617               if (xstride<=0 || ystride<=0 || zstride<=0) {
19618                 _cimg_mp_strerr;
19619                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19620                                             "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), "
19621                                             "in expression '%s'.",
19622                                             pixel_type(),_cimg_mp_calling_function,s_op,
19623                                             xstride,ystride,zstride,s0);
19624               }
19625 
19626               arg2 = xend - xstart + 1;
19627               arg3 = yend - ystart + 1;
19628               arg4 = zend + zstart + 1;
19629               arg5 = !channel_mode?sA*sM:channel_mode==1?std::max(sA,sM):
19630                 channel_mode==2?std::max(sA,sM)/std::min(sA,sM):1U;
19631 
19632               opcode[1] = pos = vector(arg2*arg3*arg4*arg5);
19633               opcode[3] = (ulongT)wA;
19634               opcode[4] = (ulongT)hA;
19635               opcode[5] = (ulongT)dA;
19636               opcode[6] = (ulongT)sA;
19637               opcode[8] = (ulongT)wM;
19638               opcode[9] = (ulongT)hM;
19639               opcode[10] = (ulongT)dM;
19640               opcode[11] = (ulongT)sM;
19641               opcode[14] = (ulongT)channel_mode;
19642               opcode[18] = (ulongT)xstart;
19643               opcode[19] = (ulongT)ystart;
19644               opcode[20] = (ulongT)zstart;
19645               opcode[21] = (ulongT)xend;
19646               opcode[22] = (ulongT)yend;
19647               opcode[23] = (ulongT)zend;
19648               opcode.move_to(code);
19649               return_new_comp = true;
19650               _cimg_mp_return(pos);
19651             }
19652             break;
19653 
19654           case 'd' :
19655             if (*ss1=='(') { // Image depth
19656               _cimg_mp_op("Function 'd()'");
19657               if (*ss2=='#') { // Index specified
19658                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
19659                 _cimg_mp_check_list(false);
19660               } else { if (ss2!=se1) break; p1 = ~0U; }
19661               _cimg_mp_scalar1(mp_image_d,p1);
19662             }
19663 
19664             if (!std::strncmp(ss,"da_back(",8) ||
19665                 !std::strncmp(ss,"da_pop(",7)) { // Get latest element in a dynamic array
19666               if (!is_inside_critical) is_parallelizable = false;
19667               const bool is_pop = *ss3=='p';
19668               _cimg_mp_op(is_pop?"Function 'da_pop()'":"Function 'da_back()'");
19669               s0 = ss + (is_pop?7:8);
19670               if (*s0=='#') { // Index specified
19671                 s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19672                 p1 = compile(s0,s1++,depth1,0,bloc_flags);
19673                 _cimg_mp_check_list(true);
19674               } else { p1 = 11; s1 = s0; }
19675               _cimg_mp_check_const_scalar(p1,1,1);
19676               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
19677               p2 = listin[p3]._spectrum;
19678               if (p2>1) pos = vector(p2); else pos = scalar(); // Return vector or scalar result
19679               CImg<ulongT>::vector((ulongT)mp_da_back_or_pop,pos,p2,p1,is_pop).move_to(code);
19680               return_new_comp = true;
19681               _cimg_mp_return(pos);
19682             }
19683 
19684             if (!std::strncmp(ss,"da_insert(",10) ||
19685                 !std::strncmp(ss,"da_push(",8)) { // Insert element(s) in a dynamic array
19686               if (!is_inside_critical) is_parallelizable = false;
19687               const bool is_push = *ss3=='p';
19688               _cimg_mp_op(is_push?"Function 'da_push()'":"Function 'da_insert()'");
19689               s0 = ss + (is_push?8:10);
19690               if (*s0=='#') { // Index specified
19691                 s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19692                 p1 = compile(s0,s1++,depth1,0,bloc_flags);
19693                 _cimg_mp_check_list(true);
19694               } else { p1 = 11; s1 = s0; }
19695 
19696               if (!is_push) {
19697                 s0 = s1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19698                 arg1 = compile(s0,s1++,depth1,0,bloc_flags); // Position
19699               } else arg1 = ~0U;
19700 
19701               CImg<ulongT>::vector((ulongT)mp_da_insert,_cimg_mp_slot_nan,p1,arg1,0,0).move_to(l_opcode);
19702               p3 = p1==~0U?2:3;
19703               p1 = ~0U;
19704               for (s = s1; s<se; ++s) {
19705                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19706                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19707                 arg2 = compile(s,ns,depth1,0,bloc_flags); // Element
19708                 p2 = _cimg_mp_size(arg2);
19709                 if (p1==~0U) p1 = p2;
19710                 else {
19711                   if (!p1) _cimg_mp_check_type(arg2,p3,1,0);
19712                   else _cimg_mp_check_type(arg2,p3,2,p1);
19713                 }
19714                 CImg<ulongT>::vector(arg2).move_to(l_opcode);
19715                 s = ns;
19716                 ++p3;
19717               }
19718               if (p1==~0U) compile(s1 + 1,se1,depth1,0,bloc_flags); // Missing element -> error
19719               (l_opcode>'y').move_to(opcode);
19720               opcode[4] = p1;
19721               opcode[5] = opcode._height;
19722               opcode.move_to(code);
19723               _cimg_mp_return_nan();
19724             }
19725 
19726             if (!std::strncmp(ss,"da_remove(",10)) { // Remove element(s) in a dynamic array
19727               if (!is_inside_critical) is_parallelizable = false;
19728               _cimg_mp_op("Function 'da_remove()'");
19729               if (ss[10]=='#') { // Index specified
19730                 s0 = ss + 11; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19731                 p1 = compile(ss + 11,s0++,depth1,0,bloc_flags);
19732                 _cimg_mp_check_list(true);
19733               } else { p1 = 11; s0 = ss + 10; }
19734 
19735               arg1 = arg2 = ~0U;
19736               if (s0<se1) {
19737                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19738                 arg1 = compile(s0,s1,depth1,0,bloc_flags); // Starting position
19739                 arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):~0U; // Ending position
19740               }
19741               CImg<ulongT>::vector((ulongT)mp_da_remove,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
19742               _cimg_mp_return_nan();
19743             }
19744 
19745             if (!std::strncmp(ss,"da_size(",8)) { // Size of a dynamic array
19746               if (!is_inside_critical) is_parallelizable = false;
19747               _cimg_mp_op("Function 'da_size()'");
19748               if (ss[8]=='#') { // Index specified
19749                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19750                 p1 = compile(ss + 9,s0++,depth1,0,bloc_flags);
19751                 _cimg_mp_check_list(true);
19752               } else { p1 = 11; s0 = ss + 8; }
19753               _cimg_mp_scalar1(mp_da_size,p1);
19754             }
19755 
19756             if (!std::strncmp(ss,"date(",5)) { // Current date or file date
19757               _cimg_mp_op("Function 'date()'");
19758               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19759               arg1 = ss5!=se1?compile(ss5,s1,depth1,0,bloc_flags):~0U;
19760               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):~0U;
19761               if (arg2!=~0U) _cimg_mp_check_type(arg2,1,2,0);
19762               pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
19763               CImg<ulongT>::vector((ulongT)mp_date,pos,_cimg_mp_size(pos),
19764                                    arg1,arg1==~0U?~0U:_cimg_mp_size(arg1),
19765                                    arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code);
19766               return_new_comp = true;
19767               _cimg_mp_return(pos);
19768             }
19769 
19770             if (!std::strncmp(ss,"debug(",6)) { // Print debug info
19771               _cimg_mp_op("Function 'debug()'");
19772               p1 = code._width;
19773               arg1 = compile(ss6,se1,depth1,p_ref,bloc_flags);
19774               *se1 = 0;
19775               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
19776               cimg::strpare(variable_name,false,true);
19777               ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
19778                 variable_name)>'y').move_to(opcode);
19779               opcode[2] = opcode._height;
19780               opcode.move_to(code,p1);
19781               *se1 = ')';
19782               _cimg_mp_return(arg1);
19783             }
19784 
19785             if (!std::strncmp(ss,"deg2rad(",8)) { // Degrees to radians
19786               _cimg_mp_op("Function 'deg2rad()'");
19787               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
19788               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
19789               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
19790               _cimg_mp_scalar1(mp_deg2rad,arg1);
19791             }
19792 
19793             if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
19794               _cimg_mp_op("Function 'display()'");
19795               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19796                 CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
19797                 _cimg_mp_return_nan();
19798               }
19799               if (*ss8!='#') { // Vector
19800                 s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19801                 arg1 = compile(ss8,s1,depth1,0,bloc_flags);
19802                 arg2 = 0; arg3 = arg4 = arg5 = 1;
19803                 if (s1<se1) {
19804                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19805                   arg2 = compile(s1 + 1,s2,depth1,0,bloc_flags);
19806                   if (s2<se1) {
19807                     s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
19808                     arg3 = compile(s2,s3,depth1,0,bloc_flags);
19809                     if (s3<se1) {
19810                       s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19811                       arg4 = compile(s3,s2,depth1,0,bloc_flags);
19812                       arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
19813                     }
19814                   }
19815                 }
19816                 _cimg_mp_check_type(arg2,2,1,0);
19817                 _cimg_mp_check_type(arg3,3,1,0);
19818                 _cimg_mp_check_type(arg4,4,1,0);
19819                 _cimg_mp_check_type(arg5,5,1,0);
19820 
19821                 c1 = *s1; *s1 = 0;
19822                 variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
19823                 cimg::strpare(variable_name,false,true);
19824                 if (_cimg_mp_is_vector(arg1))
19825                   ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
19826                     variable_name)>'y').move_to(opcode);
19827                 else
19828                   ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
19829                     variable_name)>'y').move_to(opcode);
19830                 opcode[2] = opcode._height;
19831                 opcode.move_to(code);
19832 
19833                 ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
19834                                        arg2,arg3,arg4,arg5),
19835                   variable_name)>'y').move_to(opcode);
19836                 opcode[2] = opcode._height;
19837                 opcode.move_to(code);
19838                 *s1 = c1;
19839                 _cimg_mp_return(arg1);
19840 
19841               } else { // Image
19842                 p1 = compile(ss8 + 1,se1,depth1,0,bloc_flags);
19843                 _cimg_mp_check_list(true);
19844                 CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
19845                 _cimg_mp_return_nan();
19846               }
19847             }
19848 
19849             if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
19850               _cimg_mp_op("Function 'det()'");
19851               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
19852               _cimg_mp_check_matrix_square(arg1,1);
19853               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19854               _cimg_mp_scalar2(mp_det,arg1,p1);
19855             }
19856 
19857             if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
19858               _cimg_mp_op("Function 'diag()'");
19859               CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode);
19860               for (s = ss5; s<se; ++s) {
19861                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19862                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19863                 arg2 = compile(s,ns,depth1,0,bloc_flags);
19864                 if (_cimg_mp_is_vector(arg2))
19865                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19866                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
19867                     move_to(l_opcode);
19868                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19869                 s = ns;
19870               }
19871               (l_opcode>'y').move_to(opcode);
19872               arg1 = opcode._height - 3;
19873               pos = vector(arg1*arg1);
19874               opcode[1] = pos;
19875               opcode[2] = opcode._height;
19876               opcode.move_to(code);
19877               return_new_comp = true;
19878               _cimg_mp_return(pos);
19879             }
19880 
19881             if (!std::strncmp(ss,"dot(",4)) { // Dot product
19882               _cimg_mp_op("Function 'dot()'");
19883               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19884               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
19885               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
19886               if (_cimg_mp_is_vector(arg1)) {
19887                 _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1));
19888                 _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
19889               }
19890               _cimg_mp_check_type(arg2,2,1,0);
19891               _cimg_mp_scalar2(mp_mul,arg1,arg2);
19892             }
19893 
19894             if (!std::strncmp(ss,"do(",3)) { // Do..while
19895               _cimg_mp_op("Function 'do()'");
19896               s0 = *ss2=='('?ss3:ss8;
19897               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19898               arg1 = code._width;
19899               arg6 = mempos;
19900               p1 = compile(s0,s1,depth1,0,bloc_flags); // Body
19901               arg2 = code._width;
19902               p2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):p1; // Condition
19903               _cimg_mp_check_type(p2,2,1,0);
19904               CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
19905                                    p1>=arg6 && !_cimg_mp_is_const_scalar(p1),
19906                                    p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
19907               _cimg_mp_return(p1);
19908             }
19909 
19910             if (!std::strncmp(ss,"draw(",5)) { // Draw image
19911               if (!is_inside_critical) is_parallelizable = false;
19912               _cimg_mp_op("Function 'draw()'");
19913               if (*ss5=='#') { // Index specified
19914                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19915                 p1 = compile(ss6,s0++,depth1,0,bloc_flags);
19916                 _cimg_mp_check_list(true);
19917               } else { p1 = ~0U; s0 = ss5; }
19918               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19919               arg1 = compile(s0,s1,depth1,0,bloc_flags);
19920               arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
19921               arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
19922               arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
19923               arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
19924               s0 = se1;
19925               if (s1<se1) {
19926                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19927                 arg2 = compile(++s1,s0,depth1,0,bloc_flags);
19928                 if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
19929                   p2 = _cimg_mp_size(arg2);
19930                   ++arg2;
19931                   if (p2>1) {
19932                     arg3 = arg2 + 1;
19933                     if (p2>2) {
19934                       arg4 = arg3 + 1;
19935                       if (p2>3) arg5 = arg4 + 1;
19936                     }
19937                   }
19938                   ++s0;
19939                   is_sth = true;
19940                 } else {
19941                   if (s0<se1) {
19942                     is_sth = p1!=~0U;
19943                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19944                     arg3 = compile(++s0,s1,depth1,0,bloc_flags);
19945                     _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
19946                     if (s1<se1) {
19947                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19948                       arg4 = compile(++s1,s0,depth1,0,bloc_flags);
19949                       _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
19950                       if (s0<se1) {
19951                         s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19952                         arg5 = compile(++s0,s1,depth1,0,bloc_flags);
19953                         _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
19954                         s0 = ++s1;
19955                       }
19956                     }
19957                   }
19958                   is_sth = false;
19959                 }
19960               }
19961 
19962               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19963               CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
19964                                    0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode);
19965 
19966               arg2 = arg3 = arg4 = arg5 = ~0U;
19967               p2 = p1!=~0U?0:1;
19968               if (s0<se1) {
19969                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19970                 arg2 = compile(s0,s1,depth1,0,bloc_flags);
19971                 _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
19972                 if (s1<se1) {
19973                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19974                   arg3 = compile(++s1,s0,depth1,0,bloc_flags);
19975                   _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
19976                   if (s0<se1) {
19977                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19978                     arg4 = compile(++s0,s1,depth1,0,bloc_flags);
19979                     _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
19980                     if (s1<se1) {
19981                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19982                       arg5 = compile(++s1,s0,depth1,0,bloc_flags);
19983                       _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
19984                     }
19985                   }
19986                 }
19987               }
19988               if (s0<s1) s0 = s1;
19989 
19990               l_opcode(0,8) = (ulongT)arg2;
19991               l_opcode(0,9) = (ulongT)arg3;
19992               l_opcode(0,10) = (ulongT)arg4;
19993               l_opcode(0,11) = (ulongT)arg5;
19994 
19995               if (s0<se1) {
19996                 s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19997                 arg6 = compile(++s0,s1,depth1,0,bloc_flags);
19998                 _cimg_mp_check_type(arg6,0,1,0);
19999                 l_opcode(0,12) = arg6;
20000                 if (s1<se1) {
20001                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20002                   p2 = compile(++s1,s0,depth1,0,bloc_flags);
20003                   _cimg_mp_check_type(p2,0,2,0);
20004                   l_opcode(0,13) = p2;
20005                   l_opcode(0,14) = _cimg_mp_size(p2);
20006                   p3 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):1;
20007                   _cimg_mp_check_type(p3,0,1,0);
20008                   l_opcode(0,15) = p3;
20009                 }
20010               }
20011               l_opcode[0].move_to(code);
20012               _cimg_mp_return(arg1);
20013             }
20014 
20015             break;
20016 
20017           case 'e' :
20018             if (!std::strncmp(ss,"echo(",5)) { // Echo
20019               _cimg_mp_op("Function 'echo()'");
20020               CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
20021               for (s = ss5; s<se1; ++s) {
20022                 ns = s; while (ns<se1 && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20023                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20024                 arg1 = compile(s,ns,depth1,0,bloc_flags);
20025                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
20026                 s = ns;
20027               }
20028               (l_opcode>'y').move_to(opcode);
20029               opcode[2] = opcode._height;
20030               opcode.move_to(code);
20031               _cimg_mp_return_nan();
20032             }
20033 
20034             if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
20035               _cimg_mp_op("Function 'eig()'");
20036               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20037               _cimg_mp_check_matrix_square(arg1,1);
20038               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
20039               pos = vector((p1 + 1)*p1);
20040               CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
20041               return_new_comp = true;
20042               _cimg_mp_return(pos);
20043             }
20044 
20045             if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
20046               if (!is_inside_critical) is_parallelizable = false;
20047               _cimg_mp_op("Function 'ellipse()'");
20048               if (*ss8=='#') { // Index specified
20049                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20050                 p1 = compile(ss + 9,s0++,depth1,0,bloc_flags);
20051                 _cimg_mp_check_list(true);
20052               } else { p1 = ~0U; s0 = ss8; }
20053               if (s0==se1) compile(s0,se1,depth1,0,bloc_flags); // 'missing' argument error
20054               CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
20055               for (s = s0; s<se; ++s) {
20056                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20057                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20058                 arg2 = compile(s,ns,depth1,0,bloc_flags);
20059                 if (_cimg_mp_is_vector(arg2))
20060                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20061                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20062                     move_to(l_opcode);
20063                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20064                 s = ns;
20065               }
20066               (l_opcode>'y').move_to(opcode);
20067               opcode[2] = opcode._height;
20068               opcode.move_to(code);
20069               _cimg_mp_return_nan();
20070             }
20071 
20072             if (!std::strncmp(ss,"erf(",4)) { // Error function
20073               _cimg_mp_op("Function 'erf()'");
20074               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20075               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1);
20076               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::erf(mem[arg1]));
20077               _cimg_mp_scalar1(mp_erf,arg1);
20078             }
20079 
20080             if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function
20081               _cimg_mp_op("Function 'erfinv()'");
20082               arg1 = compile(ss7,se1,depth1,0,bloc_flags);
20083               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1);
20084               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::erfinv(mem[arg1]));
20085               _cimg_mp_scalar1(mp_erfinv,arg1);
20086             }
20087 
20088             if (!std::strncmp(ss,"exp(",4)) { // Exponential
20089               _cimg_mp_op("Function 'exp()'");
20090               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20091               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
20092               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::exp(mem[arg1]));
20093               _cimg_mp_scalar1(mp_exp,arg1);
20094             }
20095 
20096             if (!std::strncmp(ss,"expr(",5)) { // Vector from expression
20097               _cimg_mp_op("Function 'expr()'");
20098               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20099               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
20100               _cimg_mp_check_type(arg1,1,2,0);
20101               p1 = _cimg_mp_size(arg1);
20102               arg2 = arg3 = arg4 = arg5 = 0;
20103               if (s1<se1) {
20104                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20105                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20106                 _cimg_mp_check_const_scalar(arg2,2,2);
20107                 arg2 = (unsigned int)mem[arg2];
20108                 if (arg2) arg3 = arg4 = arg5 = 1;
20109                 if (s2<se1) {
20110                   s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20111                   arg3 = compile(++s2,s1,depth1,0,bloc_flags);
20112                   _cimg_mp_check_const_scalar(arg3,3,3);
20113                   arg3 = (unsigned int)mem[arg3];
20114                   if (s1<se1) {
20115                     s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20116                     arg4 = compile(++s1,s2,depth1,0,bloc_flags);
20117                     _cimg_mp_check_const_scalar(arg4,4,3);
20118                     arg4 = (unsigned int)mem[arg4];
20119                     arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20120                     _cimg_mp_check_const_scalar(arg5,5,3);
20121                     arg5 = (unsigned int)mem[arg5];
20122                   }
20123                 }
20124               }
20125               p2 = arg2*arg3*arg4*arg5;
20126               if (p2) pos = vector(p2); else pos = scalar();
20127               CImg<ulongT>::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code);
20128               return_new_comp = true;
20129               _cimg_mp_return(pos);
20130             }
20131 
20132             if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
20133               _cimg_mp_op("Function 'eye()'");
20134               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20135               _cimg_mp_check_const_scalar(arg1,1,3);
20136               p1 = (unsigned int)mem[arg1];
20137               pos = vector(p1*p1);
20138               CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
20139               return_new_comp = true;
20140               _cimg_mp_return(pos);
20141             }
20142 
20143             if (!std::strncmp(ss,"end(",4)) { // End
20144               _cimg_mp_op("Function 'end()'");
20145               s1 = ss4; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
20146               if (s1!=se1) {
20147                 const bool is_inside_end = (bool)(bloc_flags&8);
20148                 if (!is_inside_end) code.swap(code_end);
20149                 compile(s1,se1,depth1,p_ref,8);
20150                 if (!is_inside_end) code.swap(code_end);
20151                 is_end_code = true;
20152               }
20153               _cimg_mp_return_nan();
20154             }
20155 
20156             if (!std::strncmp(ss,"end_t(",6)) { // End thread
20157               _cimg_mp_op("Function 'end_t()'");
20158               s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
20159               if (s1!=se1) {
20160                 const bool is_inside_end = (bool)(bloc_flags&16);
20161                 if (!is_inside_end) code.swap(code_end_t);
20162                 compile(s1,se1,depth1,p_ref,16);
20163                 if (!is_inside_end) code.swap(code_end_t);
20164                 is_end_code = true;
20165               }
20166               _cimg_mp_return_nan();
20167             }
20168             break;
20169 
20170           case 'f' :
20171             if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion
20172               _cimg_mp_op("Function 'f2ui()'");
20173               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20174               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1);
20175               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((double)cimg::float2uint((float)mem[arg1]));
20176               _cimg_mp_scalar1(mp_f2ui,arg1);
20177             }
20178 
20179             if (!std::strncmp(ss,"fact(",5)) { // Factorial
20180               _cimg_mp_op("Function 'fact()'");
20181               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20182               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
20183               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::factorial((int)mem[arg1]));
20184               _cimg_mp_scalar1(mp_factorial,arg1);
20185             }
20186 
20187             if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
20188               _cimg_mp_op("Function 'fibo()'");
20189               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20190               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
20191               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::fibonacci((int)mem[arg1]));
20192               _cimg_mp_scalar1(mp_fibonacci,arg1);
20193             }
20194 
20195             if (!std::strncmp(ss,"fill(",5)) { // Fill
20196               _cimg_mp_op("Function 'fill()'");
20197               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20198               arg1 = compile(ss5,s0,depth1,0,bloc_flags); // Object to fill
20199               if (_cimg_mp_is_const_scalar(arg1))
20200                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20201                                             "CImg<%s>::%s: %s: Target scalar is constant, "
20202                                             "in expression '%s'.",
20203                                             pixel_type(),_cimg_mp_calling_function,s_op,ss);
20204               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20205               p1 = code._width;
20206 
20207               if (s1<se1) { // Version with 3 arguments
20208                 variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
20209                 cimg::strpare(variable_name,false,true);
20210                 if (!is_varname(variable_name)) { // Invalid variable name
20211                   cimg::strellipsize(variable_name,64);
20212                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
20213                                               "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
20214                                               "in expression '%s'.",
20215                                               pixel_type(),_cimg_mp_calling_function,s_op,
20216                                               variable_name._data,s0);
20217                 }
20218                 get_variable_pos(variable_name,arg2,arg3);
20219                 arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
20220                 if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
20221                                   _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
20222                   cimg::strellipsize(variable_name,64);
20223                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
20224                                               "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
20225                                               "(expected 'scalar'), in expression '%s'.",
20226                                               pixel_type(),_cimg_mp_calling_function,s_op,
20227                                               s_type(arg2)._data,variable_name._data,s0);
20228                 } else if (arg2==~0U) { // Variable does not exist -> create it
20229                   arg2 = scalar();
20230                   if (arg3!=~0U) reserved_label[arg3] = arg2;
20231                   else {
20232                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
20233                     variable_pos[variable_def._width] = arg2;
20234                     variable_name.move_to(variable_def);
20235                   }
20236                   memtype[arg2] = -1;
20237                 }
20238                 arg3 = compile(++s1,se1,depth1,0,bloc_flags);
20239                 _cimg_mp_check_type(arg3,3,1,0);
20240               } else { // Version with 2 arguments
20241                 arg2 = ~0U;
20242                 arg3 = compile(s0,se1,depth1,0,bloc_flags);
20243               }
20244               // arg2 = variable slot, arg3 = fill expression.
20245               _cimg_mp_check_type(arg3,3,1,0);
20246               CImg<ulongT>::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1).
20247                 move_to(code,p1);
20248               _cimg_mp_return(arg1);
20249             }
20250 
20251             if (!std::strncmp(ss,"find(",5)) { // Find
20252               _cimg_mp_op("Function 'find()'");
20253 
20254               // First argument: data to look at.
20255               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20256               if (*ss5=='#') { // Index specified
20257                 p1 = compile(ss6,s0,depth1,0,bloc_flags);
20258                 _cimg_mp_check_list(false);
20259                 arg1 = ~0U;
20260               } else { // Vector specified
20261                 arg1 = compile(ss5,s0,depth1,0,bloc_flags);
20262                 _cimg_mp_check_type(arg1,1,2,0);
20263                 p1 = ~0U;
20264               }
20265 
20266               // Second argument: data to find.
20267               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20268               arg2 = compile(s0,s1,depth1,0,bloc_flags);
20269 
20270               // Third and fourth arguments: starting index and search direction.
20271               arg3 = _cimg_mp_slot_nan; arg4 = 1;
20272               if (s1<se1) {
20273                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20274                 arg3 = compile(++s1,s0,depth1,0,bloc_flags);
20275                 _cimg_mp_check_type(arg3,3,1,0);
20276                 if (s0<se1) {
20277                   arg4 = compile(++s0,se1,depth1,0,bloc_flags);
20278                   _cimg_mp_check_type(arg4,4,1,0);
20279                 }
20280               }
20281               if (p1!=~0U) {
20282                 if (_cimg_mp_size(arg2)>1)
20283                   _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
20284                 _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20285               }
20286               if (_cimg_mp_size(arg2)>1)
20287                 _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
20288               _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20289             }
20290 
20291             if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
20292               _cimg_mp_op("Function 'for()'");
20293               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20294               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20295               s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
20296               arg1 = code._width;
20297               p1 = compile(ss4,s1,depth1,0,bloc_flags); // Init
20298               arg2 = code._width;
20299               p2 = compile(++s1,s2,depth1,0,bloc_flags); // Cond
20300               arg3 = code._width;
20301               arg6 = mempos;
20302               if (s3<se1) { // Body + post
20303                 p3 = compile(s3 + 1,se1,depth1,0,bloc_flags); // Body
20304                 arg4 = code._width;
20305                 pos = compile(++s2,s3,depth1,0,bloc_flags); // Post
20306               } else {
20307                 p3 = compile(++s2,se1,depth1,0,bloc_flags); // Body only
20308                 arg4 = pos = code._width;
20309               }
20310               _cimg_mp_check_type(p2,2,1,0);
20311               arg5 = _cimg_mp_size(pos);
20312               CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
20313                                    arg4 - arg3,code._width - arg4,
20314                                    p3>=arg6 && !_cimg_mp_is_const_scalar(p3),
20315                                    p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
20316               _cimg_mp_return(p3);
20317             }
20318 
20319             if (!std::strncmp(ss,"floor(",6)) { // Floor
20320               _cimg_mp_op("Function 'floor()'");
20321               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20322               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
20323               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::floor(mem[arg1]));
20324               _cimg_mp_scalar1(mp_floor,arg1);
20325             }
20326 
20327             if (!std::strncmp(ss,"fsize(",6)) { // File size
20328               _cimg_mp_op("Function 'fsize()'");
20329               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20330               _cimg_mp_check_type(arg1,1,2,0);
20331               pos = scalar();
20332               CImg<ulongT>::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20333               return_new_comp = true;
20334               _cimg_mp_return(pos);
20335             }
20336             break;
20337 
20338           case 'g' :
20339             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
20340               _cimg_mp_op("Function 'gauss()'");
20341               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20342               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
20343               arg2 = arg3 = 1;
20344               if (s1<se1) {
20345                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20346                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20347                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
20348               }
20349               _cimg_mp_check_type(arg2,2,1,0);
20350               _cimg_mp_check_type(arg3,3,1,0);
20351               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
20352               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
20353                 val1 = mem[arg1];
20354                 val2 = mem[arg2];
20355                 _cimg_mp_const_scalar(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
20356               }
20357               _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
20358             }
20359 
20360             if (!std::strncmp(ss,"gcd(",4)) { // Gcd
20361               _cimg_mp_op("Function 'gcd()'");
20362               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20363               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20364               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
20365               _cimg_mp_check_type(arg1,1,1,0);
20366               _cimg_mp_check_type(arg2,2,1,0);
20367               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
20368                 _cimg_mp_const_scalar(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
20369               _cimg_mp_scalar2(mp_gcd,arg1,arg2);
20370             }
20371 
20372 #ifdef cimg_mp_func_get
20373             if (!std::strncmp(ss,"get(",4)) { // Get value/vector from external variable
20374               _cimg_mp_op("Function 'get()'");
20375               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20376               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20377               arg2 = arg3 = 0;
20378               if (s1<se1) {
20379                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20380                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20381                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20382               }
20383               _cimg_mp_check_type(arg1,1,2,0);
20384               _cimg_mp_check_const_scalar(arg2,2,2);
20385               _cimg_mp_check_type(arg3,3,1,0);
20386               p1 = _cimg_mp_size(arg1);
20387               arg2 = (unsigned int)mem[arg2];
20388               if (arg2) pos = vector(arg2); else pos = scalar();
20389               CImg<ulongT>::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code);
20390               return_new_comp = true;
20391               _cimg_mp_return(pos);
20392             }
20393 #endif
20394             break;
20395 
20396           case 'h' :
20397             if (*ss1=='(') { // Image height
20398               _cimg_mp_op("Function 'h()'");
20399               if (*ss2=='#') { // Index specified
20400                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
20401                 _cimg_mp_check_list(false);
20402               } else { if (ss2!=se1) break; p1 = ~0U; }
20403               _cimg_mp_scalar1(mp_image_h,p1);
20404             }
20405             break;
20406 
20407           case 'i' :
20408             if (*ss1=='c' && *ss2=='(') { // Image median
20409               _cimg_mp_op("Function 'ic()'");
20410               if (*ss3=='#') { // Index specified
20411                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
20412                 _cimg_mp_check_list(false);
20413               } else { if (ss3!=se1) break; p1 = ~0U; }
20414               pos = scalar();
20415               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
20416               return_new_comp = true;
20417               _cimg_mp_return(pos);
20418             }
20419 
20420             if (*ss1=='n' && *ss2=='(') { // Image norm
20421               _cimg_mp_op("Function 'in()'");
20422               if (*ss3=='#') { // Index specified
20423                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
20424                 _cimg_mp_check_list(false);
20425               } else { if (ss3!=se1) break; p1 = ~0U; }
20426               pos = scalar();
20427               CImg<ulongT>::vector((ulongT)mp_image_norm,pos,p1).move_to(code);
20428               return_new_comp = true;
20429               _cimg_mp_return(pos);
20430             }
20431 
20432             if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
20433               _cimg_mp_op("Function 'if()'");
20434               s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20435               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20436               arg1 = compile(ss3,s1,depth1,0,bloc_flags);
20437               _cimg_mp_check_type(arg1,1,1,0);
20438               if (_cimg_mp_is_const_scalar(arg1)) {
20439                 if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,bloc_flags);
20440                 else return s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
20441               }
20442               p2 = code._width;
20443               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20444               p3 = code._width;
20445               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):
20446                 _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
20447               _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
20448               arg4 = _cimg_mp_size(arg2);
20449               if (arg4) pos = vector(arg4); else pos = scalar();
20450               CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
20451                                   p3 - p2,code._width - p3,arg4).move_to(code,p2);
20452               return_new_comp = true;
20453               _cimg_mp_return(pos);
20454             }
20455 
20456             if (!std::strncmp(ss,"inrange(",8)) { // Check value range
20457               _cimg_mp_op("Function 'inrange()'");
20458               s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20459               arg1 = compile(ss8,s1,depth1,0,bloc_flags);
20460               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20461               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20462               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20463               arg3 = compile(++s2,s1,depth1,0,bloc_flags);
20464               arg4 = arg5 = 1;
20465               if (s1<se1) {
20466                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20467                 arg4 = compile(++s1,s2,depth1,0,bloc_flags);
20468                 arg5 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):arg4;
20469                 _cimg_mp_check_type(arg4,4,1,0);
20470                 _cimg_mp_check_type(arg5,5,1,0);
20471               }
20472               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) &&
20473                   _cimg_mp_is_const_scalar(arg3) && _cimg_mp_is_const_scalar(arg4) &&
20474                   _cimg_mp_is_const_scalar(arg5)) { // Optimize constant case
20475                 val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
20476                 if (val2>=val1)
20477                   is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val<val2));
20478                 else
20479                   is_sth = (mem[arg5]?(val>=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val<val1));
20480                 _cimg_mp_return(is_sth?1:0);
20481               }
20482               p1 = _cimg_mp_size(arg1);
20483               p2 = _cimg_mp_size(arg2);
20484               p3 = _cimg_mp_size(arg3);
20485               arg6 = ~0U; // Size of return value
20486               if (!p1) {
20487                 arg6 = p2?p2:p3;
20488                 if (arg6) _cimg_mp_check_type(arg3,3,3,arg6);
20489               } else {
20490                 arg6 = p1;
20491                 _cimg_mp_check_type(arg2,2,3,arg6);
20492                 _cimg_mp_check_type(arg3,3,3,arg6);
20493               }
20494               pos = arg6?vector(arg6):scalar();
20495               CImg<ulongT>::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code);
20496               return_new_comp = true;
20497               _cimg_mp_return(pos);
20498             }
20499 
20500             if (!std::strncmp(ss,"int(",4)) { // Integer cast
20501               _cimg_mp_op("Function 'int()'");
20502               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20503               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
20504               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((longT)mem[arg1]);
20505               _cimg_mp_scalar1(mp_int,arg1);
20506             }
20507 
20508             if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion
20509               _cimg_mp_op("Function 'invert()'");
20510               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20511               arg1 = compile(ss7,s1,depth1,0,bloc_flags);
20512               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
20513               _cimg_mp_check_type(arg2,2,1,0);
20514               if (_cimg_mp_is_vector(arg1)) {
20515                 _cimg_mp_check_matrix_square(arg1,1);
20516                 p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
20517                 pos = vector(p1*p1);
20518                 CImg<ulongT>::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code);
20519                 return_new_comp = true;
20520                 _cimg_mp_return(pos);
20521               }
20522               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(1/mem[arg1]);
20523               _cimg_mp_scalar2(mp_div,1,arg1);
20524             }
20525 
20526             if (*ss1=='s') { // Family of 'is_?()' functions
20527 
20528               if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
20529                 _cimg_mp_op("Function 'isbool()'");
20530                 if (ss7==se1) _cimg_mp_return(0);
20531                 try { arg1 = compile(ss7,se1,depth1,0,bloc_flags); }
20532                 catch(CImgException&) { _cimg_mp_return(0); }
20533                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
20534                 if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.);
20535                 _cimg_mp_scalar1(mp_isbool,arg1);
20536               }
20537 
20538               if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
20539                 _cimg_mp_op("Function 'isdir()'");
20540                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20541                 pos = scalar();
20542                 CImg<ulongT>::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20543                 return_new_comp = true;
20544                 _cimg_mp_return(pos);
20545               }
20546 
20547               if (!std::strncmp(ss,"isfile(",7)) { // Is file?
20548                 _cimg_mp_op("Function 'isfile()'");
20549                 arg1 = compile(ss7,se1,depth1,0,bloc_flags);
20550                 pos = scalar();
20551                 CImg<ulongT>::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20552                 return_new_comp = true;
20553                 _cimg_mp_return(pos);
20554               }
20555 
20556               if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
20557                 if (ss5>=se1) _cimg_mp_return(0);
20558                 _cimg_mp_op("Function 'isin()'");
20559                 pos = scalar();
20560                 CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
20561                 for (s = ss5; s<se; ++s) {
20562                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20563                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20564                   arg1 = compile(s,ns,depth1,0,bloc_flags);
20565                   if (_cimg_mp_is_vector(arg1))
20566                     CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
20567                                            arg1 + (ulongT)_cimg_mp_size(arg1)).
20568                       move_to(l_opcode);
20569                   else CImg<ulongT>::vector(arg1).move_to(l_opcode);
20570                   s = ns;
20571                 }
20572                 (l_opcode>'y').move_to(opcode);
20573                 opcode[2] = opcode._height;
20574                 opcode.move_to(code);
20575                 return_new_comp = true;
20576                 _cimg_mp_return(pos);
20577               }
20578 
20579               if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
20580                 _cimg_mp_op("Function 'isinf()'");
20581                 if (ss6==se1) _cimg_mp_return(0);
20582                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20583                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
20584                 if (_cimg_mp_is_const_scalar(arg1))
20585                   _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
20586                 _cimg_mp_scalar1(mp_isinf,arg1);
20587               }
20588 
20589               if (!std::strncmp(ss,"isint(",6)) { // Is integer?
20590                 _cimg_mp_op("Function 'isint()'");
20591                 if (ss6==se1) _cimg_mp_return(0);
20592                 try { arg1 = compile(ss6,se1,depth1,0,bloc_flags); }
20593                 catch(CImgException&) { _cimg_mp_return(0); }
20594                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
20595                 if (_cimg_mp_is_const_scalar(arg1))
20596                   _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1]));
20597                 _cimg_mp_scalar1(mp_isint,arg1);
20598               }
20599 
20600               if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
20601                 _cimg_mp_op("Function 'isnan()'");
20602                 if (ss6==se1) _cimg_mp_return(0);
20603                 arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20604                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
20605                 if (_cimg_mp_is_const_scalar(arg1))
20606                   _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
20607                 _cimg_mp_scalar1(mp_isnan,arg1);
20608               }
20609 
20610               if (!std::strncmp(ss,"isnum(",6)) { // Is number?
20611                 _cimg_mp_op("Function 'isnum()'");
20612                 val = 0;
20613                 if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
20614                 _cimg_mp_return(0);
20615               }
20616 
20617               if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression?
20618                 _cimg_mp_op("Function 'isexpr()'");
20619                 if (ss7==se1) _cimg_mp_return(0);
20620                 try { arg1 = compile(ss7,se1,depth1,0,bloc_flags); }
20621                 catch (CImgException&) { _cimg_mp_return(0); }
20622                 _cimg_mp_return(1);
20623               }
20624 
20625               if (!std::strncmp(ss,"isvarname(",10)) { // Is variable name?
20626                 _cimg_mp_op("Function 'isvarname()'");
20627                 arg1 = compile(ss + 10,se1,depth1,0,bloc_flags);
20628                 pos = scalar();
20629                 CImg<ulongT>::vector((ulongT)mp_isvarname,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20630                 return_new_comp = true;
20631                 _cimg_mp_return(pos);
20632               }
20633             }
20634             break;
20635 
20636           case 'l' :
20637             if (*ss1=='(') { // Size of image list
20638               _cimg_mp_op("Function 'l()'");
20639               if (ss2!=se1) break;
20640               _cimg_mp_scalar0(mp_list_l);
20641             }
20642 
20643             if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation
20644               _cimg_mp_op("Function 'lerp()'");
20645               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20646               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
20647               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20648               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20649               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):16; // Default value is 0.5
20650               _cimg_mp_check_type(arg3,3,1,0);
20651               if (_cimg_mp_is_const_scalar(arg3)) { // Optimize constant cases
20652                 if (!arg3) _cimg_mp_return(arg1);
20653                 if (arg3==1) _cimg_mp_return(arg2);
20654                 if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
20655                   const double t = mem[arg3];
20656                   _cimg_mp_const_scalar(mem[arg1]*(1-t) + mem[arg2]*t);
20657                 }
20658               }
20659               if (_cimg_mp_is_scalar(arg1)) {
20660                 _cimg_mp_check_type(arg2,2,1,0);
20661                 _cimg_mp_scalar3(mp_lerp,arg1,arg2,arg3);
20662               }
20663               p1 = _cimg_mp_size(arg1);
20664               _cimg_mp_check_type(arg2,2,2,p1);
20665               pos = vector(p1);
20666               CImg<ulongT>::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code);
20667               return_new_comp = true;
20668               _cimg_mp_return(pos);
20669             }
20670 
20671             if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
20672               _cimg_mp_op("Function 'log()'");
20673               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
20674               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
20675               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log(mem[arg1]));
20676               _cimg_mp_scalar1(mp_log,arg1);
20677             }
20678 
20679             if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
20680               _cimg_mp_op("Function 'log2()'");
20681               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
20682               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
20683               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::log2(mem[arg1]));
20684               _cimg_mp_scalar1(mp_log2,arg1);
20685             }
20686 
20687             if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
20688               _cimg_mp_op("Function 'log10()'");
20689               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
20690               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
20691               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log10(mem[arg1]));
20692               _cimg_mp_scalar1(mp_log10,arg1);
20693             }
20694 
20695             if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
20696               _cimg_mp_op("Function 'lowercase()'");
20697               arg1 = compile(ss + 10,se1,depth1,0,bloc_flags);
20698               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
20699               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::lowercase(mem[arg1]));
20700               _cimg_mp_scalar1(mp_lowercase,arg1);
20701             }
20702             break;
20703 
20704           case 'm' :
20705             if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
20706               _cimg_mp_op("Function 'mul()'");
20707               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20708               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
20709               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20710               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20711               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
20712               _cimg_mp_check_type(arg1,1,2,0);
20713               _cimg_mp_check_type(arg2,2,2,0);
20714               _cimg_mp_check_const_scalar(arg3,3,3);
20715               p1 = _cimg_mp_size(arg1);
20716               p2 = _cimg_mp_size(arg2);
20717               p3 = (unsigned int)mem[arg3];
20718               arg5 = p2/p3;
20719               arg4 = p1/arg5;
20720               if (arg4*arg5!=p1 || arg5*p3!=p2) {
20721                 _cimg_mp_strerr;
20722                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20723                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
20724                                             "do not match with third argument 'nb_colsB=%u', "
20725                                             "in expression '%s'.",
20726                                             pixel_type(),_cimg_mp_calling_function,s_op,
20727                                             s_type(arg1)._data,s_type(arg2)._data,p3,s0);
20728               }
20729               pos = vector(arg4*p3);
20730               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
20731               return_new_comp = true;
20732               _cimg_mp_return(pos);
20733             }
20734 
20735             if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary
20736               _cimg_mp_op("Function 'mproj()'");
20737               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20738               arg1 = compile(ss6,s1,depth1,0,bloc_flags); // S
20739               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20740               arg2 = compile(++s1,s2,depth1,0,bloc_flags); // ncolS
20741               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20742               arg3 = compile(++s2,s1,depth1,0,bloc_flags); // D
20743               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20744               arg4 = compile(++s1,s2,depth1,0,bloc_flags); // ncolD
20745               arg5 = arg6 = p3 = 0;
20746               if (s2<se1) {
20747                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20748                 arg5 = compile(++s2,s1,depth1,0,bloc_flags); // method
20749                 if (s1<se1) {
20750                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20751                   arg6 = compile(++s1,s2,depth1,0,bloc_flags); // max_iter
20752                   p3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0; // method
20753                 }
20754               }
20755               _cimg_mp_check_type(arg1,1,2,0);
20756               _cimg_mp_check_const_scalar(arg2,2,3);
20757               _cimg_mp_check_type(arg3,3,2,0);
20758               _cimg_mp_check_const_scalar(arg4,4,3);
20759               _cimg_mp_check_type(arg5,5,1,0);
20760               _cimg_mp_check_type(arg6,6,1,0);
20761               _cimg_mp_check_type(p3,7,1,0);
20762               p1 = _cimg_mp_size(arg1);
20763               p2 = _cimg_mp_size(arg3);
20764               const unsigned int
20765                 wS = (unsigned int)mem[arg2],
20766                 wD = (unsigned int)mem[arg4],
20767                 hS = p1/wS,
20768                 hD = p2/wD;
20769 
20770               if (wS*hS!=p1) {
20771                 _cimg_mp_strerr;
20772                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20773                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20774                                             "do not match with second argument 'nb_colsS=%u', "
20775                                             "in expression '%s'.",
20776                                             pixel_type(),_cimg_mp_calling_function,s_op,
20777                                             s_type(arg1)._data,wS,s0);
20778               }
20779               if (wD*hD!=p2) {
20780                 _cimg_mp_strerr;
20781                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20782                                             "CImg<%s>::%s: %s: Type of third argument ('%s') "
20783                                             "do not match with fourth argument 'nb_colsD=%u', "
20784                                             "in expression '%s'.",
20785                                             pixel_type(),_cimg_mp_calling_function,s_op,
20786                                             s_type(arg3)._data,wD,s0);
20787               }
20788               if (hS!=hD) {
20789                 _cimg_mp_strerr;
20790                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20791                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20792                                             "do not match with third argument ('%s'), "
20793                                             "in expression '%s'.",
20794                                             pixel_type(),_cimg_mp_calling_function,s_op,
20795                                             s_type(arg1)._data,s_type(arg3)._data,s0);
20796               }
20797               pos = vector(wS*wD);
20798               CImg<ulongT>::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code);
20799               return_new_comp = true;
20800               _cimg_mp_return(pos);
20801             }
20802 
20803             if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables
20804               _cimg_mp_op("Function 'merge()'");
20805               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20806               ref.assign(7);
20807               pos = compile(ss6,s1,depth1,ref,bloc_flags);
20808               if (*ref) {
20809                 _cimg_mp_strerr;
20810                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20811                                             "CImg<%s>::%s: %s: First argument cannot be a linked reference, "
20812                                             "in expression '%s'.",
20813                                             pixel_type(),_cimg_mp_calling_function,s_op,
20814                                             s0);
20815               }
20816 
20817               arg1 = ~0U; // Merge operator
20818                           // (0='=',1='+',2='-',3='*',4='/',5='&',6='|',7='xor',8='&&',9=='||',10='min',11='max')
20819               if (s1<se1) {
20820                 ++s1;
20821                 char st_op[4] = { 0 };
20822                 is_sth = false; // blank after operator?
20823                 if (cimg_sscanf(s1," %3[=+-*/&|minaxor]%c",st_op,&sep)==2 && (sep==')' ||
20824                                                                               (is_sth=cimg::is_blank(sep)))) {
20825                   if (!is_sth || (is_sth && cimg_sscanf(s1," %*[=+-*/&|minaxor ]%c",&sep)==1 && sep==')')) {
20826                     cimg::strpare(st_op,' ',false,true);
20827                     if (!st_op[1])
20828                       arg1 = *st_op=='='?0:*st_op=='+'?1:*st_op=='-'?2:*st_op=='*'?3:*st_op=='/'?4:
20829                         *st_op=='&'?5:*st_op=='|'?6:~0U;
20830                     else if (*st_op=='x' && st_op[1]=='o' && st_op[2]=='r' && !st_op[3]) arg1 = 7;
20831                     else if (*st_op=='&' && st_op[1]=='&' && !st_op[2]) arg1 = 8;
20832                     else if (*st_op=='|' && st_op[1]=='|' && !st_op[2]) arg1 = 9;
20833                     else if (*st_op=='m' && st_op[1]=='i' && st_op[2]=='n' && !st_op[3]) arg1 = 10;
20834                     else if (*st_op=='m' && st_op[1]=='a' && st_op[2]=='x' && !st_op[3]) arg1 = 11;
20835                   }
20836                 }
20837               }
20838 
20839               cimg_rofY(memmerge,k) if (memmerge(0,k)==(int)pos) {
20840                 _cimg_mp_strerr;
20841                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20842                                             "CImg<%s>::%s: %s: Merge has already been requested before "
20843                                             "for specified variable "
20844                                             "in expression '%s'.",
20845                                             pixel_type(),_cimg_mp_calling_function,s_op,s0);
20846               }
20847               if (arg1==~0U) {
20848                 _cimg_mp_strerr;
20849                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20850                                             "CImg<%s>::%s: %s: Invalid specified operator "
20851                                             "(should be one of '=,+,-,*,/,&,|,xor,&&,||,min,max'), "
20852                                             "in expression '%s'.",
20853                                             pixel_type(),_cimg_mp_calling_function,s_op,s0);
20854               }
20855               memmerge.resize(3,memmerge._height + 1,1,1,0,0);
20856               memmerge(0,memmerge._height - 1) = (int)pos;
20857               memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos);
20858               memmerge(2,memmerge._height - 1) = (int)arg1;
20859               _cimg_mp_return(pos);
20860             }
20861             break;
20862 
20863           case 'n' :
20864 #ifdef cimg_mp_func_name
20865             if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector
20866               _cimg_mp_op("Function 'name()'");
20867               if (*ss5=='#') { // Index specified
20868                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20869                 p1 = compile(ss6,s0++,depth1,0,bloc_flags);
20870                 is_sth = true; // is_index_specified?
20871                 _cimg_mp_check_list(false);
20872               } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
20873               arg1 = s0<se1?compile(s0,se1,depth1,0,bloc_flags):~0U;
20874               if (arg1!=~0U) {
20875                 _cimg_mp_check_const_scalar(arg1,is_sth?2:1,3);
20876                 arg1 = (unsigned int)mem[arg1];
20877               } else arg1 = 1024;
20878               pos = vector(arg1);
20879               CImg<ulongT>::vector((ulongT)mp_name,pos,p1,arg1).move_to(code);
20880               return_new_comp = true;
20881               _cimg_mp_return(pos);
20882             }
20883 #endif
20884 
20885             if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
20886               _cimg_mp_op("Function 'narg()'");
20887               if (ss5>=se1) _cimg_mp_return(0);
20888               arg1 = 0;
20889               for (s = ss5; s<se; ++s) {
20890                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20891                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20892                 ++arg1; s = ns;
20893               }
20894               _cimg_mp_const_scalar((double)arg1);
20895             }
20896 
20897             if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
20898                 !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
20899                 (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
20900               _cimg_mp_op("Function 'normP()'");
20901               if (*ss4=='(') { arg1 = 2; s = ss5; }
20902               else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
20903               else if (arg1==~0U) {
20904                 arg1 = compile(ss4,s++,depth1,0,bloc_flags);
20905                 _cimg_mp_check_const_scalar(arg1,0,2);
20906                 arg1 = (unsigned int)mem[arg1];
20907               } else s = std::strchr(ss4,'(') + 1;
20908               pos = scalar();
20909               switch (arg1) {
20910               case 0 :
20911                 CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break;
20912               case 1 :
20913                 CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break;
20914               case 2 :
20915                 CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break;
20916               case ~0U :
20917                 CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break;
20918               default :
20919                 CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
20920                   move_to(l_opcode);
20921               }
20922               for ( ; s<se; ++s) {
20923                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20924                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20925                 arg2 = compile(s,ns,depth1,0,bloc_flags);
20926                 if (_cimg_mp_is_vector(arg2))
20927                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20928                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20929                     move_to(l_opcode);
20930                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20931                 s = ns;
20932               }
20933 
20934               (l_opcode>'y').move_to(opcode);
20935               if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
20936                 _cimg_mp_scalar1(mp_abs,opcode[3]);
20937               opcode[2] = opcode._height;
20938               opcode.move_to(code);
20939               return_new_comp = true;
20940               _cimg_mp_return(pos);
20941             }
20942             break;
20943 
20944           case 'p' :
20945             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
20946               _cimg_mp_op("Function 'permut()'");
20947               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20948               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20949               arg1 = compile(ss7,s1,depth1,0,bloc_flags);
20950               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
20951               arg3 = compile(++s2,se1,depth1,0,bloc_flags);
20952               _cimg_mp_check_type(arg1,1,1,0);
20953               _cimg_mp_check_type(arg2,2,1,0);
20954               _cimg_mp_check_type(arg3,3,1,0);
20955               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
20956                 _cimg_mp_const_scalar(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
20957               _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
20958             }
20959 
20960             if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
20961               if (!is_inside_critical) is_parallelizable = false;
20962               _cimg_mp_op("Function 'polygon()'");
20963               if (*ss8=='#') { // Index specified
20964                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20965                 p1 = compile(ss + 9,s0++,depth1,0,bloc_flags);
20966                 _cimg_mp_check_list(true);
20967               } else { p1 = ~0U; s0 = ss8; }
20968               if (s0==se1) compile(s0,se1,depth1,0,bloc_flags); // 'missing' argument error
20969               CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
20970               for (s = s0; s<se; ++s) {
20971                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20972                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20973                 arg2 = compile(s,ns,depth1,0,bloc_flags);
20974                 if (_cimg_mp_is_vector(arg2))
20975                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20976                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20977                     move_to(l_opcode);
20978                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20979                 s = ns;
20980               }
20981               (l_opcode>'y').move_to(opcode);
20982               opcode[2] = opcode._height;
20983               opcode.move_to(code);
20984               _cimg_mp_return_nan();
20985             }
20986 
20987             if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
20988               is_sth = ss[5]=='s'; // is prints()
20989               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
20990               s0 = is_sth?ss7:ss6;
20991               if (*s0!='#' || is_sth) { // Regular expression
20992                 for (s = s0; s<se; ++s) {
20993                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20994                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20995                   pos = compile(s,ns,depth1,p_ref,bloc_flags);
20996                   c1 = *ns; *ns = 0;
20997                   variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
20998                   cimg::strpare(variable_name,false,true);
20999                   if (_cimg_mp_is_vector(pos)) // Vector
21000                     ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
21001                       variable_name)>'y').move_to(opcode);
21002                   else // Scalar
21003                     ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
21004                       variable_name)>'y').move_to(opcode);
21005                   opcode[2] = opcode._height;
21006                   opcode.move_to(code);
21007                   *ns = c1; s = ns;
21008                 }
21009                 _cimg_mp_return(pos);
21010               } else { // Image
21011                 p1 = compile(ss7,se1,depth1,0,bloc_flags);
21012                 _cimg_mp_check_list(true);
21013                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
21014                 _cimg_mp_return_nan();
21015               }
21016             }
21017 
21018             if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion
21019               _cimg_mp_op("Function 'pseudoinvert()'");
21020               s1 = ss + 13; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21021               arg1 = compile(ss + 13,s1,depth1,0,bloc_flags);
21022               arg2 = 1;
21023               arg3 = 0;
21024               if (s1<se1) {
21025                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21026                 arg2 = compile(s1,s2,depth1,0,bloc_flags);
21027                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21028               }
21029               _cimg_mp_check_type(arg1,1,2,0);
21030               _cimg_mp_check_const_scalar(arg2,2,3);
21031               _cimg_mp_check_type(arg3,3,1,0);
21032               p1 = _cimg_mp_size(arg1);
21033               p2 = (unsigned int)mem[arg2];
21034               p3 = p1/p2;
21035               if (p3*p2!=p1) {
21036                 _cimg_mp_strerr;
21037                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21038                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
21039                                             "does not match with second argument 'nb_colsA=%u', "
21040                                             "in expression '%s'.",
21041                                             pixel_type(),_cimg_mp_calling_function,s_op,
21042                                             s_type(arg1)._data,p2,s0);
21043               }
21044               pos = vector(p1);
21045               CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code);
21046               return_new_comp = true;
21047               _cimg_mp_return(pos);
21048             }
21049             break;
21050 
21051           case 'r' :
21052             if (!std::strncmp(ss,"rad2deg(",8)) { // Degrees to radians
21053               _cimg_mp_op("Function 'rad2deg()'");
21054               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
21055               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_rad2deg,arg1);
21056               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*180/cimg::PI);
21057               _cimg_mp_scalar1(mp_rad2deg,arg1);
21058             }
21059 
21060             if (!std::strncmp(ss,"ref(",4)) { // Variable declaration
21061               _cimg_mp_op("Function 'ref()'");
21062               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21063               if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,bloc_flags); // Will throw missing argument error
21064               arg3 = compile(ss4,s1++,depth1,p_ref,bloc_flags);
21065               *se1 = 0;
21066 
21067               if (!is_varname(s1)) { // Invalid variable name
21068                 variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0;
21069                 cimg::strellipsize(variable_name,64);
21070                 *se1 = ')';
21071                 _cimg_mp_strerr;
21072                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21073                                             "CImg<%s>::%s: %s: Invalid specified variable name '%s', "
21074                                             "in expression '%s'.",
21075                                             pixel_type(),_cimg_mp_calling_function,s_op,
21076                                             variable_name._data,s0);
21077               }
21078               get_variable_pos(s1,arg1,arg2);
21079               if (arg2!=~0U) reserved_label[arg2] = arg3;
21080               else if (arg1!=~0U) variable_pos[arg1] = arg3;
21081               else { // New variable
21082                 if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
21083                 variable_pos[variable_def._width] = arg3;
21084                 CImg<char>::string(s1).move_to(variable_def);
21085               }
21086               if (_cimg_mp_is_vector(arg3))
21087                 set_reserved_vector(arg3); // Prevent from being used in further optimization
21088               else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
21089               *se1 = ')';
21090               _cimg_mp_return(arg3);
21091             }
21092 
21093             if (!std::strncmp(ss,"repeat(",7)) { // Repeat
21094               _cimg_mp_op("Function 'repeat()'");
21095               s0 = ss7; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21096               arg1 = compile(ss7,s0,depth1,0,bloc_flags); // Number of iterations
21097               _cimg_mp_check_type(arg1,1,1,0);
21098               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21099               p1 = code._width;
21100 
21101               if (s1<se1) { // Version with 3 arguments
21102                 variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
21103                 cimg::strpare(variable_name,false,true);
21104                 if (!is_varname(variable_name)) { // Invalid variable name
21105                   cimg::strellipsize(variable_name,64);
21106                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21107                                               "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
21108                                               "in expression '%s'.",
21109                                               pixel_type(),_cimg_mp_calling_function,s_op,
21110                                               variable_name._data,s0);
21111                 }
21112                 get_variable_pos(variable_name,arg2,arg3);
21113                 arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
21114                 if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
21115                                   _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
21116                   cimg::strellipsize(variable_name,64);
21117                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21118                                               "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
21119                                               "(expected 'scalar'), in expression '%s'.",
21120                                               pixel_type(),_cimg_mp_calling_function,s_op,
21121                                               s_type(arg2)._data,variable_name._data,s0);
21122                 } else if (arg2==~0U) { // Variable does not exist -> create it
21123                   arg2 = scalar();
21124                   if (arg3!=~0U) reserved_label[arg3] = arg2;
21125                   else {
21126                     if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
21127                     variable_pos[variable_def._width] = arg2;
21128                     variable_name.move_to(variable_def);
21129                   }
21130                   memtype[arg2] = -1;
21131                 }
21132                 arg3 = compile(++s1,se1,depth1,0,bloc_flags);
21133               } else { // Version with 2 arguments
21134                 arg2 = ~0U;
21135                 arg3 = compile(s0,se1,depth1,0,bloc_flags);
21136               }
21137               // arg2 = variable slot, arg3 = fill expression.
21138               CImg<ulongT>::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1);
21139               _cimg_mp_return(arg3);
21140             }
21141 
21142             if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
21143               _cimg_mp_op("Function 'resize()'");
21144               if (*ss7!='#') { // Vector
21145                 for (s = ss7; s<se; ++s) {
21146                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21147                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21148                   arg2 = compile(s,ns,depth1,0,bloc_flags);
21149                   if (s!=ss7 && _cimg_mp_is_vector(arg2))
21150                     CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
21151                                            arg2 + (ulongT)_cimg_mp_size(arg2)).
21152                       move_to(l_opcode);
21153                   else CImg<ulongT>::vector(arg2).move_to(l_opcode);
21154                   s = ns;
21155                 }
21156                 (l_opcode>'y').move_to(opcode);
21157                 if (opcode.height()<2) compile(s,se1,depth1,0,bloc_flags); // Not enough arguments -> throw exception
21158                 arg1 = (unsigned int)opcode[0]; // Vector to resize
21159                 p1 = _cimg_mp_size(arg1);
21160 
21161                 if (opcode.height()<=4) { // Simple vector resize
21162                   arg2 = (unsigned int)opcode[1];
21163                   _cimg_mp_check_const_scalar(arg2,2,3);
21164                   arg2 = (unsigned int)mem[arg2];
21165                   arg3 = opcode.height()<3?1U:(unsigned int)opcode[2];
21166                   _cimg_mp_check_type(arg3,3,1,0);
21167                   arg4 = opcode.height()<4?0U:(unsigned int)opcode[3];
21168                   _cimg_mp_check_type(arg4,4,1,0);
21169                   pos = vector(arg2);
21170                   CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,p1,arg3,arg4).move_to(code);
21171                 } else { // Advanced vector resize (vector viewed as an image)
21172                   // opcode = [ A, ow,oh,od,os, nw,nh,nd,ns, interp, boundary_cond, ax,ay,az,ac ]
21173                   //          [ 0   1  2  3  4   5  6  7  8       9             10  11 12 13 14 ]
21174 
21175                   if (opcode.height()<6) compile(s,se1,depth1,0,bloc_flags); // Not enough arguments -> throw exception
21176                   p2 = opcode.height();
21177                   opcode.resize(1,15,1,1,0);
21178                   if (p2<7) opcode[6] = opcode[2];
21179                   if (p2<8) opcode[7] = opcode[3];
21180                   if (p2<9) opcode[8] = opcode[4];
21181                   if (p2<10) opcode[9] = 1;
21182                   _cimg_mp_check_const_scalar(opcode[1],2,3);
21183                   _cimg_mp_check_const_scalar(opcode[2],3,3);
21184                   _cimg_mp_check_const_scalar(opcode[3],4,3);
21185                   _cimg_mp_check_const_scalar(opcode[4],5,3);
21186                   _cimg_mp_check_const_scalar(opcode[5],6,3);
21187                   _cimg_mp_check_const_scalar(opcode[6],7,3);
21188                   _cimg_mp_check_const_scalar(opcode[7],8,3);
21189                   _cimg_mp_check_const_scalar(opcode[8],9,3);
21190                   arg2 = (unsigned int)mem[opcode[1]]; opcode[1] = arg2;
21191                   arg3 = (unsigned int)mem[opcode[2]]; opcode[2] = arg3;
21192                   arg4 = (unsigned int)mem[opcode[3]]; opcode[3] = arg4;
21193                   arg5 = (unsigned int)mem[opcode[4]]; opcode[4] = arg5;
21194                   if (arg2*arg3*arg4*arg5!=std::max(1U,p1))
21195                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
21196                                                 "CImg<%s>::%s: %s: Input size (%lu values) and specified input vector "
21197                                                 "geometry (%u,%u,%u,%u) (%lu values) do not match.",
21198                                                 pixel_type(),_cimg_mp_calling_function,s_op,
21199                                                 std::max(p1,1U),arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5);
21200                   arg2 = (unsigned int)mem[opcode[5]]; opcode[5] = arg2;
21201                   arg3 = (unsigned int)mem[opcode[6]]; opcode[6] = arg3;
21202                   arg4 = (unsigned int)mem[opcode[7]]; opcode[7] = arg4;
21203                   arg5 = (unsigned int)mem[opcode[8]]; opcode[8] = arg5;
21204                   pos = vector(arg2*arg3*arg4*arg5);
21205                   opcode.resize(1,18,1,1,0,0,0,1);
21206                   opcode[0] = (ulongT)mp_vector_resize_ext;
21207                   opcode[1] = (ulongT)pos;
21208                   opcode[2] = (ulongT)p1;
21209                   opcode.move_to(code);
21210                 }
21211                 return_new_comp = true;
21212                 _cimg_mp_return(pos);
21213 
21214               } else { // Image
21215                 if (!is_inside_critical) is_parallelizable = false;
21216                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21217                 p1 = compile(ss8,s0++,depth1,0,bloc_flags);
21218                 _cimg_mp_check_list(true);
21219                 l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
21220                 CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
21221                   move_to(l_opcode);
21222                 pos = 0;
21223                 for (s = s0; s<se && pos<10; ++s) {
21224                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21225                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21226                   arg1 = compile(s,ns,depth1,0,bloc_flags);
21227                   _cimg_mp_check_type(arg1,pos + 2,1,0);
21228                   l_opcode(0,pos + 3) = arg1;
21229                   s = ns;
21230                   ++pos;
21231                 }
21232                 if (pos<1 || pos>10) {
21233                   _cimg_mp_strerr;
21234                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21235                                               "CImg<%s>::%s: %s: %s arguments, in expression '%s'.",
21236                                               pixel_type(),_cimg_mp_calling_function,s_op,
21237                                               pos<1?"Missing":"Too much",s0);
21238                 }
21239                 l_opcode[0].move_to(code);
21240                 _cimg_mp_return_nan();
21241               }
21242             }
21243 
21244             if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
21245               _cimg_mp_op("Function 'reverse()'");
21246               arg1 = compile(ss8,se1,depth1,0,bloc_flags);
21247               if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
21248               p1 = _cimg_mp_size(arg1);
21249               pos = vector(p1);
21250               CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
21251               return_new_comp = true;
21252               _cimg_mp_return(pos);
21253             }
21254 
21255             if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
21256               _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
21257               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
21258               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21259               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21260               _cimg_mp_check_type(arg2,2,1,0);
21261               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
21262               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
21263                 _cimg_mp_const_scalar(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
21264                                   cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
21265               _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
21266             }
21267 
21268             if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix
21269               _cimg_mp_op("Function 'rot()'");
21270               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21271               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21272               if (s1<se1) { // 3D rotation
21273                 _cimg_mp_check_type(arg1,1,3,3);
21274                 is_sth = false; // Is coordinates as vector?
21275                 if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
21276                   is_sth = true;
21277                   p2 = _cimg_mp_size(arg1);
21278                   ++arg1;
21279                   arg2 = arg3 = 0;
21280                   if (p2>1) {
21281                     arg2 = arg1 + 1;
21282                     if (p2>2) arg3 = arg2 + 1;
21283                   }
21284                   arg4 = compile(++s1,se1,depth1,0,bloc_flags);
21285                 } else {
21286                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21287                   arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21288                   s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
21289                   arg3 = compile(++s2,s3,depth1,0,bloc_flags);
21290                   arg4 = compile(++s3,se1,depth1,0,bloc_flags);
21291                   _cimg_mp_check_type(arg2,2,1,0);
21292                   _cimg_mp_check_type(arg3,3,1,0);
21293                 }
21294                 _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
21295                 pos = vector(9);
21296                 CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
21297               } else { // 2D rotation
21298                 _cimg_mp_check_type(arg1,1,1,0);
21299                 pos = vector(4);
21300                 CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
21301               }
21302               return_new_comp = true;
21303               _cimg_mp_return(pos);
21304             }
21305 
21306             if (!std::strncmp(ss,"round(",6)) { // Value rounding
21307               _cimg_mp_op("Function 'round()'");
21308               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21309               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21310               arg2 = 1;
21311               arg3 = 0;
21312               if (s1<se1) {
21313                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21314                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21315                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21316               }
21317               _cimg_mp_check_type(arg2,2,1,0);
21318               _cimg_mp_check_type(arg3,3,1,0);
21319               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
21320               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
21321                 _cimg_mp_const_scalar(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
21322               _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
21323             }
21324 
21325 #ifdef cimg_mp_func_run
21326             if (!std::strncmp(ss,"run(",4)) { // Run external command
21327               _cimg_mp_op("Function 'run()'");
21328               if (!is_inside_critical) is_parallelizable = false;
21329               CImg<ulongT>::vector((ulongT)mp_run,0,0).move_to(l_opcode);
21330               pos = 1;
21331               for (s = ss4; s<se; ++s) {
21332                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21333                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21334                 arg1 = compile(s,ns,depth1,0,bloc_flags);
21335                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
21336                 s = ns;
21337               }
21338               (l_opcode>'y').move_to(opcode);
21339               pos = scalar();
21340               opcode[1] = pos;
21341               opcode[2] = opcode._height;
21342               opcode.move_to(code);
21343               return_new_comp = true;
21344               _cimg_mp_return(pos);
21345             }
21346 #endif
21347             break;
21348 
21349           case 's' :
21350             if (*ss1=='(') { // Image spectrum
21351               _cimg_mp_op("Function 's()'");
21352               if (*ss2=='#') { // Index specified
21353                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
21354                 _cimg_mp_check_list(false);
21355               } else { if (ss2!=se1) break; p1 = ~0U; }
21356               _cimg_mp_scalar1(mp_image_s,p1);
21357             }
21358 
21359             if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
21360               _cimg_mp_op("Function 'same()'");
21361               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21362               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21363               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21364               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21365               arg3 = 11;
21366               arg4 = 1;
21367               if (s2<se1) {
21368                 s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
21369                 arg3 = compile(++s2,s3,depth1,0,bloc_flags);
21370                 _cimg_mp_check_type(arg3,3,1,0);
21371                 arg4 = s3<se1?compile(++s3,se1,depth1,0,bloc_flags):1;
21372               }
21373               p1 = _cimg_mp_size(arg1);
21374               p2 = _cimg_mp_size(arg2);
21375               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
21376             }
21377 
21378 #ifdef cimg_mp_func_set
21379             if (!std::strncmp(ss,"set(",4)) { // Set value/vector to external variable
21380               _cimg_mp_op("Function 'set()'");
21381               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21382               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21383               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
21384               _cimg_mp_check_type(arg2,2,2,0);
21385               p1 = _cimg_mp_size(arg1);
21386               p2 = _cimg_mp_size(arg2);
21387               CImg<ulongT>::vector((ulongT)mp_set,arg1,p1,arg2,p2).move_to(code);
21388               _cimg_mp_return(arg1);
21389             }
21390 #endif
21391 
21392             if (!std::strncmp(ss,"shift(",6)) { // Shift vector
21393               _cimg_mp_op("Function 'shift()'");
21394               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21395               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21396               arg2 = 1; arg3 = 0;
21397               if (s1<se1) {
21398                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21399                 arg2 = compile(s1,s0,depth1,0,bloc_flags);
21400                 arg3 = s0<se1?compile(++s0,se1,depth1,0,bloc_flags):0;
21401               }
21402               _cimg_mp_check_type(arg1,1,2,0);
21403               _cimg_mp_check_type(arg2,2,1,0);
21404               _cimg_mp_check_type(arg3,3,1,0);
21405               p1 = _cimg_mp_size(arg1);
21406               pos = vector(p1);
21407               CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
21408               return_new_comp = true;
21409               _cimg_mp_return(pos);
21410             }
21411 
21412             if (!std::strncmp(ss,"sign(",5)) { // Sign
21413               _cimg_mp_op("Function 'sign()'");
21414               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21415               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
21416               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sign(mem[arg1]));
21417               _cimg_mp_scalar1(mp_sign,arg1);
21418             }
21419 
21420             if (!std::strncmp(ss,"sin(",4)) { // Sine
21421               _cimg_mp_op("Function 'sin()'");
21422               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21423               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
21424               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sin(mem[arg1]));
21425               _cimg_mp_scalar1(mp_sin,arg1);
21426             }
21427 
21428             if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
21429               _cimg_mp_op("Function 'sinc()'");
21430               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21431               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
21432               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sinc(mem[arg1]));
21433               _cimg_mp_scalar1(mp_sinc,arg1);
21434             }
21435 
21436             if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
21437               _cimg_mp_op("Function 'sinh()'");
21438               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21439               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
21440               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sinh(mem[arg1]));
21441               _cimg_mp_scalar1(mp_sinh,arg1);
21442             }
21443 
21444             if (!std::strncmp(ss,"size(",5)) { // Vector size
21445               _cimg_mp_op("Function 'size()'");
21446               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21447               _cimg_mp_const_scalar(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
21448             }
21449 
21450             if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system
21451               _cimg_mp_op("Function 'solve()'");
21452               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21453               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21454               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21455               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21456               arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):1;
21457               _cimg_mp_check_type(arg1,1,2,0);
21458               _cimg_mp_check_type(arg2,2,2,0);
21459               _cimg_mp_check_const_scalar(arg3,3,3);
21460               p1 = _cimg_mp_size(arg1);
21461               p2 = _cimg_mp_size(arg2);
21462               p3 = (unsigned int)mem[arg3];
21463               arg5 = p2/p3;
21464               arg4 = p1/arg5;
21465               if (arg4*arg5!=p1 || arg5*p3!=p2) {
21466                 _cimg_mp_strerr;
21467                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21468                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
21469                                             "do not match with third argument 'nb_colsB=%u', "
21470                                             "in expression '%s'.",
21471                                             pixel_type(),_cimg_mp_calling_function,s_op,
21472                                             s_type(arg1)._data,s_type(arg2)._data,p3,s0);
21473               }
21474               pos = vector(arg4*p3);
21475               CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
21476               return_new_comp = true;
21477               _cimg_mp_return(pos);
21478             }
21479 
21480             if (!std::strncmp(ss,"sort(",5)) { // Sort vector
21481               _cimg_mp_op("Function 'sort()'");
21482               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21483               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21484               arg2 = arg4 = 1; arg3 = ~0U;
21485               if (s1<se1) {
21486                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21487                 arg2 = compile(s1,s0,depth1,0,bloc_flags);
21488                 if (s0<se1) {
21489                   s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21490                   arg3 = compile(s0,s1,depth1,0,bloc_flags);
21491                   arg4 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21492                 }
21493               }
21494               _cimg_mp_check_type(arg1,1,2,0);
21495               _cimg_mp_check_type(arg2,2,1,0);
21496               if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
21497               _cimg_mp_check_type(arg4,4,1,0);
21498               p1 = _cimg_mp_size(arg1);
21499               pos = vector(p1);
21500               CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
21501               return_new_comp = true;
21502               _cimg_mp_return(pos);
21503             }
21504 
21505             if (!std::strncmp(ss,"sqr(",4)) { // Square
21506               _cimg_mp_op("Function 'sqr()'");
21507               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21508               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
21509               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sqr(mem[arg1]));
21510               _cimg_mp_scalar1(mp_sqr,arg1);
21511             }
21512 
21513             if (!std::strncmp(ss,"sqrt(",5)) { // Square root
21514               _cimg_mp_op("Function 'sqrt()'");
21515               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21516               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
21517               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sqrt(mem[arg1]));
21518               _cimg_mp_scalar1(mp_sqrt,arg1);
21519             }
21520 
21521             if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
21522               _cimg_mp_op("Function 'srand()'");
21523               arg1 = ss6<se1?compile(ss6,se1,depth1,0,bloc_flags):~0U;
21524               if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
21525               _cimg_mp_scalar0(mp_srand0);
21526             }
21527 
21528             if (!std::strncmp(ss,"stats(",6)) { // Image statistics
21529               _cimg_mp_op("Function 'stats()'");
21530               if (*ss6=='#') { // Index specified
21531                 p1 = compile(ss7,se1,depth1,0,bloc_flags);
21532                 _cimg_mp_check_list(false);
21533               } else { if (ss6!=se1) break; p1 = ~0U; }
21534               pos = vector(14);
21535               CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
21536               return_new_comp = true;
21537               _cimg_mp_return(pos);
21538             }
21539 
21540 #ifdef cimg_mp_func_store
21541             if (!std::strncmp(ss,"store(",6)) { // Store vector to variable
21542               _cimg_mp_op("Function 'store()'");
21543               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21544               arg1 = compile(ss6,s1,depth1,0,bloc_flags);
21545               p1 = _cimg_mp_size(arg1);
21546               p3 = std::max(1U,p1);
21547               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21548               arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21549               _cimg_mp_check_type(arg2,2,2,0);
21550               p2 = _cimg_mp_size(arg2);
21551               arg3 = ~0U; arg4 = arg5 = arg6 = 1U; pos = 0;
21552               if (s2<se1) {
21553                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21554                 arg3 = compile(++s2,s1,depth1,0,bloc_flags);
21555                 _cimg_mp_check_type(arg3,3,1,0);
21556                 arg4 = arg5 = arg6 = 1U;
21557                 if (s1<se1) {
21558                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21559                   arg4 = compile(++s1,s2,depth1,0,bloc_flags);
21560                   _cimg_mp_check_type(arg4,4,1,0);
21561                   if (s2<se1) {
21562                     s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21563                     arg5 = compile(++s2,s1,depth1,0,bloc_flags);
21564                     _cimg_mp_check_type(arg5,5,1,0);
21565                     if (s1<se1) {
21566                       s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21567                       arg6 = compile(++s1,s2,depth1,0,bloc_flags);
21568                       pos = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21569                       _cimg_mp_check_type(arg6,6,1,0);
21570                       _cimg_mp_check_type(pos,7,1,0);
21571                     }
21572                   }
21573                 }
21574               }
21575               if (arg3==~0U) arg3 = const_scalar(p3);
21576               CImg<ulongT>::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2,
21577                                    arg3,arg4,arg5,arg6,pos).move_to(code);
21578               _cimg_mp_return_nan();
21579             }
21580 #endif
21581 
21582             if (!std::strncmp(ss,"stov(",5)) { // String to double
21583               _cimg_mp_op("Function 'stov()'");
21584               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21585               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
21586               arg2 = arg3 = 0;
21587               if (s1<se1) {
21588                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21589                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
21590                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):0;
21591               }
21592               _cimg_mp_check_type(arg2,2,1,0);
21593               _cimg_mp_check_type(arg3,3,1,0);
21594               p1 = _cimg_mp_size(arg1);
21595               pos = scalar();
21596               CImg<ulongT>::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code);
21597               return_new_comp = true;
21598               _cimg_mp_return(pos);
21599             }
21600 
21601             if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments
21602               _cimg_mp_op("Function 'string()'");
21603               CImg<ulongT>::vector((ulongT)mp_string,0,0,0).move_to(l_opcode);
21604 
21605               if (*ss7=='#') { // Output vector size specified, with '#'
21606                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21607                 arg1 = compile(ss8,s0++,depth1,0,bloc_flags);
21608                 _cimg_mp_check_const_scalar(arg1,1,3);
21609                 arg1 = (unsigned int)mem[arg1];
21610                 s = s0;
21611               } else { arg1=~0U; s = ss7; }
21612 
21613               p1 = 0;
21614               for (; s<se; ++s) {
21615                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21616                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21617                 arg2 = compile(s,ns,depth1,0,bloc_flags);
21618                 p2 = _cimg_mp_size(arg2);
21619                 if (p2) p1+=p2;
21620                 else {
21621                   if (_cimg_mp_is_const_scalar(arg2)) p1+=cimg_snprintf(variable_name.assign(24),24,"%.17g",mem[arg2]);
21622                   else p1+=23;
21623                 }
21624                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21625                 s = ns;
21626               }
21627               if (arg1==~0U) arg1 = p1;
21628               pos = vector(arg1,0);
21629               (l_opcode>'y').move_to(opcode);
21630               opcode[1] = pos;
21631               opcode[2] = arg1;
21632               opcode[3] = opcode._height;
21633               opcode.move_to(code);
21634               return_new_comp = true;
21635               _cimg_mp_return(pos);
21636             }
21637 
21638             if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
21639               _cimg_mp_op("Function 'svd()'");
21640               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21641               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
21642               arg2 = s1<se1?compile(++s1,se1,depth1,0,bloc_flags):1;
21643               _cimg_mp_check_type(arg1,1,2,0);
21644               _cimg_mp_check_const_scalar(arg2,2,3);
21645               p1 = _cimg_mp_size(arg1);
21646               p2 = (unsigned int)mem[arg2];
21647               p3 = p1/p2;
21648               if (p3*p2!=p1) {
21649                 _cimg_mp_strerr;
21650                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21651                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
21652                                             "does not match with second argument 'nb_colsA=%u', "
21653                                             "in expression '%s'.",
21654                                             pixel_type(),_cimg_mp_calling_function,s_op,
21655                                             s_type(arg1)._data,p2,s0);
21656               }
21657               pos = vector(p1 + p2 + p2*p2);
21658               CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
21659               return_new_comp = true;
21660               _cimg_mp_return(pos);
21661             }
21662 
21663             if (!std::strncmp(ss,"swap(",5)) { // Swap values
21664               _cimg_mp_op("Function 'swap()'");
21665               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21666               ref.assign(14);
21667               arg1 = compile(ss5,s1,depth1,ref,bloc_flags);
21668               arg2 = compile(++s1,se1,depth1,ref._data + 7,bloc_flags);
21669               p1 = _cimg_mp_size(arg1);
21670               _cimg_mp_check_type(arg2,2,p1?2:1,p1);
21671               if (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_const_scalar(arg2)) {
21672                 _cimg_mp_strerr;
21673                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21674                                             "CImg<%s>::%s: %s: %s argument cannot be a constant, "
21675                                             "in expression '%s'.",
21676                                             pixel_type(),_cimg_mp_calling_function,s_op,
21677                                             _cimg_mp_is_const_scalar(arg1)?"First":"Second",s0);
21678               }
21679               CImg<ulongT>::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code);
21680 
21681               // Write back values of linked arg1 and arg2.
21682               const unsigned int *_ref = ref;
21683               is_sth = true; // Is first argument?
21684               do {
21685                 switch (*_ref) {
21686                 case 1 : // arg1: V[k]
21687                   arg3 = _ref[1]; // Vector slot
21688                   arg4 = _ref[2]; // Index
21689                   CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
21690                     move_to(code);
21691                   break;
21692                 case 2 : // arg1: i/j[_#ind,off]
21693                   if (!is_inside_critical) is_parallelizable = false;
21694                   p1 = _ref[1]; // Index
21695                   is_relative = (bool)_ref[2];
21696                   arg3 = _ref[3]; // Offset
21697                   if (p1!=~0U) {
21698                     if (listout)
21699                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
21700                                            arg1,p1,arg3).move_to(code);
21701                   } else {
21702                     if (imgout)
21703                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
21704                                            arg1,arg3).move_to(code);
21705                   }
21706                   break;
21707                 case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c)
21708                   if (!is_inside_critical) is_parallelizable = false;
21709                   p1 = _ref[1]; // Index
21710                   is_relative = (bool)_ref[2];
21711                   arg3 = _ref[3]; // X
21712                   arg4 = _ref[4]; // Y
21713                   arg5 = _ref[5]; // Z
21714                   arg6 = _ref[6]; // C
21715                   if (p1!=~0U) {
21716                     if (listout)
21717                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
21718                                            arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
21719                   } else {
21720                     if (imgout)
21721                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
21722                                            arg1,arg3,arg4,arg5,arg6).move_to(code);
21723                   }
21724                   break;
21725               case 4: // arg1: I/J[_#ind,off]
21726                 if (!is_inside_critical) is_parallelizable = false;
21727                 p1 = _ref[1]; // Index
21728                 is_relative = (bool)_ref[2];
21729                 arg3 = _ref[3]; // Offset
21730                 if (p1!=~0U) {
21731                   if (listout) {
21732                     if (_cimg_mp_is_scalar(arg1))
21733                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
21734                                            arg1,p1,arg3).move_to(code);
21735                     else {
21736                       _cimg_mp_check_const_index(p1);
21737                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
21738                                            arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
21739                     }
21740                   }
21741                 } else {
21742                   if (imgout) {
21743                     if (_cimg_mp_is_scalar(arg1))
21744                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
21745                                            arg1,arg3).move_to(code);
21746                     else
21747                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
21748                                            arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
21749                   }
21750                 }
21751                 break;
21752                 case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c)
21753                   if (!is_inside_critical) is_parallelizable = false;
21754                   p1 = _ref[1]; // Index
21755                   is_relative = (bool)_ref[2];
21756                   arg3 = _ref[3]; // X
21757                   arg4 = _ref[4]; // Y
21758                   arg5 = _ref[5]; // Z
21759                   if (p1!=~0U) {
21760                     if (listout) {
21761                       if (_cimg_mp_is_scalar(arg1))
21762                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
21763                                              arg1,p1,arg3,arg4,arg5).move_to(code);
21764                       else {
21765                         _cimg_mp_check_const_index(p1);
21766                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
21767                                              arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21768                       }
21769                     }
21770                   } else {
21771                     if (imgout) {
21772                       if (_cimg_mp_is_scalar(arg1))
21773                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
21774                                              arg1,arg3,arg4,arg5).move_to(code);
21775                       else
21776                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
21777                                              arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21778                     }
21779                   }
21780                   break;
21781                 }
21782 
21783                 _ref+=7;
21784                 arg1 = arg2;
21785                 is_sth = !is_sth;
21786               } while (!is_sth);
21787 
21788               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
21789               _cimg_mp_return(arg1);
21790             }
21791             break;
21792 
21793           case 't' :
21794             if (!std::strncmp(ss,"tan(",4)) { // Tangent
21795               _cimg_mp_op("Function 'tan()'");
21796               arg1 = compile(ss4,se1,depth1,0,bloc_flags);
21797               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
21798               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tan(mem[arg1]));
21799               _cimg_mp_scalar1(mp_tan,arg1);
21800             }
21801 
21802             if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
21803               _cimg_mp_op("Function 'tanh()'");
21804               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21805               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
21806               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tanh(mem[arg1]));
21807               _cimg_mp_scalar1(mp_tanh,arg1);
21808             }
21809 
21810             if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
21811               _cimg_mp_op("Function 'trace()'");
21812               arg1 = compile(ss6,se1,depth1,0,bloc_flags);
21813               _cimg_mp_check_matrix_square(arg1,1);
21814               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
21815               _cimg_mp_scalar2(mp_trace,arg1,p1);
21816             }
21817 
21818             if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose
21819               _cimg_mp_op("Function 'transpose()'");
21820               s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21821               arg1 = compile(ss + 10,s1,depth1,0,bloc_flags);
21822               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
21823               _cimg_mp_check_type(arg1,1,2,0);
21824               _cimg_mp_check_const_scalar(arg2,2,3);
21825               p1 = _cimg_mp_size(arg1);
21826               p2 = (unsigned int)mem[arg2];
21827               p3 = p1/p2;
21828               if (p2*p3!=p1) {
21829                 _cimg_mp_strerr;
21830                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21831                                             "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
21832                                             "second argument 'nb_cols=%u', in expression '%s'.",
21833                                             pixel_type(),_cimg_mp_calling_function,s_op,
21834                                             s_type(arg1)._data,p2,s0);
21835               }
21836               pos = vector(p3*p2);
21837               CImg<ulongT>::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code);
21838               return_new_comp = true;
21839               _cimg_mp_return(pos);
21840             }
21841             break;
21842 
21843           case 'u' :
21844             if (*ss1=='(') { // Random value with uniform distribution
21845               _cimg_mp_op("Function 'u()'");
21846               if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
21847               s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21848               arg1 = compile(ss2,s1,depth1,0,bloc_flags);
21849               if (s1<se1) arg2 = compile(++s1,se1,depth1,0,bloc_flags); else { arg2 = arg1; arg1 = 0; }
21850               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
21851               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
21852               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
21853               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
21854               _cimg_mp_scalar2(mp_u,arg1,arg2);
21855             }
21856 
21857             if (!std::strncmp(ss,"ui2f(",5)) { // Special uint->float conversion
21858               _cimg_mp_op("Function 'ui2f()'");
21859               arg1 = compile(ss5,se1,depth1,0,bloc_flags);
21860               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1);
21861               if (_cimg_mp_is_const_scalar(arg1))
21862                 _cimg_mp_const_scalar((double)cimg::uint2float((unsigned int)mem[arg1]));
21863               _cimg_mp_scalar1(mp_ui2f,arg1);
21864             }
21865 
21866             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
21867               _cimg_mp_op("Function 'unref()'");
21868               arg1=~0U;
21869               for (s0 = ss6; s0<se1; s0 = s1) {
21870                 if (s0>ss6 && *s0==',') ++s0;
21871                 s1 = s0; while (s1<se1 && *s1!=',') ++s1;
21872                 c1 = *s1;
21873                 if (s1>s0) {
21874                   *s1 = 0;
21875                   get_variable_pos(s0,arg1,arg2);
21876                   if (arg2!=~0U) reserved_label[arg2] = ~0U;
21877                   else if (arg1!=~0U) {
21878                     variable_def.remove(arg1);
21879                     if (arg1<variable_pos._width - 1)
21880                       std::memmove(variable_pos._data + arg1,variable_pos._data + arg1 + 1,
21881                                    sizeof(uintT)*(variable_pos._width - arg1 - 1));
21882                     --variable_pos._width;
21883                   }
21884                   *s1 = c1;
21885                 } else compile(s0,s1,depth1,0,bloc_flags); // Will throw a 'missing argument' exception
21886               }
21887               _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable
21888             }
21889 
21890             if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
21891               _cimg_mp_op("Function 'uppercase()'");
21892               arg1 = compile(ss + 10,se1,depth1,0,bloc_flags);
21893               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
21894               if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::uppercase(mem[arg1]));
21895               _cimg_mp_scalar1(mp_uppercase,arg1);
21896             }
21897             break;
21898 
21899           case 'v' :
21900             if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
21901                 !std::strncmp(ss,"vector(",7) ||
21902                 (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
21903               _cimg_mp_op("Function 'vector()'");
21904               arg2 = 0; // Number of specified values
21905               if (arg1==~0U && *ss6!='(') {
21906                 arg1 = compile(ss6,s++,depth1,0,bloc_flags);
21907                 _cimg_mp_check_const_scalar(arg1,0,3);
21908                 arg1 = (unsigned int)mem[arg1];
21909               } else s = std::strchr(ss6,'(') + 1;
21910 
21911               if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#'
21912                 s0 = ++s; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21913                 arg1 = compile(s,s0++,depth1,0,bloc_flags);
21914                 _cimg_mp_check_const_scalar(arg1,1,3);
21915                 arg1 = (unsigned int)mem[arg1];
21916                 s = s0;
21917               }
21918 
21919               if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
21920                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21921                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21922                   arg3 = compile(s,ns,depth1,0,bloc_flags);
21923                   if (_cimg_mp_is_vector(arg3)) {
21924                     arg4 = _cimg_mp_size(arg3);
21925                     CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
21926                     arg2+=arg4;
21927                   } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
21928                   s = ns;
21929                 }
21930               if (arg1==~0U) arg1 = arg2;
21931               if (!arg1) _cimg_mp_return(0);
21932               pos = vector(arg1);
21933               l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
21934               (l_opcode>'y').move_to(opcode);
21935               opcode[2] = opcode._height;
21936               opcode.move_to(code);
21937               return_new_comp = true;
21938               _cimg_mp_return(pos);
21939             }
21940 
21941             if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) ||
21942                 !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) ||
21943                 !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) ||
21944                 !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) ||
21945                 !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) ||
21946                 !std::strncmp(ss,"vprod(",6) ||
21947                 !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) ||
21948                 !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) ||
21949                 !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions
21950               _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'":
21951                                       ss[4]=='k'?"Function 'vargkth()'":
21952                                       ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'":
21953                                       ss[5]=='i'?"Function vargminabs()'":
21954                                       ss[7]=='('?"Function 'vargmax()'":
21955                                       "Function 'vargmaxabs()'"):
21956                           ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"):
21957                           ss[1]=='k'?"Function 'vkth()'":
21958                           ss[1]=='p'?"Function 'vprod()'":
21959                           ss[1]=='v'?"Function 'vvar()'":
21960                           ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'":
21961                                       "Function 'vminabs()'"):
21962                           ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'":
21963                                       "Function 'vmaxabs()'"):
21964                           "Function 'vmed()'");
21965               op = ss[1]=='a'?(ss[2]=='v'?mp_vavg:
21966                                ss[4]=='k'?mp_vargkth:
21967                                ss[5]=='i' && ss[7]=='('?mp_vargmin:
21968                                ss[5]=='i'?mp_vargminabs:
21969                                ss[7]=='('?mp_vargmax:mp_vargmaxabs):
21970                 ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd):
21971                 ss[1]=='k'?mp_vkth:
21972                 ss[1]=='p'?mp_vprod:
21973                 ss[1]=='v'?mp_vvar:
21974                 ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs):
21975                 ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs):
21976                 mp_vmedian;
21977               CImg<ulongT>::vector((ulongT)op,0,0,0).move_to(l_opcode);
21978               p1 = ~0U;
21979               p3 = 1;
21980               for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
21981                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21982                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21983                 arg2 = compile(s,ns,depth1,0,bloc_flags);
21984                 p2 = _cimg_mp_size(arg2);
21985                 if (p1==~0U) { if (_cimg_mp_is_vector(arg2)) p1 = p2; }
21986                 else _cimg_mp_check_type(arg2,p3,3,p1);
21987                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21988                 s = ns;
21989                 ++p3;
21990               }
21991               (l_opcode>'y').move_to(opcode);
21992               if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1);
21993               opcode[1] = pos;
21994               opcode[2] = p1;
21995               opcode[3] = opcode._height;
21996               opcode.move_to(code);
21997               return_new_comp = true;
21998               _cimg_mp_return(pos);
21999             }
22000 
22001             if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
22002               _cimg_mp_op("Function 'vtos()'");
22003               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
22004               arg1 = compile(ss5,s1,depth1,0,bloc_flags);
22005               arg2 = 0; arg3 = ~0U;
22006               if (s1<se1) {
22007                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
22008                 arg2 = compile(++s1,s2,depth1,0,bloc_flags);
22009                 arg3 = s2<se1?compile(++s2,se1,depth1,0,bloc_flags):~0U;
22010               }
22011               _cimg_mp_check_type(arg2,2,1,0);
22012               if (arg3==~0U) { // Auto-guess best output vector size
22013                 p1 = _cimg_mp_size(arg1);
22014                 p1 = p1?25*p1 - 1:24;
22015               } else {
22016                 _cimg_mp_check_const_scalar(arg3,3,3);
22017                 p1 = (unsigned int)mem[arg3];
22018               }
22019               pos = vector(p1);
22020               CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
22021               return_new_comp = true;
22022               _cimg_mp_return(pos);
22023             }
22024             break;
22025 
22026           case 'w' :
22027             if (*ss1=='(') { // Image width
22028               _cimg_mp_op("Function 'w()'");
22029               if (*ss2=='#') { // Index specified
22030                 p1 = compile(ss3,se1,depth1,0,bloc_flags);
22031                 _cimg_mp_check_list(false);
22032               } else { if (ss2!=se1) break; p1 = ~0U; }
22033               _cimg_mp_scalar1(mp_image_w,p1);
22034             }
22035 
22036             if (*ss1=='h' && *ss2=='(') { // Image width*height
22037               _cimg_mp_op("Function 'wh()'");
22038               if (*ss3=='#') { // Index specified
22039                 p1 = compile(ss4,se1,depth1,0,bloc_flags);
22040                 _cimg_mp_check_list(false);
22041               } else { if (ss3!=se1) break; p1 = ~0U; }
22042               _cimg_mp_scalar1(mp_image_wh,p1);
22043             }
22044 
22045             if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
22046               _cimg_mp_op("Function 'whd()'");
22047               if (*ss4=='#') { // Index specified
22048                 p1 = compile(ss5,se1,depth1,0,bloc_flags);
22049                 _cimg_mp_check_list(false);
22050               } else { if (ss4!=se1) break; p1 = ~0U; }
22051               _cimg_mp_scalar1(mp_image_whd,p1);
22052             }
22053 
22054             if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
22055               _cimg_mp_op("Function 'whds()'");
22056               if (*ss5=='#') { // Index specified
22057                 p1 = compile(ss6,se1,depth1,0,bloc_flags);
22058                 _cimg_mp_check_list(false);
22059               } else { if (ss5!=se1) break; p1 = ~0U; }
22060               _cimg_mp_scalar1(mp_image_whds,p1);
22061             }
22062 
22063             if (!std::strncmp(ss,"while(",6)) { // While...do
22064               _cimg_mp_op("Function 'while()'");
22065               s0 = *ss5=='('?ss6:ss8;
22066               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
22067               p1 = code._width;
22068               arg1 = compile(s0,s1,depth1,0,bloc_flags);
22069               p2 = code._width;
22070               arg6 = mempos;
22071               pos = compile(++s1,se1,depth1,0,bloc_flags);
22072               _cimg_mp_check_type(arg1,1,1,0);
22073               arg2 = _cimg_mp_size(pos);
22074               CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2,
22075                                    pos>=arg6 && !_cimg_mp_is_const_scalar(pos),
22076                                    arg1>=arg6 && !_cimg_mp_is_const_scalar(arg1)).move_to(code,p1);
22077               _cimg_mp_return(pos);
22078             }
22079             break;
22080 
22081           case 'x' :
22082             if (!std::strncmp(ss,"xor(",4)) { // Xor
22083               _cimg_mp_op("Function 'xor()'");
22084               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
22085               arg1 = compile(ss4,s1,depth1,0,bloc_flags);
22086               arg2 = compile(++s1,se1,depth1,0,bloc_flags);
22087               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
22088               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
22089               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
22090               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
22091               if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
22092                 _cimg_mp_const_scalar((longT)mem[arg1] ^ (longT)mem[arg2]);
22093               _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
22094             }
22095             break;
22096           }
22097 
22098           if (!std::strncmp(ss,"max(",4) || !std::strncmp(ss,"min(",4) ||
22099               !std::strncmp(ss,"maxabs(",7) || !std::strncmp(ss,"minabs(",7) ||
22100               !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
22101               !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
22102               !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
22103               !std::strncmp(ss,"prod(",5) ||
22104               !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
22105               !std::strncmp(ss,"argminabs(",10) || !std::strncmp(ss,"argmaxabs(",10) ||
22106               !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
22107             _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
22108                                   ss[3]=='k'?"Function 'argkth()'":
22109                                   ss[4]=='i' && ss[6]=='('?"Function 'argmin()'":
22110                                   ss[4]=='i'?"Function argminabs()'":
22111                                   ss[6]=='('?"Function 'argmax()'":
22112                                   "Function 'argmaxabs()'"):
22113                         *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
22114                         *ss=='k'?"Function 'kth()'":
22115                         *ss=='p'?"Function 'prod()'":
22116                         *ss=='v'?"Function 'var()'":
22117                         ss[1]=='i'?(ss[3]=='('?"Function 'min()'":
22118                                     "Function 'minabs()'"):
22119                         ss[1]=='a'?(ss[3]=='('?"Function 'max()'":
22120                                     "Function 'maxabs()'"):
22121                         "Function 'med()'");
22122             op = *ss=='a'?(ss[1]=='v'?mp_avg:
22123                            ss[3]=='k'?mp_argkth:
22124                            ss[4]=='i' && ss[6]=='('?mp_argmin:
22125                            ss[4]=='i'?mp_argminabs:
22126                            ss[6]=='('?mp_argmax:mp_argmaxabs):
22127               *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
22128               *ss=='k'?mp_kth:
22129               *ss=='p'?mp_prod:
22130               *ss=='v'?mp_var:
22131               ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
22132               ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
22133               mp_median;
22134             is_sth = true; // Tell if all arguments are constant
22135             pos = scalar();
22136             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
22137             for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
22138               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22139                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
22140               arg2 = compile(s,ns,depth1,0,bloc_flags);
22141               if (_cimg_mp_is_vector(arg2))
22142                 CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
22143                                        arg2 + (ulongT)_cimg_mp_size(arg2)).
22144                   move_to(l_opcode);
22145               else CImg<ulongT>::vector(arg2).move_to(l_opcode);
22146               is_sth&=_cimg_mp_is_const_scalar(arg2);
22147               s = ns;
22148             }
22149             (l_opcode>'y').move_to(opcode);
22150             opcode[2] = opcode._height;
22151             if (is_sth) _cimg_mp_const_scalar(op(*this));
22152             opcode.move_to(code);
22153             return_new_comp = true;
22154             _cimg_mp_return(pos);
22155           }
22156 
22157           // No corresponding built-in function -> Look for a user-defined macro call.
22158           s0 = strchr(ss,'(');
22159           if (s0) {
22160             variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
22161 
22162             // Count number of specified arguments.
22163             p1 = 0;
22164             for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
22165               while (*s && cimg::is_blank(*s)) ++s;
22166               if (*s==')' && !p1) break;
22167               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22168                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
22169             }
22170 
22171             arg3 = 0; // Number of possible name matches
22172             cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
22173                                           macro_def[l].back()==(char)p1) {
22174               p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
22175               CImg<charT> _expr = macro_body[l]; // Expression to be substituted
22176 
22177               p1 = 1; // Index of current parsed argument
22178               for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
22179                 while (*s && cimg::is_blank(*s)) ++s;
22180                 if (*s==')' && p1==1) break; // Function has no arguments
22181                 if (p1>p2) { ++p1; break; }
22182                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22183                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
22184                 variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
22185                 arg2 = 0;
22186                 cimg_forX(_expr,k) {
22187                   if (_expr[k]==(char)p1) { // Perform argument substitution
22188                     arg1 = _expr._width;
22189                     _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
22190                     std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
22191                     std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
22192                     k+=variable_name._width - 2;
22193                   }
22194                   ++arg2;
22195                 }
22196               }
22197 
22198               // Recompute 'pexpr' and 'level' for evaluating substituted expression.
22199               CImg<charT> _pexpr(_expr._width);
22200               ns = _pexpr._data;
22201               for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
22202                 if (!cimg::is_blank(*ps)) c1 = *ps;
22203                 *(ns++) = c1;
22204               }
22205               *ns = 0;
22206 
22207               CImg<uintT> _level = get_level(_expr);
22208               expr.swap(_expr);
22209               pexpr.swap(_pexpr);
22210               level.swap(_level);
22211               s0 = user_macro;
22212               user_macro = macro_def[l];
22213               pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,bloc_flags);
22214               user_macro = s0;
22215               level.swap(_level);
22216               pexpr.swap(_pexpr);
22217               expr.swap(_expr);
22218               _cimg_mp_return(pos);
22219             }
22220 
22221             if (arg3) { // Macro name matched but number of arguments does not
22222               CImg<uintT> sig_nargs(arg3);
22223               arg1 = 0;
22224               cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
22225                 sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
22226               _cimg_mp_strerr;
22227               cimg::strellipsize(variable_name,64);
22228               if (sig_nargs._width>1) {
22229                 sig_nargs.sort();
22230                 arg1 = sig_nargs.back();
22231                 --sig_nargs._width;
22232                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
22233                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
22234                                             "does not match macro declaration (defined for %s or %u arguments), "
22235                                             "in expression '%s'.",
22236                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
22237                                             p1,sig_nargs.value_string()._data,arg1,s0);
22238               } else
22239                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
22240                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
22241                                             "does not match macro declaration (defined for %u argument%s), "
22242                                             "in expression '%s'.",
22243                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
22244                                             p1,*sig_nargs,*sig_nargs!=1?"s":"",s0);
22245             }
22246           }
22247         } // if (se1==')')
22248 
22249         // Char / string initializer.
22250         if (*se1=='\'' &&
22251             ((se1>ss && *ss=='\'') ||
22252             (se1>ss1 && *ss=='_' && *ss1=='\''))) {
22253           if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
22254           else { _cimg_mp_op("String initializer"); s1 = ss1; }
22255           arg1 = (unsigned int)(se1 - s1); // Original string length
22256           if (arg1) {
22257             CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
22258             cimg::strunescape(variable_name);
22259             arg1 = (unsigned int)std::strlen(variable_name);
22260           }
22261           if (!arg1) _cimg_mp_return(0); // Empty string -> 0
22262           if (*ss=='_') {
22263             if (arg1==1) _cimg_mp_const_scalar((unsigned char)*variable_name);
22264             _cimg_mp_strerr;
22265             cimg::strellipsize(variable_name,64);
22266             throw CImgArgumentException("[" cimg_appname "_math_parser] "
22267                                         "CImg<%s>::%s: %s: Literal %s contains more than one byte, "
22268                                         "in expression '%s'.",
22269                                         pixel_type(),_cimg_mp_calling_function,s_op,
22270                                         ss1,s0);
22271           }
22272           pos = vector(arg1);
22273           CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
22274           CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
22275           std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
22276           (l_opcode>'y').move_to(code);
22277           return_new_comp = true;
22278           _cimg_mp_return(pos);
22279         }
22280 
22281         // Vector initializer [ ... ].
22282         if (*ss=='[' && *se1==']') {
22283           _cimg_mp_op("Vector initializer");
22284           s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1;
22285           s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2;
22286           if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
22287             arg1 = (unsigned int)(s2 - s1 - 1); // Original string length
22288             if (arg1) {
22289               CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
22290               cimg::strunescape(variable_name);
22291               arg1 = (unsigned int)std::strlen(variable_name);
22292             }
22293             if (!arg1) _cimg_mp_return(0); // Empty string -> 0
22294             pos = vector(arg1);
22295             CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
22296             CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
22297             std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
22298             (l_opcode>'y').move_to(code);
22299           } else { // Vector values provided as list of items
22300             arg1 = 0; // Number of specified values
22301             if (*ss1!=']') for (s = ss1; s<se; ++s) {
22302                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
22303                                (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
22304                 arg2 = compile(s,ns,depth1,0,bloc_flags);
22305                 if (_cimg_mp_is_vector(arg2)) {
22306                   arg3 = _cimg_mp_size(arg2);
22307                   CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
22308                   arg1+=arg3;
22309                 } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
22310                 s = ns;
22311               }
22312             if (!arg1) _cimg_mp_return(0);
22313             pos = vector(arg1);
22314             l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
22315             (l_opcode>'y').move_to(opcode);
22316             opcode[2] = opcode._height;
22317             opcode.move_to(code);
22318           }
22319           return_new_comp = true;
22320           _cimg_mp_return(pos);
22321         }
22322 
22323         // Variables related to the input list of images.
22324         if (*ss1=='#' && ss2<se) {
22325           arg1 = compile(ss2,se,depth1,0,bloc_flags);
22326           p1 = (unsigned int)(listin._width && _cimg_mp_is_const_scalar(arg1)?
22327                               cimg::mod((int)mem[arg1],listin.width()):~0U);
22328           switch (*ss) {
22329           case 'w' : // w#ind
22330             if (!listin) _cimg_mp_return(0);
22331             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._width);
22332             _cimg_mp_scalar1(mp_list_width,arg1);
22333           case 'h' : // h#ind
22334             if (!listin) _cimg_mp_return(0);
22335             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._height);
22336             _cimg_mp_scalar1(mp_list_height,arg1);
22337           case 'd' : // d#ind
22338             if (!listin) _cimg_mp_return(0);
22339             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._depth);
22340             _cimg_mp_scalar1(mp_list_depth,arg1);
22341           case 'r' : // r#ind
22342             if (!listin) _cimg_mp_return(0);
22343             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._is_shared);
22344             _cimg_mp_scalar1(mp_list_is_shared,arg1);
22345           case 's' : // s#ind
22346             if (!listin) _cimg_mp_return(0);
22347             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._spectrum);
22348             _cimg_mp_scalar1(mp_list_spectrum,arg1);
22349           case 'i' : // i#ind
22350             if (!listin) _cimg_mp_return(0);
22351             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
22352                              0,_cimg_mp_boundary);
22353           case 'I' : // I#ind
22354             p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
22355             if (!p2) _cimg_mp_return(0);
22356             pos = vector(p2);
22357             CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
22358             return_new_comp = true;
22359             _cimg_mp_return(pos);
22360           case 'R' : // R#ind
22361             if (!listin) _cimg_mp_return(0);
22362             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
22363                              0,_cimg_mp_boundary);
22364           case 'G' : // G#ind
22365             if (!listin) _cimg_mp_return(0);
22366             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
22367                              0,_cimg_mp_boundary);
22368           case 'B' : // B#ind
22369             if (!listin) _cimg_mp_return(0);
22370             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
22371                              0,_cimg_mp_boundary);
22372           case 'A' : // A#ind
22373             if (!listin) _cimg_mp_return(0);
22374             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
22375                              0,_cimg_mp_boundary);
22376           }
22377         }
22378 
22379         if (*ss1 && *ss2=='#' && ss3<se) {
22380           arg1 = compile(ss3,se,depth1,0,bloc_flags);
22381           p1 = (unsigned int)(listin._width && _cimg_mp_is_const_scalar(arg1)?
22382                               cimg::mod((int)mem[arg1],listin.width()):~0U);
22383           if (*ss=='w' && *ss1=='h') { // wh#ind
22384             if (!listin) _cimg_mp_return(0);
22385             if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._width*listin[p1]._height);
22386             _cimg_mp_scalar1(mp_list_wh,arg1);
22387           }
22388           arg2 = ~0U;
22389 
22390           if (*ss=='i') {
22391             if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
22392               if (!listin) _cimg_mp_return(0);
22393               _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
22394                                0,_cimg_mp_boundary);
22395             }
22396 
22397             if (*ss1=='c') { // ic#ind
22398               if (!listin) _cimg_mp_return(0);
22399               if (_cimg_mp_is_const_scalar(arg1)) {
22400                 if (!list_median) list_median.assign(listin._width);
22401                 if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
22402                 _cimg_mp_const_scalar(*list_median[p1]);
22403               }
22404               _cimg_mp_scalar1(mp_list_median,arg1);
22405             }
22406 
22407             if (*ss1=='n') { // in#ind
22408               if (!listin) _cimg_mp_return(0);
22409               if (_cimg_mp_is_const_scalar(arg1)) {
22410                 if (!list_norm) list_norm.assign(listin._width);
22411                 if (!list_norm[p1]) CImg<doubleT>::vector(listin[p1].magnitude()).move_to(list_norm[p1]);
22412                 _cimg_mp_const_scalar(*list_norm[p1]);
22413               }
22414               _cimg_mp_scalar1(mp_list_norm,arg1);
22415             }
22416 
22417             switch (*ss1) {
22418             case 'm' : arg2 = 0; break; // im#ind
22419             case 'M' : arg2 = 1; break; // iM#ind
22420             case 'a' : arg2 = 2; break; // ia#ind
22421             case 'v' : arg2 = 3; break; // iv#ind
22422             case 's' : arg2 = 12; break; // is#ind
22423             case 'p' : arg2 = 13; break; // ip#ind
22424             }
22425           } else if (*ss1=='m') switch (*ss) {
22426             case 'x' : arg2 = 4; break; // xm#ind
22427             case 'y' : arg2 = 5; break; // ym#ind
22428             case 'z' : arg2 = 6; break; // zm#ind
22429             case 'c' : arg2 = 7; break; // cm#ind
22430             } else if (*ss1=='M') switch (*ss) {
22431             case 'x' : arg2 = 8; break; // xM#ind
22432             case 'y' : arg2 = 9; break; // yM#ind
22433             case 'z' : arg2 = 10; break; // zM#ind
22434             case 'c' : arg2 = 11; break; // cM#ind
22435             }
22436           if (arg2!=~0U) {
22437             if (!listin) _cimg_mp_return(0);
22438             if (_cimg_mp_is_const_scalar(arg1)) {
22439               if (!list_stats) list_stats.assign(listin._width);
22440               if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
22441               _cimg_mp_const_scalar(list_stats(p1,arg2));
22442             }
22443             _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
22444           }
22445         }
22446 
22447         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
22448           arg1 = compile(ss4,se,depth1,0,bloc_flags);
22449           if (!listin) _cimg_mp_return(0);
22450           p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22451           if (p1!=~0U) _cimg_mp_const_scalar(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
22452           _cimg_mp_scalar1(mp_list_whd,arg1);
22453         }
22454         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
22455           arg1 = compile(ss5,se,depth1,0,bloc_flags);
22456           if (!listin) _cimg_mp_return(0);
22457           p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22458           if (p1!=~0U)
22459             _cimg_mp_const_scalar(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
22460           _cimg_mp_scalar1(mp_list_whds,arg1);
22461         }
22462 
22463         if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
22464         if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
22465 
22466 #ifdef cimg_mp_operator_dollar
22467         // External variable '$varname'.
22468         variable_name.assign(ss,(unsigned int)(se + 1 - ss)).back() = 0;
22469         if (*ss=='$' && is_varname(variable_name._data + 1))
22470           _cimg_mp_const_scalar(cimg_mp_operator_dollar(variable_name._data + 1));
22471 #endif
22472 
22473         // No known item found, assuming this is an already initialized variable.
22474         if (is_varname(variable_name)) { // Valid variable name
22475           get_variable_pos(variable_name,arg1,arg2);
22476           arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
22477           if (arg1!=~0U) _cimg_mp_return(arg1);
22478         }
22479 
22480         // Reached an unknown item -> error.
22481         c1 = *se1;
22482         _cimg_mp_strerr;
22483         cimg::strellipsize(variable_name,64);
22484         if (is_sth)
22485           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22486                                       "CImg<%s>::%s: Undefined variable '%s' in expression '%s'.",
22487                                       pixel_type(),_cimg_mp_calling_function,
22488                                       variable_name._data,s0);
22489         s1 = std::strchr(ss,'(');
22490         s_op = s1 && c1==')'?"function call":"item";
22491         throw CImgArgumentException("[" cimg_appname "_math_parser] "
22492                                     "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s'.",
22493                                     pixel_type(),_cimg_mp_calling_function,
22494                                     s_op,variable_name._data,s0);
22495       }
22496 
22497       // Evaluation procedure.
22498       double operator()(const double x, const double y, const double z, const double c) {
22499         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22500         for (p_code = code; p_code<p_code_end; ++p_code) {
22501           opcode._data = p_code->_data;
22502           const ulongT target = opcode[1];
22503           mem[target] = _cimg_mp_defunc(*this);
22504         }
22505         return *result;
22506       }
22507 
22508       // Evaluation procedure (return output values in vector 'output').
22509       template<typename t>
22510       void operator()(const double x, const double y, const double z, const double c, t *const output) {
22511         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22512         for (p_code = code; p_code<p_code_end; ++p_code) {
22513           opcode._data = p_code->_data;
22514           const ulongT target = opcode[1];
22515           mem[target] = _cimg_mp_defunc(*this);
22516         }
22517         if (result_dim) {
22518           const double *ptrs = result + 1;
22519           t *ptrd = output;
22520           for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
22521         } else *output = (t)*result;
22522       }
22523 
22524       // Evaluation procedure for begin_t() bloc.
22525       void begin_t() {
22526         if (!code_begin_t) return;
22527         mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22528         p_code_end = code_begin_t.end();
22529         for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
22530           opcode._data = p_code->_data;
22531           const ulongT target = opcode[1];
22532           mem[target] = _cimg_mp_defunc(*this);
22533         }
22534         p_code_end = code.end();
22535       }
22536 
22537       // Evaluation procedure for end_t() bloc.
22538       void end_t() {
22539         if (!code_end_t) return;
22540         if (imgin) {
22541           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22542           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22543           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22544           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22545         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22546         p_code_end = code_end_t.end();
22547         for (p_code = code_end_t; p_code<p_code_end; ++p_code) {
22548           opcode._data = p_code->_data;
22549           const ulongT target = opcode[1];
22550           mem[target] = _cimg_mp_defunc(*this);
22551         }
22552       }
22553 
22554       // Evaluation procedure the end() bloc.
22555       void end() {
22556         if (!code_end) return;
22557         if (imgin) {
22558           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22559           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22560           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22561           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22562         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22563         p_code_end = code_end.end();
22564         for (p_code = code_end; p_code<p_code_end; ++p_code) {
22565           opcode._data = p_code->_data;
22566           const ulongT target = opcode[1];
22567           mem[target] = _cimg_mp_defunc(*this);
22568         }
22569       }
22570 
22571       // Merge inter-thread variables.
22572       // (argument 'mp' is the master instance).
22573       void merge(_cimg_math_parser& mp) {
22574         if (&mp==this) return;
22575         cimg_rofY(mp.memmerge,k) {
22576           const unsigned int
22577             pos = (unsigned int)mp.memmerge(0,k),
22578             siz = (unsigned int)mp.memmerge(1,k),
22579             iop = (unsigned int)mp.memmerge(2,k);
22580           if (!siz) switch (iop) { // Scalar value
22581             case 0 : mp.mem[pos] = mem[pos]; break;                                       // Assignment
22582             case 1 : mp.mem[pos]+=mem[pos]; break;                                        // Operator+
22583             case 2 : mp.mem[pos]-=mem[pos]; break;                                        // Operator-
22584             case 3 : mp.mem[pos]*=mem[pos]; break;                                        // Operator*
22585             case 4 : mp.mem[pos]/=mem[pos]; break;                                        // Operator/
22586             case 5 : mp.mem[pos] = (double)((longT)mp.mem[pos] & (longT)mem[pos]); break; // Operator&
22587             case 6 : mp.mem[pos] = (double)((longT)mp.mem[pos] | (longT)mem[pos]); break; // Operator|
22588             case 7 : mp.mem[pos] = (double)((longT)mp.mem[pos] ^ (longT)mem[pos]); break; // Operator 'xor'
22589             case 8 : mp.mem[pos] = mp.mem[pos] && mem[pos]; break;                        // Operator&&
22590             case 9 : mp.mem[pos] = mp.mem[pos] || mem[pos]; break;                        // Operator||
22591             case 10 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break;                // Operator 'min'
22592             case 11 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break;                // Operator 'max'
22593             } else switch (iop) { // Vector value
22594             case 0 : // Assignment
22595               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true) = CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22596               break;
22597             case 1 : // Operator+
22598               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22599               break;
22600             case 2 : // Operator-
22601               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22602               break;
22603             case 3 : // Operator*
22604               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22605               break;
22606             case 4 : // Operator/
22607               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22608               break;
22609             case 5 : // Operator&
22610               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)&=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22611               break;
22612             case 6 : // Operator|
22613               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)|=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22614               break;
22615             case 7 : // Operator 'xor'
22616               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)^=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22617               break;
22618             case 8 : { // Operator&&
22619               CImg<doubleT>
22620                 arg1(&mp.mem[pos + 1],siz,1,1,1,true),
22621                 arg2(&mem[pos + 1],siz,1,1,1,true);
22622               cimg_foroff(arg1,off) arg1[off] = arg1[off] && arg2[off];
22623               } break;
22624             case 9 : { // Operator||
22625               CImg<doubleT>
22626                 arg1(&mp.mem[pos + 1],siz,1,1,1,true),
22627                 arg2(&mem[pos + 1],siz,1,1,1,true);
22628               cimg_foroff(arg1,off) arg1[off] = arg1[off] || arg2[off];
22629             } break;
22630             case 10 : // Operator 'min'
22631               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22632               break;
22633             case 11 : // Operator 'max'
22634               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22635               break;
22636             }
22637         }
22638       }
22639 
22640       // Return specified argument number as a string.
22641       static const char *s_argth(const unsigned int n_arg) {
22642         const char
22643           *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth",
22644                         "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
22645                         "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" };
22646         return _s_arg[n_arg<sizeof(_s_arg)/sizeof(char*)?n_arg:sizeof(_s_arg)/sizeof(char*)-1];
22647       }
22648 
22649       // Return a string that defines the calling function + the user-defined function scope.
22650       CImg<charT> s_calling_function() const {
22651         CImg<charT> res;
22652         const unsigned int
22653           l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
22654           l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
22655         if (l2) {
22656           res.assign(l1 + l2 + 48);
22657           cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
22658         } else {
22659           res.assign(l1 + l2 + 4);
22660           cimg_snprintf(res,res._width,"%s()",calling_function);
22661         }
22662         return res;
22663       }
22664 
22665       // Return type of a memory element as a string.
22666       CImg<charT> s_type(const unsigned int arg) const {
22667         CImg<charT> res;
22668         if (_cimg_mp_is_vector(arg)) { // Vector
22669           CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
22670           cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
22671         } else if (_cimg_mp_is_const_scalar(arg)) CImg<charT>::string("const scalar").move_to(res); // Const scalar
22672         else CImg<charT>::string("scalar").move_to(res); // Scalar
22673         return res;
22674       }
22675 
22676       // Count parentheses/brackets level of each character of the expression.
22677       CImg<uintT> get_level(CImg<charT>& _expr) const {
22678         bool is_escaped = false, next_is_escaped = false;
22679         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22680         CImg<uintT> res(_expr._width - 1);
22681         unsigned int *pd = res._data;
22682         int _level = 0;
22683         for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
22684           if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
22685           if (!is_escaped && *ps=='\'') { // Non-escaped character
22686             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22687             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22688             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22689           }
22690           *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
22691                                    *ps=='(' || *ps=='['?_level++:
22692                                    *ps==')' || *ps==']'?--_level:
22693                                    _level);
22694           mode = next_mode;
22695           is_escaped = next_is_escaped;
22696           next_is_escaped = false;
22697         }
22698         if (mode) {
22699           cimg::strellipsize(_expr,64);
22700           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22701                                       "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
22702                                       pixel_type(),_cimg_mp_calling_function,
22703                                       _expr._data);
22704         }
22705         if (_level) {
22706           cimg::strellipsize(_expr,64);
22707           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22708                                       "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
22709                                       pixel_type(),_cimg_mp_calling_function,
22710                                       _expr._data);
22711         }
22712         return res;
22713       }
22714 
22715       // Find and return index of current image 'imgin' within image list 'listin'.
22716       unsigned int get_mem_img_index() {
22717         if (mem_img_index==~0U) {
22718           if (&imgout>listout.data() && &imgout<listout.end())
22719             mem_img_index = const_scalar((double)(&imgout - listout.data()));
22720           else {
22721             unsigned int pos = ~0U;
22722             cimglist_for(listout,l)
22723               if (imgout._data==listout[l]._data && imgout.is_sameXYZC(listout[l])) { pos = l; break; }
22724             if (pos!=~0U) mem_img_index = const_scalar((double)pos);
22725           }
22726         }
22727         return mem_img_index;
22728       }
22729 
22730       // Return indices for accessing math parser variables.
22731       void get_variable_pos(const char *variable_name, unsigned int &pos, unsigned int &rpos) {
22732         char c1, c2, c3, c4;
22733         pos = rpos = ~0U;
22734         if (!variable_name || !*variable_name) return;
22735 
22736         unsigned int rp = variable_name[1]?~0U:*variable_name; // One-char variable
22737         if (variable_name[1] && !variable_name[2]) { // Two-chars variable
22738           c1 = variable_name[0];
22739           c2 = variable_name[1];
22740           if (c1=='w' && c2=='h') rp = 0; // wh
22741           else if (c1=='p' && c2=='i') rp = 3; // pi
22742           else if (c1=='i') {
22743             if (c2>='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9
22744             else if (c2=='m') rp = 4; // im
22745             else if (c2=='M') rp = 5; // iM
22746             else if (c2=='a') rp = 6; // ia
22747             else if (c2=='v') rp = 7; // iv
22748             else if (c2=='s') rp = 8; // is
22749             else if (c2=='p') rp = 9; // ip
22750             else if (c2=='c') rp = 10; // ic
22751             else if (c2=='n') rp = 11; // in
22752           } else if (c2=='m') {
22753             if (c1=='x') rp = 12; // xm
22754             else if (c1=='y') rp = 13; // ym
22755             else if (c1=='z') rp = 14; // zm
22756             else if (c1=='c') rp = 15; // cm
22757           } else if (c2=='M') {
22758             if (c1=='x') rp = 16; // xM
22759             else if (c1=='y') rp = 17; // yM
22760             else if (c1=='z') rp = 18; // zM
22761             else if (c1=='c') rp = 19; // cM
22762           }
22763         } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
22764           c1 = variable_name[0];
22765           c2 = variable_name[1];
22766           c3 = variable_name[2];
22767           if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd
22768         } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
22769                    !variable_name[4]) { // Four-chars variable
22770           c1 = variable_name[0];
22771           c2 = variable_name[1];
22772           c3 = variable_name[2];
22773           c4 = variable_name[3];
22774           if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds
22775         } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation
22776         else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary
22777 
22778         if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels
22779 
22780         // Multi-char variable name : check for existing variable with same name
22781         cimglist_for(variable_def,i)
22782           if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; }
22783       }
22784 
22785       // Tell for each character of an expression if it is inside a string or not.
22786       CImg<boolT> is_inside_string(CImg<charT>& _expr) const {
22787         bool is_escaped = false, next_is_escaped = false;
22788         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22789         CImg<boolT> res = CImg<charT>::string(_expr);
22790         bool *pd = res._data;
22791         for (const char *ps = _expr._data; *ps; ++ps) {
22792           if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
22793           if (!is_escaped && *ps=='\'') { // Non-escaped character
22794             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22795             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22796             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22797           }
22798           *(pd++) = mode>=1 || is_escaped;
22799           mode = next_mode;
22800           is_escaped = next_is_escaped;
22801           next_is_escaped = false;
22802         }
22803         return res;
22804       }
22805 
22806       // Return true if specified argument can be a part of an allowed  variable name.
22807       static bool is_varchar(const char c) {
22808         return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
22809       }
22810 
22811       // Return true if specified argument can be considered as a variable name.
22812       static bool is_varname(const char *const str, const unsigned int length=~0U) {
22813         if (*str>='0' && *str<='9') return false;
22814         for (unsigned int l = 0; l<length && str[l]; ++l)
22815           if (!is_varchar(str[l])) return false;
22816         return true;
22817       }
22818 
22819       // Return true if all values of a vector are computation values.
22820       bool is_comp_vector(const unsigned int arg) const {
22821         unsigned int siz = _cimg_mp_size(arg);
22822         if (siz>128) return false;
22823         const int *ptr = memtype.data(arg + 1);
22824         bool is_tmp = true;
22825         while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
22826         return is_tmp;
22827       }
22828 
22829       // Check if a memory slot is a positive integer constant scalar value.
22830       // 'mode' can be:
22831       // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
22832       void check_const_scalar(const unsigned int arg, const unsigned int n_arg,
22833                               const unsigned int mode,
22834                               char *const ss, char *const se, const char saved_char) {
22835         _cimg_mp_check_type(arg,n_arg,1,0);
22836         if (!_cimg_mp_is_const_scalar(arg)) {
22837           const char *const s_arg = s_argth(n_arg);
22838           char *s0; _cimg_mp_strerr;
22839           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22840                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a constant, "
22841                                       "in expression '%s'.",
22842                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22843                                       s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,s0);
22844         }
22845         const double val = mem[arg];
22846 
22847         if (!((!mode || (double)(int)mem[arg]==mem[arg]) &&
22848               (mode<2 || mem[arg]>=(mode==3)))) {
22849           const char *const s_arg = s_argth(n_arg);
22850           char *s0; _cimg_mp_strerr;
22851           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22852                                       "CImg<%s>::%s: %s%s %s%s (of type '%s' and value %g) is not a%s constant, "
22853                                       "in expression '%s'.",
22854                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22855                                       s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,val,
22856                                       !mode?"":mode==1?"n integer":
22857                                       mode==2?" positive integer":" strictly positive integer",s0);
22858         }
22859       }
22860 
22861       // Check if an image index is a constant value.
22862       void check_const_index(const unsigned int arg,
22863                              char *const ss, char *const se, const char saved_char) {
22864         if (arg!=~0U && !_cimg_mp_is_const_scalar(arg)) {
22865           char *s0; _cimg_mp_strerr;
22866           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22867                                       "CImg<%s>::%s: %s%s Specified image index is not a constant, "
22868                                       "in expression '%s'.",
22869                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
22870         }
22871       }
22872 
22873       // Check a matrix is square.
22874       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
22875                                char *const ss, char *const se, const char saved_char) {
22876         _cimg_mp_check_type(arg,n_arg,2,0);
22877         const unsigned int
22878           siz = _cimg_mp_size(arg),
22879           n = (unsigned int)cimg::round(std::sqrt((float)siz));
22880         if (n*n!=siz) {
22881           const char *s_arg;
22882           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22883           else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One";
22884           char *s0; _cimg_mp_strerr;
22885           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22886                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') "
22887                                       "cannot be considered as a square matrix, in expression '%s'.",
22888                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22889                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22890                                       s_type(arg)._data,s0);
22891         }
22892       }
22893 
22894       // Check type compatibility for one argument.
22895       // Bits of 'mode' tells what types are allowed:
22896       // { 1 = scalar | 2 = vectorN }.
22897       // If 'N' is not zero, it also restricts the vectors to be of size N only.
22898       void check_type(const unsigned int arg, const unsigned int n_arg,
22899                       const unsigned int mode, const unsigned int N,
22900                       char *const ss, char *const se, const char saved_char) {
22901         const bool
22902           is_scalar = _cimg_mp_is_scalar(arg),
22903           is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
22904         bool cond = false;
22905         if (mode&1) cond|=is_scalar;
22906         if (mode&2) cond|=is_vector;
22907         if (!cond) {
22908           const char *s_arg;
22909           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22910           else s_arg = s_argth(n_arg);
22911           CImg<charT> sb_type(32);
22912           if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
22913           else if (mode==2) {
22914             if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
22915             else cimg_snprintf(sb_type,sb_type._width,"'vector'");
22916           } else {
22917             if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
22918             else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
22919           }
22920           char *s0; _cimg_mp_strerr;
22921           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22922                                       "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
22923                                       "in expression '%s'.",
22924                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22925                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22926                                       s_type(arg)._data,sb_type._data,s0);
22927         }
22928       }
22929 
22930       // Check that listin or listout are not empty.
22931       void check_list(const bool is_out,
22932                       char *const ss, char *const se, const char saved_char) {
22933         if ((!is_out && !listin) || (is_out && !listout)) {
22934           char *s0; _cimg_mp_strerr;
22935           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22936                                       "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
22937                                       "in expression '%s'.",
22938                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
22939         }
22940       }
22941 
22942       // Insert constant value in memory.
22943       unsigned int const_scalar(const double val) {
22944 
22945         // Search for built-in constant.
22946         if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
22947         if (val==(double)(int)val) {
22948           if (val>=0 && val<=10) return (unsigned int)val;
22949           if (val<0 && val>=-5) return (unsigned int)(10 - val);
22950         }
22951         if (val==0.5) return 16;
22952 
22953         // Search for constant already requested before (in const cache).
22954         unsigned int ind = ~0U;
22955         if (constcache_size<1024) {
22956           if (!constcache_size) {
22957             constcache_vals.assign(16,1,1,1,0);
22958             constcache_inds.assign(16,1,1,1,0);
22959             *constcache_vals = val;
22960             constcache_size = 1;
22961             ind = 0;
22962           } else { // Dichotomic search
22963             const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
22964             if (val_beg>=val) ind = 0;
22965             else if (val_end==val) ind = constcache_size - 1;
22966             else if (val_end<val) ind = constcache_size;
22967             else {
22968               unsigned int i0 = 1, i1 = constcache_size - 2;
22969               while (i0<=i1) {
22970                 const unsigned int mid = (i0 + i1)/2;
22971                 if (constcache_vals[mid]==val) { i0 = mid; break; }
22972                 else if (constcache_vals[mid]<val) i0 = mid + 1;
22973                 else i1 = mid - 1;
22974               }
22975               ind = i0;
22976             }
22977 
22978             if (ind>=constcache_size || constcache_vals[ind]!=val) {
22979               ++constcache_size;
22980               if (constcache_size>constcache_vals._width) {
22981                 constcache_vals.resize(-200,1,1,1,0);
22982                 constcache_inds.resize(-200,1,1,1,0);
22983               }
22984               const int l = constcache_size - (int)ind - 1;
22985               if (l>0) {
22986                 std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
22987                 std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
22988               }
22989               constcache_vals[ind] = val;
22990               constcache_inds[ind] = 0;
22991             }
22992           }
22993           if (constcache_inds[ind]) return constcache_inds[ind];
22994         }
22995 
22996         // Insert new constant in memory if necessary.
22997         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
22998         const unsigned int pos = mempos++;
22999         mem[pos] = val;
23000         memtype[pos] = 1; // Set constant property
23001         if (ind!=~0U) constcache_inds[ind] = pos;
23002         return pos;
23003       }
23004 
23005       // Insert new scalar in memory.
23006       unsigned int scalar() {
23007         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
23008         return mempos++;
23009       }
23010 
23011       // Insert new vector of specified size in memory.
23012       unsigned int vector(const unsigned int siz) {
23013         if (mempos + siz>=mem._width) {
23014           mem.resize(2*mem._width + siz,1,1,1,0);
23015           memtype.resize(mem._width,1,1,1,0);
23016         }
23017         const unsigned int pos = mempos++;
23018         mem[pos] = cimg::type<double>::nan();
23019         memtype[pos] = siz + 1;
23020         mempos+=siz;
23021         return pos;
23022       }
23023 
23024       // Insert new initialized vector.
23025       unsigned int vector(const unsigned int siz, const double value) {
23026         const unsigned int pos = vector(siz);
23027         double *ptr = &mem[pos] + 1;
23028         for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
23029         return pos;
23030       }
23031 
23032       // Insert new copy of specified vector in memory.
23033       unsigned int vector_copy(const unsigned int arg) {
23034         const unsigned int
23035           siz = _cimg_mp_size(arg),
23036           pos = vector(siz);
23037         CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
23038         return pos;
23039       }
23040 
23041       // Set reserved status to all values of a vector.
23042       void set_reserved_vector(const unsigned int arg) {
23043         unsigned int siz = _cimg_mp_size(arg);
23044         int *ptr = memtype.data(arg + 1);
23045         while (siz-->0) *(ptr++) = -1;
23046       }
23047 
23048       unsigned int scalar0(const mp_func op) {
23049         const unsigned int pos = scalar();
23050         CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
23051         return_new_comp = true;
23052         return pos;
23053       }
23054 
23055       unsigned int scalar1(const mp_func op, const unsigned int arg1) {
23056         const unsigned int pos =
23057           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:
23058           ((return_new_comp = true), scalar());
23059         CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
23060         return pos;
23061       }
23062 
23063       unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23064         const unsigned int pos =
23065           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23066           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23067           ((return_new_comp = true), scalar());
23068         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
23069         return pos;
23070       }
23071 
23072       unsigned int scalar3(const mp_func op,
23073                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
23074         const unsigned int pos =
23075           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23076           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23077           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
23078           ((return_new_comp = true), scalar());
23079         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
23080         return pos;
23081       }
23082 
23083       unsigned int scalar4(const mp_func op,
23084                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
23085                            const unsigned int arg4) {
23086         const unsigned int pos =
23087           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23088           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23089           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
23090           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
23091           ((return_new_comp = true), scalar());
23092         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
23093         return pos;
23094       }
23095 
23096       unsigned int scalar5(const mp_func op,
23097                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
23098                            const unsigned int arg4, const unsigned int arg5) {
23099         const unsigned int pos =
23100           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23101           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23102           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
23103           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
23104           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
23105           ((return_new_comp = true), scalar());
23106         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
23107         return pos;
23108       }
23109 
23110       unsigned int scalar6(const mp_func op,
23111                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
23112                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
23113         const unsigned int pos =
23114           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23115           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23116           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
23117           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
23118           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
23119           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
23120           ((return_new_comp = true), scalar());
23121         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
23122         return pos;
23123       }
23124 
23125       unsigned int scalar7(const mp_func op,
23126                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
23127                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
23128                            const unsigned int arg7) {
23129         const unsigned int pos =
23130           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
23131           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
23132           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
23133           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
23134           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
23135           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
23136           arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:
23137           ((return_new_comp = true), scalar());
23138         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
23139         return pos;
23140       }
23141 
23142       void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
23143         const unsigned int siz = _cimg_mp_size(pos);
23144         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
23145         else {
23146           code.insert(siz);
23147           for (unsigned int k = 1; k<=siz; ++k)
23148             CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
23149         }
23150       }
23151 
23152       void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
23153         const unsigned int siz = _cimg_mp_size(pos);
23154         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
23155         else {
23156           code.insert(siz);
23157           for (unsigned int k = 1; k<=siz; ++k)
23158             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
23159         }
23160       }
23161 
23162       unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
23163         const unsigned int
23164           siz = _cimg_mp_size(arg1),
23165           pos = is_comp_vector(arg1)?arg1:
23166           ((return_new_comp = true), vector(siz));
23167         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
23168         else {
23169           code.insert(siz);
23170           for (unsigned int k = 1; k<=siz; ++k)
23171             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
23172         }
23173         return pos;
23174       }
23175 
23176       unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23177         const unsigned int
23178           siz = _cimg_mp_size(arg1),
23179           pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
23180           ((return_new_comp = true), vector(siz));
23181         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23182         else {
23183           code.insert(siz);
23184           for (unsigned int k = 1; k<=siz; ++k)
23185             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
23186         }
23187         return pos;
23188       }
23189 
23190       unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23191         const unsigned int
23192           siz = _cimg_mp_size(arg1),
23193           pos = is_comp_vector(arg1)?arg1:
23194           ((return_new_comp = true), vector(siz));
23195         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23196         else {
23197           code.insert(siz);
23198           for (unsigned int k = 1; k<=siz; ++k)
23199             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
23200         }
23201         return pos;
23202       }
23203 
23204       unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
23205         const unsigned int
23206           siz = _cimg_mp_size(arg2),
23207           pos = is_comp_vector(arg2)?arg2:
23208           ((return_new_comp = true), vector(siz));
23209         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
23210         else {
23211           code.insert(siz);
23212           for (unsigned int k = 1; k<=siz; ++k)
23213             CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
23214         }
23215         return pos;
23216       }
23217 
23218       unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
23219                                const unsigned int arg3) {
23220         const unsigned int
23221           siz = _cimg_mp_size(arg1),
23222           pos = is_comp_vector(arg1)?arg1:
23223           ((return_new_comp = true), vector(siz));
23224         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
23225         else {
23226           code.insert(siz);
23227           for (unsigned int k = 1; k<=siz; ++k)
23228             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
23229         }
23230         return pos;
23231       }
23232 
23233       // Evaluation functions, known by the parser.
23234       // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
23235       // so we can store pointers to them directly in the opcode vectors.
23236 #ifdef _mp_arg
23237 #undef _mp_arg
23238 #endif
23239 #define _mp_arg(x) mp.mem[mp.opcode[x]]
23240 
23241       static double mp_abs(_cimg_math_parser& mp) {
23242         return cimg::abs(_mp_arg(2));
23243       }
23244 
23245       static double mp_add(_cimg_math_parser& mp) {
23246         return _mp_arg(2) + _mp_arg(3);
23247       }
23248 
23249       static double mp_acos(_cimg_math_parser& mp) {
23250         return std::acos(_mp_arg(2));
23251       }
23252 
23253       static double mp_acosh(_cimg_math_parser& mp) {
23254         return cimg::acosh(_mp_arg(2));
23255       }
23256 
23257       static double mp_asinh(_cimg_math_parser& mp) {
23258         return cimg::asinh(_mp_arg(2));
23259       }
23260 
23261       static double mp_atanh(_cimg_math_parser& mp) {
23262         return cimg::atanh(_mp_arg(2));
23263       }
23264 
23265       static double mp_arg(_cimg_math_parser& mp) {
23266         const int _ind = (int)_mp_arg(4);
23267         const unsigned int
23268           nb_args = (unsigned int)mp.opcode[2] - 4,
23269           ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
23270           siz = (unsigned int)mp.opcode[3];
23271         if (siz>0) {
23272           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
23273           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
23274           return cimg::type<double>::nan();
23275         }
23276         if (ind>=nb_args) return 0;
23277         return _mp_arg(ind + 4);
23278       }
23279 
23280       static double mp_arg0(_cimg_math_parser& mp) {
23281         const int _ind = (int)_mp_arg(4);
23282         const unsigned int
23283           nb_args = (unsigned int)mp.opcode[2] - 4,
23284           ind = _ind<0?_ind + nb_args:_ind + 1U,
23285           siz = (unsigned int)mp.opcode[3];
23286         if (siz>0) {
23287           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
23288           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
23289           return cimg::type<double>::nan();
23290         }
23291         if (ind>=nb_args) return 0;
23292         return _mp_arg(ind + 4);
23293       }
23294 
23295       static double mp_argkth(_cimg_math_parser& mp) {
23296         const unsigned int i_end = (unsigned int)mp.opcode[2];
23297         const double val = mp_kth(mp);
23298         for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.;
23299         return 1.;
23300       }
23301 
23302       static double mp_argmin(_cimg_math_parser& mp) {
23303         const unsigned int i_end = (unsigned int)mp.opcode[2];
23304         double val = _mp_arg(3);
23305         unsigned int argval = 0;
23306         for (unsigned int i = 4; i<i_end; ++i) {
23307           const double _val = _mp_arg(i);
23308           if (_val<val) { val = _val; argval = i - 3; }
23309         }
23310         return (double)argval;
23311       }
23312 
23313       static double mp_argminabs(_cimg_math_parser& mp) {
23314         const unsigned int i_end = (unsigned int)mp.opcode[2];
23315         double val = _mp_arg(3), absval = cimg::abs(val);
23316         unsigned int argval = 0;
23317         for (unsigned int i = 4; i<i_end; ++i) {
23318           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
23319           if (_absval<absval) { val = _val; absval = _absval; argval = i - 3; }
23320         }
23321         return (double)argval;
23322       }
23323 
23324       static double mp_argmax(_cimg_math_parser& mp) {
23325         const unsigned int i_end = (unsigned int)mp.opcode[2];
23326         double val = _mp_arg(3);
23327         unsigned int argval = 0;
23328         for (unsigned int i = 4; i<i_end; ++i) {
23329           const double _val = _mp_arg(i);
23330           if (_val>val) { val = _val; argval = i - 3; }
23331         }
23332         return (double)argval;
23333       }
23334 
23335       static double mp_argmaxabs(_cimg_math_parser& mp) {
23336         const unsigned int i_end = (unsigned int)mp.opcode[2];
23337         double val = _mp_arg(3), absval = cimg::abs(val);
23338         unsigned int argval = 0;
23339         for (unsigned int i = 4; i<i_end; ++i) {
23340           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
23341           if (_absval>absval) { val = _val; absval = _absval; argval = i - 3; }
23342         }
23343         return (double)argval;
23344       }
23345 
23346       static double mp_asin(_cimg_math_parser& mp) {
23347         return std::asin(_mp_arg(2));
23348       }
23349 
23350       static double mp_atan(_cimg_math_parser& mp) {
23351         return std::atan(_mp_arg(2));
23352       }
23353 
23354       static double mp_atan2(_cimg_math_parser& mp) {
23355         return std::atan2(_mp_arg(2),_mp_arg(3));
23356       }
23357 
23358       static double mp_avg(_cimg_math_parser& mp) {
23359         const unsigned int i_end = (unsigned int)mp.opcode[2];
23360         double val = _mp_arg(3);
23361         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
23362         return val/(i_end - 3);
23363       }
23364 
23365       static double mp_bitwise_and(_cimg_math_parser& mp) {
23366         return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
23367       }
23368 
23369       static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
23370         return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
23371       }
23372 
23373       static double mp_bitwise_not(_cimg_math_parser& mp) {
23374         // Limit result to 32bits such that it can be entirely represented as a 'double'.
23375         return (double)~(unsigned int)_mp_arg(2);
23376       }
23377 
23378       static double mp_bitwise_or(_cimg_math_parser& mp) {
23379         return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
23380       }
23381 
23382       static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
23383         return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
23384       }
23385 
23386       static double mp_bitwise_xor(_cimg_math_parser& mp) {
23387         return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
23388       }
23389 
23390       static double mp_bool(_cimg_math_parser& mp) {
23391         return (double)(bool)_mp_arg(2);
23392       }
23393 
23394       static double mp_break(_cimg_math_parser& mp) {
23395         mp.break_type = 1;
23396         mp.p_code = mp.p_break - 1;
23397         return cimg::type<double>::nan();
23398       }
23399 
23400       static double mp_breakpoint(_cimg_math_parser& mp) {
23401         cimg_abort_init;
23402         cimg_abort_test;
23403         cimg::unused(mp);
23404         return cimg::type<double>::nan();
23405       }
23406 
23407 #ifdef cimg_mp_func_run
23408       static double mp_run(_cimg_math_parser& mp) {
23409         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
23410         CImgList<charT> _str;
23411         CImg<charT> it;
23412         for (unsigned int n = 0; n<nb_args; ++n) {
23413           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
23414           if (siz) { // Vector argument -> string
23415             const double *ptr = &_mp_arg(3 + 2*n) + 1;
23416             unsigned int l = 0;
23417             while (l<siz && ptr[l]) ++l;
23418             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
23419           } else { // Scalar argument -> number
23420             it.assign(24);
23421             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
23422             CImg<charT>::string(it,false,true).move_to(_str);
23423           }
23424         }
23425         CImg(1,1,1,1,0).move_to(_str);
23426         CImg<charT> str = _str>'x';
23427         cimg_mp_func_run(str._data);
23428         return cimg::type<double>::nan();
23429       }
23430 #endif
23431 
23432       static double mp_cbrt(_cimg_math_parser& mp) {
23433         return cimg::cbrt(_mp_arg(2));
23434       }
23435 
23436       static double mp_ceil(_cimg_math_parser& mp) {
23437         return std::ceil(_mp_arg(2));
23438       }
23439 
23440       static double mp_complex_abs(_cimg_math_parser& mp) {
23441         return cimg::_hypot(_mp_arg(2),_mp_arg(3));
23442       }
23443 
23444       static double mp_complex_conj(_cimg_math_parser& mp) {
23445         const double real = _mp_arg(2), imag = _mp_arg(3);
23446         double *ptrd = &_mp_arg(1) + 1;
23447         ptrd[0] = real;
23448         ptrd[1] = -imag;
23449         return cimg::type<double>::nan();
23450       }
23451 
23452       static double mp_complex_div_sv(_cimg_math_parser& mp) {
23453         const double
23454           *ptr2 = &_mp_arg(3) + 1,
23455           r1 = _mp_arg(2),
23456           r2 = *(ptr2++), i2 = *ptr2;
23457         double *ptrd = &_mp_arg(1) + 1;
23458         const double denom = r2*r2 + i2*i2;
23459         *(ptrd++) = r1*r2/denom;
23460         *ptrd =  -r1*i2/denom;
23461         return cimg::type<double>::nan();
23462       }
23463 
23464       static double mp_complex_div_vv(_cimg_math_parser& mp) {
23465         const double
23466           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23467           r1 = *(ptr1++), i1 = *ptr1,
23468           r2 = *(ptr2++), i2 = *ptr2;
23469         double *ptrd = &_mp_arg(1) + 1;
23470         const double denom = r2*r2 + i2*i2;
23471         *(ptrd++) = (r1*r2 + i1*i2)/denom;
23472         *ptrd = (r2*i1 - r1*i2)/denom;
23473         return cimg::type<double>::nan();
23474       }
23475 
23476       static double mp_complex_exp(_cimg_math_parser& mp) {
23477         const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real);
23478         double *ptrd = &_mp_arg(1) + 1;
23479         ptrd[0] = exp_real*std::cos(imag);
23480         ptrd[1] = exp_real*std::sin(imag);
23481         return cimg::type<double>::nan();
23482       }
23483 
23484       static double mp_complex_log(_cimg_math_parser& mp) {
23485         const double real = _mp_arg(2), imag = _mp_arg(3);
23486         double *ptrd = &_mp_arg(1) + 1;
23487         ptrd[0] = 0.5*std::log(real*real + imag*imag);
23488         ptrd[1] = std::atan2(imag,real);
23489         return cimg::type<double>::nan();
23490       }
23491 
23492       static double mp_complex_mul(_cimg_math_parser& mp) {
23493         const double
23494           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23495           r1 = *(ptr1++), i1 = *ptr1,
23496           r2 = *(ptr2++), i2 = *ptr2;
23497         double *ptrd = &_mp_arg(1) + 1;
23498         *(ptrd++) = r1*r2 - i1*i2;
23499         *(ptrd++) = r1*i2 + r2*i1;
23500         return cimg::type<double>::nan();
23501       }
23502 
23503       static void _mp_complex_pow(const double r1, const double i1,
23504                                   const double r2, const double i2,
23505                                   double *ptrd) {
23506         double ro, io;
23507         if (cimg::abs(i2)<1e-15) { // Exponent is real
23508           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
23509             if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
23510             else ro = io = 0;
23511           } else {
23512             const double
23513               mod1_2 = r1*r1 + i1*i1,
23514               phi1 = std::atan2(i1,r1),
23515               modo = std::pow(mod1_2,0.5*r2),
23516               phio = r2*phi1;
23517             ro = modo*std::cos(phio);
23518             io = modo*std::sin(phio);
23519           }
23520         } else { // Exponent is complex
23521           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
23522           const double
23523             mod1_2 = r1*r1 + i1*i1,
23524             phi1 = std::atan2(i1,r1),
23525             modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
23526             phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
23527           ro = modo*std::cos(phio);
23528           io = modo*std::sin(phio);
23529         }
23530         *(ptrd++) = ro;
23531         *ptrd = io;
23532       }
23533 
23534       static double mp_complex_pow_ss(_cimg_math_parser& mp) {
23535         const double val1 = _mp_arg(2), val2 = _mp_arg(3);
23536         double *ptrd = &_mp_arg(1) + 1;
23537         _mp_complex_pow(val1,0,val2,0,ptrd);
23538         return cimg::type<double>::nan();
23539       }
23540 
23541       static double mp_complex_pow_sv(_cimg_math_parser& mp) {
23542         const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
23543         double *ptrd = &_mp_arg(1) + 1;
23544         _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
23545         return cimg::type<double>::nan();
23546       }
23547 
23548       static double mp_complex_pow_vs(_cimg_math_parser& mp) {
23549         const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
23550         double *ptrd = &_mp_arg(1) + 1;
23551         _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
23552         return cimg::type<double>::nan();
23553       }
23554 
23555       static double mp_complex_pow_vv(_cimg_math_parser& mp) {
23556         const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
23557         double *ptrd = &_mp_arg(1) + 1;
23558         _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
23559         return cimg::type<double>::nan();
23560       }
23561 
23562       static double mp_complex_cos(_cimg_math_parser& mp) {
23563         const double real = _mp_arg(2), imag = _mp_arg(3);
23564         double *ptrd = &_mp_arg(1) + 1;
23565         ptrd[0] = std::cos(real)*std::cosh(imag);
23566         ptrd[1] = -std::sin(real)*std::sinh(imag);
23567         return cimg::type<double>::nan();
23568       }
23569 
23570       static double mp_complex_sin(_cimg_math_parser& mp) {
23571         const double real = _mp_arg(2), imag = _mp_arg(3);
23572         double *ptrd = &_mp_arg(1) + 1;
23573         ptrd[0] = std::sin(real)*std::cosh(imag);
23574         ptrd[1] = std::cos(real)*std::sinh(imag);
23575         return cimg::type<double>::nan();
23576       }
23577 
23578       static double mp_complex_tan(_cimg_math_parser& mp) {
23579         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
23580         double *ptrd = &_mp_arg(1) + 1;
23581         ptrd[0] = std::sin(2*real)/denom;
23582         ptrd[1] = std::sinh(2*imag)/denom;
23583         return cimg::type<double>::nan();
23584       }
23585 
23586       static double mp_complex_cosh(_cimg_math_parser& mp) {
23587         const double real = _mp_arg(2), imag = _mp_arg(3);
23588         double *ptrd = &_mp_arg(1) + 1;
23589         ptrd[0] = std::cosh(real)*std::cos(imag);
23590         ptrd[1] = std::sinh(real)*std::sin(imag);
23591         return cimg::type<double>::nan();
23592       }
23593 
23594       static double mp_complex_sinh(_cimg_math_parser& mp) {
23595         const double real = _mp_arg(2), imag = _mp_arg(3);
23596         double *ptrd = &_mp_arg(1) + 1;
23597         ptrd[0] = std::sinh(real)*std::cos(imag);
23598         ptrd[1] = std::cosh(real)*std::sin(imag);
23599         return cimg::type<double>::nan();
23600       }
23601 
23602       static double mp_complex_tanh(_cimg_math_parser& mp) {
23603         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag);
23604         double *ptrd = &_mp_arg(1) + 1;
23605         ptrd[0] = std::sinh(2*real)/denom;
23606         ptrd[1] = std::sin(2*imag)/denom;
23607         return cimg::type<double>::nan();
23608       }
23609 
23610       static double mp_continue(_cimg_math_parser& mp) {
23611         mp.break_type = 2;
23612         mp.p_code = mp.p_break - 1;
23613         return cimg::type<double>::nan();
23614       }
23615 
23616       static double mp_convolve(_cimg_math_parser &mp) {
23617         return _mp_correlate(mp,true);
23618       }
23619 
23620       static double mp_copy(_cimg_math_parser& mp) {
23621         return _mp_arg(2);
23622       }
23623 
23624       static double mp_correlate(_cimg_math_parser &mp) {
23625         return _mp_correlate(mp,false);
23626       }
23627 
23628       static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) {
23629         double *ptrd = &_mp_arg(1) + 1;
23630         const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1;
23631         const unsigned int
23632           wA = (unsigned int)mp.opcode[3],
23633           hA = (unsigned int)mp.opcode[4],
23634           dA = (unsigned int)mp.opcode[5],
23635           sA = (unsigned int)mp.opcode[6],
23636           wM = (unsigned int)mp.opcode[8],
23637           hM = (unsigned int)mp.opcode[9],
23638           dM = (unsigned int)mp.opcode[10],
23639           sM = (unsigned int)mp.opcode[11],
23640           boundary_conditions = (unsigned int)_mp_arg(12),
23641           channel_mode = (unsigned int)mp.opcode[14];
23642         const bool
23643           is_normalized = (bool)_mp_arg(13),
23644           interpolation_type = (bool)_mp_arg(30);
23645         const int
23646           xcenter = mp.opcode[15]!=~0U?(int)_mp_arg(15):(int)(~0U>>1),
23647           ycenter = mp.opcode[16]!=~0U?(int)_mp_arg(16):(int)(~0U>>1),
23648           zcenter = mp.opcode[17]!=~0U?(int)_mp_arg(17):(int)(~0U>>1),
23649           xstart = (int)mp.opcode[18],
23650           ystart = (int)mp.opcode[19],
23651           zstart = (int)mp.opcode[20],
23652           xend = (int)mp.opcode[21],
23653           yend = (int)mp.opcode[22],
23654           zend = (int)mp.opcode[23];
23655         const float
23656           xstride = (float)_mp_arg(24),
23657           ystride = (float)_mp_arg(25),
23658           zstride = (float)_mp_arg(26),
23659           xdilation = (float)_mp_arg(27),
23660           ydilation = (float)_mp_arg(28),
23661           zdilation = (float)_mp_arg(29);
23662         CImg<doubleT> res;
23663         if (is_convolve) res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23664                            get_convolve(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23665                                         boundary_conditions,is_normalized,channel_mode,
23666                                         xcenter,ycenter,zcenter,
23667                                         xstart,ystart,zstart,
23668                                         xend,yend,zend,
23669                                         xstride,ystride,zstride,
23670                                         xdilation,ydilation,zdilation,
23671                                         interpolation_type);
23672         else res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23673                get_correlate(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23674                              boundary_conditions,is_normalized,channel_mode,
23675                              xcenter,ycenter,zcenter,
23676                              xstart,ystart,zstart,
23677                              xend,yend,zend,
23678                              xstride,ystride,zstride,
23679                              xdilation,ydilation,zdilation,
23680                              interpolation_type);
23681         CImg<doubleT>(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res;
23682         return cimg::type<double>::nan();
23683       }
23684 
23685       static double mp_cos(_cimg_math_parser& mp) {
23686         return std::cos(_mp_arg(2));
23687       }
23688 
23689       static double mp_cosh(_cimg_math_parser& mp) {
23690         return std::cosh(_mp_arg(2));
23691       }
23692 
23693       static double mp_critical(_cimg_math_parser& mp) {
23694         const ulongT g_target = mp.opcode[1];
23695         cimg_pragma_openmp(critical(mp_critical))
23696         {
23697           for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
23698                mp.p_code<p_end; ++mp.p_code) { // Evaluate body
23699             mp.opcode._data = mp.p_code->_data;
23700             const ulongT target = mp.opcode[1];
23701             mp.mem[target] = _cimg_mp_defunc(mp);
23702           }
23703         }
23704         --mp.p_code;
23705         return mp.mem[g_target];
23706       }
23707 
23708       static double mp_crop(_cimg_math_parser& mp) {
23709         double *ptrd = &_mp_arg(1) + 1;
23710         const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
23711         const unsigned int
23712           dx = (unsigned int)mp.opcode[7],
23713           dy = (unsigned int)mp.opcode[8],
23714           dz = (unsigned int)mp.opcode[9],
23715           dc = (unsigned int)mp.opcode[10];
23716         const unsigned int boundary_conditions = (unsigned int)_mp_arg(11);
23717         unsigned int ind = (unsigned int)mp.opcode[2];
23718         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23719         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
23720         if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
23721         else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
23722                                                                  x + dx - 1,y + dy - 1,
23723                                                                  z + dz - 1,c + dc - 1,
23724                                                                  boundary_conditions);
23725         return cimg::type<double>::nan();
23726       }
23727 
23728       static double mp_cross(_cimg_math_parser& mp) {
23729         CImg<doubleT>
23730           vout(&_mp_arg(1) + 1,1,3,1,1,true),
23731           v1(&_mp_arg(2) + 1,1,3,1,1,true),
23732           v2(&_mp_arg(3) + 1,1,3,1,1,true);
23733         (vout = v1).cross(v2);
23734         return cimg::type<double>::nan();
23735       }
23736 
23737       static double mp_cut(_cimg_math_parser& mp) {
23738         double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
23739         return val<cmin?cmin:val>cmax?cmax:val;
23740       }
23741 
23742       static double mp_da_back_or_pop(_cimg_math_parser& mp) {
23743         const bool is_pop = (bool)mp.opcode[4];
23744         const char *const s_op = is_pop?"da_pop":"da_back";
23745         const unsigned int
23746           dim = (unsigned int)mp.opcode[2],
23747           ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
23748         double *const ptrd = &_mp_arg(1) + (dim>1?1:0);
23749         CImg<T> &img = mp.listout[ind];
23750         int siz = img?(int)img[img._height - 1]:0;
23751         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
23752           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
23753                                       "Specified image (%d,%d,%d,%d) cannot be used as dynamic array%s.",
23754                                       mp.imgin.pixel_type(),s_op,img.width(),img.height(),img.depth(),img.spectrum(),
23755                                       img._width==1 && img._depth==1?"":" (contains invalid element counter)");
23756         if (!siz)
23757           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
23758                                       "Specified dynamic array #%d contains no elements.",
23759                                       mp.imgin.pixel_type(),s_op,(int)_mp_arg(3));
23760 
23761         double ret = cimg::type<double>::nan();
23762         if (dim<1) ret = img[siz - 1]; // Scalar element
23763         else cimg_forC(img,c) ptrd[c] = img(0,siz - 1,0,c); // Vector element
23764         if (is_pop) { // Remove element from array
23765           --siz;
23766           if (img.height()>32 && siz<2*img.height()/3) // Reduce size of dynamic array
23767             img.resize(1,std::max(2*siz + 1,32),1,1,0);
23768           img[img._height - 1] = (T)siz;
23769         }
23770         return ret;
23771       }
23772 
23773       static double mp_da_insert(_cimg_math_parser& mp) {
23774         const char *const s_op = mp.opcode[3]==~0U?"da_push":"da_insert";
23775         const unsigned int
23776           dim = (unsigned int)mp.opcode[4],
23777           _dim = std::max(1U,dim),
23778           nb_elts = (unsigned int)mp.opcode[5] - 6,
23779           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23780 
23781         CImg<T> &img = mp.listout[ind];
23782         const int
23783           siz = img?(int)img[img._height - 1]:0,
23784           pos0 = mp.opcode[3]==~0U?siz:(int)_mp_arg(3),
23785           pos = pos0<0?pos0 + siz:pos0;
23786 
23787         if (img && _dim!=img._spectrum)
23788           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
23789                                       "Element to insert has invalid size %u (should be %u).",
23790                                       mp.imgin.pixel_type(),s_op,_dim,img._spectrum);
23791         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
23792           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
23793                                       "Specified image (%d,%d,%d,%d) cannot be used as dynamic array%s.",
23794                                       mp.imgin.pixel_type(),s_op,img.width(),img.height(),img.depth(),img.spectrum(),
23795                                       img._width==1 && img._depth==1?"":" (contains invalid element counter)");
23796         if (pos<0 || pos>siz)
23797           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
23798                                       "Invalid position %d (not in range -%d...%d).",
23799                                       mp.imgin.pixel_type(),s_op,pos0,siz,siz);
23800 
23801         if (siz + nb_elts + 1>=img._height) // Increase size of dynamic array, if necessary
23802           img.resize(1,2*siz + nb_elts + 1,1,_dim,0);
23803 
23804         if (pos!=siz) // Move existing data in dynamic array
23805           cimg_forC(img,c) std::memmove(img.data(0,pos + nb_elts,0,c),img.data(0,pos,0,c),(siz - pos)*sizeof(T));
23806 
23807         if (!dim) // Scalar or vector1() elements
23808           for (unsigned int k = 0; k<nb_elts; ++k) img[pos + k] = (T)_mp_arg(6 + k);
23809         else // vectorN() elements, with N>1
23810           for (unsigned int k = 0; k<nb_elts; ++k) {
23811             double *ptrs = &_mp_arg(6 + k) + 1;
23812             cimg_forC(img,c) img(0,pos + k,0,c) = ptrs[c];
23813           }
23814         img[img._height - 1] = (T)(siz + nb_elts);
23815         return cimg::type<double>::nan();
23816       }
23817 
23818       static double mp_da_remove(_cimg_math_parser& mp) {
23819         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23820         CImg<T> &img = mp.listout[ind];
23821         int siz = img?(int)img[img._height - 1]:0;
23822         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
23823           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
23824                                       "Specified image (%d,%d,%d,%d) cannot be used as dynamic array%s.",
23825                                       mp.imgin.pixel_type(),img.width(),img.height(),img.depth(),img.spectrum(),
23826                                       img._width==1 && img._depth==1?"":" (contains invalid element counter)");
23827         if (img._height<2)
23828           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
23829                                       "Dynamic array is empty.",
23830                                       mp.imgin.pixel_type());
23831         int
23832           start0 = mp.opcode[3]==~0U?siz - 1:_mp_arg(3),
23833           end0 = mp.opcode[4]==~0U?start0:_mp_arg(4),
23834           start = start0<0?start0 + siz:start0,
23835           end = end0<0?end0 + siz:end0;
23836         if (start<0 || start>=siz || end<0 || end>=siz || start>end)
23837           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
23838                                       "Invalid starting (%d) and ending (%d) positions "
23839                                       "(not ordered, in range -%d...%d).",
23840                                       mp.imgin.pixel_type(),start0,end0,siz,siz - 1);
23841         if (end<siz - 1) // Move remaining data in dynamic array
23842           cimg_forC(img,c) std::memmove(img.data(0,start,0,c),img.data(0,end + 1,0,c),(siz - 1 - end)*sizeof(T));
23843         siz-=end - start + 1;
23844         if (img.height()>32 && siz<2*img.height()/3) // Reduce size of dynamic array
23845           img.resize(1,std::max(2*siz + 1,32),1,1,0);
23846         img[img._height - 1] = (T)siz;
23847         return cimg::type<double>::nan();
23848       }
23849 
23850       static double mp_da_size(_cimg_math_parser& mp) {
23851         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23852         CImg<T> &img = mp.listout[ind];
23853         const int siz = img?(int)img[img._height - 1]:0;
23854         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
23855           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': "
23856                                       "Specified image (%d,%d,%d,%d) cannot be used as dynamic array%s.",
23857                                       mp.imgin.pixel_type(),img.width(),img.height(),img.depth(),img.spectrum(),
23858                                       img._width==1 && img._depth==1?"":" (contains invalid element counter)");
23859         return siz;
23860       }
23861 
23862       static double mp_date(_cimg_math_parser& mp) {
23863         const unsigned int
23864           siz_out = (unsigned int)mp.opcode[2],
23865           siz_arg1 = (unsigned int)mp.opcode[4],
23866           siz_arg2 = (unsigned int)mp.opcode[6];
23867         double *ptr_out = &_mp_arg(1) + (siz_out?1:0);
23868         const double
23869           *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0),
23870           *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1;
23871 
23872         if (!ptr_arg2) { // No filename specified
23873           if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1);
23874           if (siz_arg1==~0U) for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = k;
23875           else for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23876           cimg::date(ptr_out,siz_out);
23877           return cimg::type<double>::nan();
23878         }
23879 
23880         // Filename specified.
23881         CImg<charT> ss(siz_arg2 + 1);
23882         cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i];
23883         ss.back() = 0;
23884         if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1);
23885         for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23886         cimg::fdate(ss,ptr_out,siz_out);
23887         return cimg::type<double>::nan();
23888       }
23889 
23890       static double mp_debug(_cimg_math_parser& mp) {
23891         CImg<charT> expr(mp.opcode[2] - 4);
23892         {
23893           const ulongT *ptrs = mp.opcode._data + 4;
23894           cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23895         }
23896         cimg::strellipsize(expr);
23897         const ulongT g_target = mp.opcode[1];
23898 
23899 #if cimg_use_openmp==0
23900         const unsigned int n_thread = 0;
23901 #else
23902         const unsigned int n_thread = omp_get_thread_num();
23903 #endif
23904         cimg_pragma_openmp(critical(mp_debug))
23905         {
23906           std::fprintf(cimg::output(),
23907                        "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23908                        "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
23909                        (void*)&mp,n_thread,mp.debug_indent,' ',
23910                        expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
23911           std::fflush(cimg::output());
23912           mp.debug_indent+=3;
23913         }
23914         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[3];
23915         CImg<ulongT> _op;
23916         for ( ; mp.p_code<p_end; ++mp.p_code) {
23917           const CImg<ulongT> &op = *mp.p_code;
23918           mp.opcode._data = op._data;
23919 
23920           _op.assign(1,op._height - 1);
23921           const ulongT *ptrs = op._data + 1;
23922           for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
23923             *ptrd = *(ptrs++);
23924 
23925           const ulongT target = mp.opcode[1];
23926           mp.mem[target] = _cimg_mp_defunc(mp);
23927           cimg_pragma_openmp(critical(mp_debug))
23928           {
23929             std::fprintf(cimg::output(),
23930                          "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23931                          "Opcode %p = [ %p,%s ] -> mem[%u] = %.17g",
23932                          (void*)&mp,n_thread,mp.debug_indent,' ',
23933                          (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
23934                          (unsigned int)target,mp.mem[target]);
23935             std::fflush(cimg::output());
23936           }
23937         }
23938         cimg_pragma_openmp(critical(mp_debug))
23939         {
23940           mp.debug_indent-=3;
23941           std::fprintf(cimg::output(),
23942             "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23943             "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)",
23944             (void*)&mp,n_thread,mp.debug_indent,' ',
23945             expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
23946           std::fflush(cimg::output());
23947         }
23948         --mp.p_code;
23949         return mp.mem[g_target];
23950       }
23951 
23952       static double mp_decrement(_cimg_math_parser& mp) {
23953         return _mp_arg(2) - 1;
23954       }
23955 
23956       static double mp_deg2rad(_cimg_math_parser& mp) {
23957         return _mp_arg(2)*cimg::PI/180;
23958       }
23959 
23960       static double mp_det(_cimg_math_parser& mp) {
23961         const double *ptrs = &_mp_arg(2) + 1;
23962         const unsigned int k = (unsigned int)mp.opcode[3];
23963         return CImg<doubleT>(ptrs,k,k,1,1,true).det();
23964       }
23965 
23966       static double mp_diag(_cimg_math_parser& mp) {
23967         const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3;
23968         double *ptrd = &_mp_arg(1) + 1;
23969         std::memset(ptrd,0,siz*siz*sizeof(double));
23970         for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; }
23971         return cimg::type<double>::nan();
23972       }
23973 
23974       static double mp_display_memory(_cimg_math_parser& mp) {
23975         cimg::unused(mp);
23976         std::fputc('\n',cimg::output());
23977         CImg<charT> title(128);
23978         cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width);
23979         mp.mem.display(title);
23980         return cimg::type<double>::nan();
23981       }
23982 
23983       static double mp_display(_cimg_math_parser& mp) {
23984         const unsigned int
23985           _siz = (unsigned int)mp.opcode[3],
23986           siz = _siz?_siz:1;
23987         const double *const ptr = &_mp_arg(1) + (_siz?1:0);
23988         const int
23989           w = (int)_mp_arg(4),
23990           h = (int)_mp_arg(5),
23991           d = (int)_mp_arg(6),
23992           s = (int)_mp_arg(7);
23993         CImg<doubleT> img;
23994         if (w>0 && h>0 && d>0 && s>0) {
23995           if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
23996           else img.assign(ptr,siz).resize(w,h,d,s,-1);
23997         } else img.assign(ptr,1,siz,1,1,true);
23998 
23999         CImg<charT> expr(mp.opcode[2] - 8);
24000         const ulongT *ptrs = mp.opcode._data + 8;
24001         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
24002         ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
24003         cimg::strellipsize(expr);
24004         std::fputc('\n',cimg::output());
24005         img.display(expr._data);
24006         return cimg::type<double>::nan();
24007       }
24008 
24009       static double mp_div(_cimg_math_parser& mp) {
24010         return _mp_arg(2)/_mp_arg(3);
24011       }
24012 
24013       static double mp_dot(_cimg_math_parser& mp) {
24014         const unsigned int siz = (unsigned int)mp.opcode[4];
24015         return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
24016           dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
24017       }
24018 
24019       static double mp_do(_cimg_math_parser& mp) {
24020         const ulongT
24021           mem_body = mp.opcode[1],
24022           mem_cond = mp.opcode[2];
24023         const CImg<ulongT>
24024           *const p_body = ++mp.p_code,
24025           *const p_cond = p_body + mp.opcode[3],
24026           *const p_end = p_cond + mp.opcode[4];
24027         const unsigned int vsiz = (unsigned int)mp.opcode[5];
24028         if (mp.opcode[6]) { // Set default value for result and condition if necessary
24029           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
24030           else mp.mem[mem_body] = cimg::type<double>::nan();
24031         }
24032         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
24033 
24034         const unsigned int _break_type = mp.break_type;
24035         mp.break_type = 0;
24036         do {
24037           for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
24038             mp.opcode._data = mp.p_code->_data;
24039             const ulongT target = mp.opcode[1];
24040             mp.mem[target] = _cimg_mp_defunc(mp);
24041           }
24042           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24043           for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
24044             mp.opcode._data = mp.p_code->_data;
24045             const ulongT target = mp.opcode[1];
24046             mp.mem[target] = _cimg_mp_defunc(mp);
24047           }
24048           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24049         } while (mp.mem[mem_cond]);
24050         mp.break_type = _break_type;
24051         mp.p_code = p_end - 1;
24052         return mp.mem[mem_body];
24053       }
24054 
24055       static double mp_draw(_cimg_math_parser& mp) {
24056         const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
24057         unsigned int ind = (unsigned int)mp.opcode[3];
24058         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
24059         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24060         unsigned int
24061           dx = (unsigned int)mp.opcode[8],
24062           dy = (unsigned int)mp.opcode[9],
24063           dz = (unsigned int)mp.opcode[10],
24064           dc = (unsigned int)mp.opcode[11];
24065         dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
24066         dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
24067         dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
24068         dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
24069 
24070         const ulongT sizS = mp.opcode[2];
24071         if (sizS<(ulongT)dx*dy*dz*dc)
24072           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
24073                                       "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
24074                                       "(%lu values) do not match.",
24075                                       mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
24076         CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
24077         const float opacity = (float)_mp_arg(12);
24078 
24079         if (img._data) {
24080           if (mp.opcode[13]!=~0U) { // Opacity mask specified
24081             const ulongT sizM = mp.opcode[14];
24082             if (sizM<(ulongT)dx*dy*dz)
24083               throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
24084                                           "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
24085                                           "(%lu values) do not match.",
24086                                           mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
24087             const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
24088             img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
24089           } else img.draw_image(x,y,z,c,S,opacity);
24090         }
24091         return cimg::type<double>::nan();
24092       }
24093 
24094       static double mp_echo(_cimg_math_parser& mp) {
24095         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
24096         if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type<double>::nan(); } // No arguments
24097         CImgList<charT> _str;
24098         CImg<charT> it;
24099         for (unsigned int n = 0; n<nb_args; ++n) {
24100           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
24101           if (siz) { // Vector argument -> string
24102             const double *ptr = &_mp_arg(3 + 2*n) + 1;
24103             unsigned int l = 0;
24104             while (l<siz && ptr[l]) ++l;
24105             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
24106           } else { // Scalar argument -> number
24107             it.assign(24);
24108             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
24109             CImg<charT>::string(it,false,true).move_to(_str);
24110           }
24111         }
24112         CImg(1,1,1,1,0).move_to(_str);
24113         const CImg<charT> str = _str>'x';
24114         std::fprintf(cimg::output(),"\n%s",str._data);
24115         return cimg::type<double>::nan();
24116       }
24117 
24118       static double mp_ellipse(_cimg_math_parser& mp) {
24119         const unsigned int i_end = (unsigned int)mp.opcode[2];
24120         unsigned int ind = (unsigned int)mp.opcode[3];
24121         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
24122         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24123         CImg<T> color(img._spectrum,1,1,1,0);
24124         bool is_invalid_arguments = false, is_outlined = false;
24125         float r1 = 0, r2 = 0, angle = 0, opacity = 1;
24126         unsigned int i = 4, pattern = ~0U;
24127         int x0 = 0, y0 = 0;
24128         if (i>=i_end) is_invalid_arguments = true;
24129         else {
24130           x0 = (int)cimg::round(_mp_arg(i++));
24131           if (i>=i_end) is_invalid_arguments = true;
24132           else {
24133             y0 = (int)cimg::round(_mp_arg(i++));
24134             if (i>=i_end) is_invalid_arguments = true;
24135             else {
24136               r1 = (float)_mp_arg(i++);
24137               if (i>=i_end) r2 = r1;
24138               else {
24139                 r2 = (float)_mp_arg(i++);
24140                 if (i<i_end) {
24141                   angle = (float)(_mp_arg(i++)*180/cimg::PI);
24142                   if (i<i_end) {
24143                     opacity = (float)_mp_arg(i++);
24144                     if (r1<0 && r2<0) {
24145                       pattern = (unsigned int)_mp_arg(i++);
24146                       is_outlined = true;
24147                       r1 = -r1; r2 = -r2;
24148                     }
24149                     if (i<i_end) {
24150                       cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
24151                       else { color.resize(k,1,1,1,-1); break; }
24152                       color.resize(img._spectrum,1,1,1,0,2);
24153                     }
24154                   }
24155                 }
24156               }
24157             }
24158           }
24159         }
24160         if (!is_invalid_arguments) {
24161           if (is_outlined) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity,pattern);
24162           else img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
24163         } else {
24164           CImg<doubleT> args(i_end - 4);
24165           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
24166           if (ind==~0U)
24167             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
24168                                         "Invalid arguments '%s'. ",
24169                                         mp.imgin.pixel_type(),args.value_string()._data);
24170           else
24171             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
24172                                         "Invalid arguments '#%u%s%s'. ",
24173                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
24174         }
24175         return cimg::type<double>::nan();
24176       }
24177 
24178       static double mp_eq(_cimg_math_parser& mp) {
24179         return (double)(_mp_arg(2)==_mp_arg(3));
24180       }
24181 
24182       static double mp_erf(_cimg_math_parser& mp) {
24183         return std::erf(_mp_arg(2));
24184       }
24185 
24186       static double mp_erfinv(_cimg_math_parser& mp) {
24187         return cimg::erfinv(_mp_arg(2));
24188       }
24189 
24190       static double mp_exp(_cimg_math_parser& mp) {
24191         return std::exp(_mp_arg(2));
24192       }
24193 
24194       static double mp_expr(_cimg_math_parser& mp) {
24195         const unsigned int
24196           sizs = (unsigned int)mp.opcode[3],
24197           w = (unsigned int)mp.opcode[4],
24198           h = (unsigned int)mp.opcode[5],
24199           d = (unsigned int)mp.opcode[6],
24200           s = (unsigned int)mp.opcode[7],
24201           sizd = w*h*d*s;
24202         const double *ptrs = &_mp_arg(2) + 1;
24203         double *ptrd = &_mp_arg(1);
24204         CImg<charT> ss(sizs + 1);
24205         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
24206         ss.back() = 0;
24207         if (!sizd) return CImg<T>(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result
24208         CImg<doubleT>(++ptrd,w,h,d,s,true) = CImg<T>(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout);
24209         return cimg::type<double>::nan();
24210       }
24211 
24212       static double mp_eye(_cimg_math_parser& mp) {
24213         double *ptrd = &_mp_arg(1) + 1;
24214         const unsigned int k = (unsigned int)mp.opcode[2];
24215         CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
24216         return cimg::type<double>::nan();
24217       }
24218 
24219       static double mp_f2ui(_cimg_math_parser& mp) {
24220         return (double)cimg::float2uint((float)_mp_arg(2));
24221       }
24222 
24223       static double mp_factorial(_cimg_math_parser& mp) {
24224         return cimg::factorial((int)_mp_arg(2));
24225       }
24226 
24227       static double mp_fibonacci(_cimg_math_parser& mp) {
24228         return cimg::fibonacci((int)_mp_arg(2));
24229       }
24230 
24231       static double mp_fill(_cimg_math_parser& mp) {
24232         unsigned int siz = (unsigned int)mp.opcode[2];
24233         double
24234           *ptrd = &_mp_arg(1),
24235           *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
24236           *const ptrs = &_mp_arg(4);
24237         if (siz) ++ptrd; else ++siz; // Fill vector-valued slot
24238         const CImg<ulongT>
24239           *const p_body = ++mp.p_code,
24240           *const p_end = p_body + mp.opcode[5];
24241         const unsigned int _break_type = mp.break_type;
24242         mp.break_type = 0;
24243 
24244         unsigned int it = 0;
24245         if (ptrc) { // Version with loop variable (3 arguments)
24246           while (it<siz) {
24247             *ptrc = (double)it;
24248             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
24249               mp.opcode._data = mp.p_code->_data;
24250               const ulongT target = mp.opcode[1];
24251               mp.mem[target] = _cimg_mp_defunc(mp);
24252             }
24253             if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24254             else ptrd[it] = *ptrs;
24255             ++it;
24256           }
24257           *ptrc = (double)it;
24258         } else // Version without loop variable (2 arguments)
24259           while (it<siz) {
24260             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
24261               mp.opcode._data = mp.p_code->_data;
24262               const ulongT target = mp.opcode[1];
24263               mp.mem[target] = _cimg_mp_defunc(mp);
24264             }
24265             if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24266             else ptrd[it] = *ptrs;
24267             ++it;
24268           }
24269 
24270         mp.break_type = _break_type;
24271         mp.p_code = p_end - 1;
24272         return *ptrd;
24273       }
24274 
24275       static double mp_find(_cimg_math_parser& mp) {
24276         const int _step = (int)_mp_arg(6), step = _step?_step:-1;
24277         const ulongT siz = (ulongT)mp.opcode[3];
24278         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1);
24279         if (ind<0 || ind>=(longT)siz) return -1.;
24280         const double
24281           *const ptrb = &_mp_arg(2) + 1,
24282           *const ptre = ptrb + siz,
24283           val = _mp_arg(4),
24284           *ptr = ptrb + ind;
24285 
24286         // Forward search
24287         if (step>0) {
24288           while (ptr<ptre && *ptr!=val) ptr+=step;
24289           return ptr>=ptre?-1.:(double)(ptr - ptrb);
24290         }
24291 
24292         // Backward search.
24293         while (ptr>=ptrb && *ptr!=val) ptr+=step;
24294         return ptr<ptrb?-1.:(double)(ptr - ptrb);
24295       }
24296 
24297       static double mp_find_seq(_cimg_math_parser& mp) {
24298         const int _step = (int)_mp_arg(7), step = _step?_step:-1;
24299         const ulongT
24300           siz1 = (ulongT)mp.opcode[3],
24301           siz2 = (ulongT)mp.opcode[5];
24302         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):step>0?0:siz1 - 1);
24303         if (ind<0 || ind>=(longT)siz1) return -1.;
24304         const double
24305           *const ptr1b = &_mp_arg(2) + 1,
24306           *const ptr1e = ptr1b + siz1,
24307           *const ptr2b = &_mp_arg(4) + 1,
24308           *const ptr2e = ptr2b + siz2,
24309           *ptr1 = ptr1b + ind,
24310           *p1 = 0,
24311           *p2 = 0;
24312 
24313         // Forward search.
24314         if (step>0) {
24315           do {
24316             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
24317             if (ptr1>=ptr1e) return -1.;
24318             p1 = ptr1 + 1;
24319             p2 = ptr2b + 1;
24320             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24321           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
24322           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24323         }
24324 
24325         // Backward search.
24326         do {
24327           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
24328           if (ptr1<ptr1b) return -1.;
24329           p1 = ptr1 + 1;
24330           p2 = ptr2b + 1;
24331           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24332         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
24333         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24334       }
24335 
24336       static double mp_floor(_cimg_math_parser& mp) {
24337         return std::floor(_mp_arg(2));
24338       }
24339 
24340       static double mp_for(_cimg_math_parser& mp) {
24341         const ulongT
24342           mem_body = mp.opcode[1],
24343           mem_cond = mp.opcode[3];
24344         const CImg<ulongT>
24345           *const p_init = ++mp.p_code,
24346           *const p_cond = p_init + mp.opcode[4],
24347           *const p_body = p_cond + mp.opcode[5],
24348           *const p_post = p_body + mp.opcode[6],
24349           *const p_end = p_post + mp.opcode[7];
24350         const unsigned int vsiz = (unsigned int)mp.opcode[2];
24351         bool is_cond = false;
24352         if (mp.opcode[8]) { // Set default value for result and condition if necessary
24353           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
24354           else mp.mem[mem_body] = cimg::type<double>::nan();
24355         }
24356         if (mp.opcode[9]) mp.mem[mem_cond] = 0;
24357         const unsigned int _break_type = mp.break_type;
24358         mp.break_type = 0;
24359 
24360         for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
24361           mp.opcode._data = mp.p_code->_data;
24362           const ulongT target = mp.opcode[1];
24363           mp.mem[target] = _cimg_mp_defunc(mp);
24364         }
24365 
24366         if (!mp.break_type) do {
24367             for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
24368               mp.opcode._data = mp.p_code->_data;
24369               const ulongT target = mp.opcode[1];
24370               mp.mem[target] = _cimg_mp_defunc(mp);
24371             }
24372             if (mp.break_type==1) break;
24373 
24374             is_cond = (bool)mp.mem[mem_cond];
24375             if (is_cond && !mp.break_type) {
24376               for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
24377                 mp.opcode._data = mp.p_code->_data;
24378                 const ulongT target = mp.opcode[1];
24379                 mp.mem[target] = _cimg_mp_defunc(mp);
24380               }
24381               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24382 
24383               for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
24384                 mp.opcode._data = mp.p_code->_data;
24385                 const ulongT target = mp.opcode[1];
24386                 mp.mem[target] = _cimg_mp_defunc(mp);
24387               }
24388               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
24389             }
24390           } while (is_cond);
24391 
24392         mp.break_type = _break_type;
24393         mp.p_code = p_end - 1;
24394         return mp.mem[mem_body];
24395       }
24396 
24397       static double mp_fsize(_cimg_math_parser& mp) {
24398         const double *ptrs = &_mp_arg(2) + 1;
24399         const ulongT siz = (ulongT)mp.opcode[3];
24400         CImg<charT> ss(siz + 1);
24401         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24402         ss.back() = 0;
24403         return (double)cimg::fsize(ss);
24404       }
24405 
24406       static double mp_g(_cimg_math_parser& mp) {
24407         cimg::unused(mp);
24408         return cimg::grand(&mp.rng);
24409       }
24410 
24411       static double mp_gauss(_cimg_math_parser& mp) {
24412         const double x = _mp_arg(2), s = _mp_arg(3);
24413         return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
24414       }
24415 
24416 #ifdef cimg_mp_func_get
24417       static double mp_get(_cimg_math_parser& mp) {
24418         const double *ptrs = &_mp_arg(2) + 1;
24419         double *ptrd = &_mp_arg(1);
24420         const unsigned int
24421           sizs = (unsigned int)mp.opcode[3],
24422           sizd = (unsigned int)mp.opcode[4];
24423         const bool to_string = (bool)mp.opcode[5];
24424         CImg<charT> ss(sizs + 1);
24425         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
24426         ss.back() = 0;
24427         if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data);
24428         else cimg_mp_func_get(ptrd,0,to_string,ss._data);
24429         return cimg::type<double>::nan();
24430       }
24431 #endif
24432 
24433       static double mp_gcd(_cimg_math_parser& mp) {
24434         return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
24435       }
24436 
24437 #ifdef cimg_mp_func_name
24438       static double mp_name(_cimg_math_parser& mp) {
24439         double *const ptr = &_mp_arg(1) + 1;
24440         const unsigned int siz = (unsigned int)mp.opcode[3];
24441         unsigned int ind = (unsigned int)mp.opcode[2];
24442         if (ind==~0U) std::memset(ptr,0,siz*sizeof(double));
24443         else {
24444           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24445           cimg_mp_func_name(ind,ptr,siz);
24446         }
24447         return cimg::type<double>::nan();
24448       }
24449 #endif
24450 
24451       static double mp_gt(_cimg_math_parser& mp) {
24452         return (double)(_mp_arg(2)>_mp_arg(3));
24453       }
24454 
24455       static double mp_gte(_cimg_math_parser& mp) {
24456         return (double)(_mp_arg(2)>=_mp_arg(3));
24457       }
24458 
24459       static double mp_i(_cimg_math_parser& mp) {
24460         return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
24461                                        (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
24462       }
24463 
24464       static double mp_if(_cimg_math_parser& mp) {
24465         const bool is_cond = (bool)_mp_arg(2);
24466         const ulongT
24467           mem_left = mp.opcode[3],
24468           mem_right = mp.opcode[4];
24469         const CImg<ulongT>
24470           *const p_right = ++mp.p_code + mp.opcode[5],
24471           *const p_end = p_right + mp.opcode[6];
24472         const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
24473         if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
24474             mp.opcode._data = mp.p_code->_data;
24475             const ulongT target = mp.opcode[1];
24476             mp.mem[target] = _cimg_mp_defunc(mp);
24477           }
24478         else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
24479             mp.opcode._data = mp.p_code->_data;
24480             const ulongT target = mp.opcode[1];
24481             mp.mem[target] = _cimg_mp_defunc(mp);
24482           }
24483         if (mp.p_code==mp.p_break) --mp.p_code;
24484         else mp.p_code = p_end - 1;
24485         if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
24486         return mp.mem[is_cond?mem_left:mem_right];
24487       }
24488 
24489       static double mp_image_d(_cimg_math_parser& mp) {
24490         unsigned int ind = (unsigned int)mp.opcode[2];
24491         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24492         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24493         return (double)img.depth();
24494       }
24495 
24496       static double mp_image_display(_cimg_math_parser& mp) {
24497         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24498         cimg::mutex(6);
24499         CImg<T> &img = mp.listout[ind];
24500         CImg<charT> title(256);
24501         std::fputc('\n',cimg::output());
24502         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
24503         img.display(title);
24504         cimg::mutex(6,0);
24505         return cimg::type<double>::nan();
24506       }
24507 
24508       static double mp_image_h(_cimg_math_parser& mp) {
24509         unsigned int ind = (unsigned int)mp.opcode[2];
24510         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24511         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24512         return (double)img.height();
24513       }
24514 
24515       static double mp_image_median(_cimg_math_parser& mp) {
24516         unsigned int ind = (unsigned int)mp.opcode[2];
24517         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24518         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24519         return (double)img.median();
24520       }
24521 
24522       static double mp_image_norm(_cimg_math_parser& mp) {
24523         unsigned int ind = (unsigned int)mp.opcode[2];
24524         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24525         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24526         return (double)img.magnitude();
24527       }
24528 
24529       static double mp_image_print(_cimg_math_parser& mp) {
24530         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24531         cimg::mutex(6);
24532         CImg<T> &img = mp.listout[ind];
24533         CImg<charT> title(256);
24534         std::fputc('\n',cimg::output());
24535         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
24536         img.print(title);
24537         cimg::mutex(6,0);
24538         return cimg::type<double>::nan();
24539       }
24540 
24541       static double mp_image_resize(_cimg_math_parser& mp) {
24542         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
24543         cimg::mutex(6);
24544         CImg<T> &img = mp.listout[ind];
24545         const double
24546           _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
24547           _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
24548           _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
24549           _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
24550         const unsigned int
24551           w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
24552           h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
24553           d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
24554           s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
24555           interp = (int)_mp_arg(7);
24556         if (mp.is_fill && img._data==mp.imgout._data) {
24557           cimg::mutex(6,0);
24558           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
24559                                       "Cannot both fill and resize image (%u,%u,%u,%u) "
24560                                       "to new dimensions (%u,%u,%u,%u).",
24561                                       img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
24562         }
24563         const unsigned int
24564           boundary = (int)_mp_arg(8);
24565         const float
24566           cx = (float)_mp_arg(9),
24567           cy = (float)_mp_arg(10),
24568           cz = (float)_mp_arg(11),
24569           cc = (float)_mp_arg(12);
24570         img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
24571         cimg::mutex(6,0);
24572         return cimg::type<double>::nan();
24573       }
24574 
24575       static double mp_image_s(_cimg_math_parser& mp) {
24576         unsigned int ind = (unsigned int)mp.opcode[2];
24577         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24578         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24579         return (double)img.spectrum();
24580       }
24581 
24582       static double mp_image_sort(_cimg_math_parser& mp) {
24583         const bool is_increasing = (bool)_mp_arg(3);
24584         const unsigned int
24585           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
24586           axis = (unsigned int)_mp_arg(4);
24587         cimg::mutex(6);
24588         CImg<T> &img = mp.listout[ind];
24589         img.sort(is_increasing,
24590                  axis==0 || axis=='x'?'x':
24591                  axis==1 || axis=='y'?'y':
24592                  axis==2 || axis=='z'?'z':
24593                  axis==3 || axis=='c'?'c':0);
24594         cimg::mutex(6,0);
24595         return cimg::type<double>::nan();
24596       }
24597 
24598       static double mp_image_stats(_cimg_math_parser& mp) {
24599         double *ptrd = &_mp_arg(1) + 1;
24600         unsigned int ind = (unsigned int)mp.opcode[2];
24601         if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
24602         else {
24603           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24604           CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
24605         }
24606         return cimg::type<double>::nan();
24607       }
24608 
24609       static double mp_image_w(_cimg_math_parser& mp) {
24610         unsigned int ind = (unsigned int)mp.opcode[2];
24611         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24612         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24613         return (double)img.width();
24614       }
24615 
24616       static double mp_image_wh(_cimg_math_parser& mp) {
24617         unsigned int ind = (unsigned int)mp.opcode[2];
24618         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24619         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24620         return (double)img.width()*img.height();
24621       }
24622 
24623       static double mp_image_whd(_cimg_math_parser& mp) {
24624         unsigned int ind = (unsigned int)mp.opcode[2];
24625         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24626         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24627         return (double)img.width()*img.height()*img.depth();
24628       }
24629 
24630       static double mp_image_whds(_cimg_math_parser& mp) {
24631         unsigned int ind = (unsigned int)mp.opcode[2];
24632         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24633         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24634         return (double)img.width()*img.height()*img.depth()*img.spectrum();
24635       }
24636 
24637       static double mp_increment(_cimg_math_parser& mp) {
24638         return _mp_arg(2) + 1;
24639       }
24640 
24641       static double mp_inrange(_cimg_math_parser& mp) {
24642         const unsigned int sizd = (unsigned int)mp.opcode[2];
24643         const bool
24644           include_m = (bool)_mp_arg(9),
24645           include_M = (bool)_mp_arg(10);
24646         if (!sizd) { // Scalar result
24647           const double val = _mp_arg(3);
24648           const double m = _mp_arg(5), M = _mp_arg(7);
24649           if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24650           else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24651         }
24652 
24653         // Vector result
24654         const unsigned int
24655           siz1 = (unsigned int)mp.opcode[4],
24656           siz2 = (unsigned int)mp.opcode[6],
24657           siz3 = (unsigned int)mp.opcode[8],
24658           off1 = siz1?1:0,
24659           off2 = siz2?1:0,
24660           off3 = siz3?1:0;
24661         double *ptrd = &_mp_arg(1) + 1;
24662         const double
24663           *ptr1 = &_mp_arg(3) + off1,
24664           *ptr2 = &_mp_arg(5) + off2,
24665           *ptr3 = &_mp_arg(7) + off3;
24666         for (unsigned int k = 0; k<sizd; ++k) {
24667           const double val = *ptr1;
24668           const double m = *ptr2, M = *ptr3;
24669           if (M>=m)
24670             ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24671           else
24672             ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24673           ptr1+=off1;
24674           ptr2+=off2;
24675           ptr3+=off3;
24676         }
24677         return cimg::type<double>::nan();
24678       }
24679 
24680       static double mp_int(_cimg_math_parser& mp) {
24681         return (double)(longT)_mp_arg(2);
24682       }
24683 
24684       static double mp_ioff(_cimg_math_parser& mp) {
24685         const unsigned int
24686           boundary_conditions = (unsigned int)_mp_arg(3);
24687         const CImg<T> &img = mp.imgin;
24688         const longT
24689           off = (longT)_mp_arg(2),
24690           whds = (longT)img.size();
24691         if (off>=0 && off<whds) return (double)img[off];
24692         if (img._data) switch (boundary_conditions) {
24693           case 3 : { // Mirror
24694             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24695             return (double)img[moff<whds?moff:whds2 - moff - 1];
24696           }
24697           case 2 : // Periodic
24698             return (double)img[cimg::mod(off,whds)];
24699           case 1 : // Neumann
24700             return (double)img[off<0?0:whds - 1];
24701           default : // Dirichlet
24702             return 0;
24703           }
24704         return 0;
24705       }
24706 
24707       static double mp_isbool(_cimg_math_parser& mp) {
24708         const double val = _mp_arg(2);
24709         return (double)(val==0. || val==1.);
24710       }
24711 
24712       static double mp_isdir(_cimg_math_parser& mp) {
24713         const unsigned int siz = (unsigned int)mp.opcode[3];
24714         const double *ptrs = &_mp_arg(2) + (siz?1:0);
24715         if (!siz) { char str[2] = { 0 }; *str = *ptrs; return (double)cimg::is_directory(str); }
24716         CImg<charT> ss(siz + 1);
24717         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24718         ss.back() = 0;
24719         return (double)cimg::is_directory(ss);
24720       }
24721 
24722       static double mp_isin(_cimg_math_parser& mp) {
24723         const unsigned int i_end = (unsigned int)mp.opcode[2];
24724         const double val = _mp_arg(3);
24725         for (unsigned int i = 4; i<i_end; ++i)
24726           if (val==_mp_arg(i)) return 1.;
24727         return 0.;
24728       }
24729 
24730       static double mp_isinf(_cimg_math_parser& mp) {
24731         return (double)cimg::type<double>::is_inf(_mp_arg(2));
24732       }
24733 
24734       static double mp_isint(_cimg_math_parser& mp) {
24735         return (double)((double)(longT)_mp_arg(2)==_mp_arg(2));
24736       }
24737 
24738       static double mp_isfile(_cimg_math_parser& mp) {
24739         const unsigned int siz = (unsigned int)mp.opcode[3];
24740         const double *ptrs = &_mp_arg(2) + (siz?1:0);
24741         if (!siz) { char str[2] = { 0 }; *str = *ptrs; return (double)cimg::is_file(str); }
24742         CImg<charT> ss(siz + 1);
24743         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24744         ss.back() = 0;
24745         return (double)cimg::is_file(ss);
24746       }
24747 
24748       static double mp_isnan(_cimg_math_parser& mp) {
24749         return (double)cimg::type<double>::is_nan(_mp_arg(2));
24750       }
24751 
24752       static double mp_isvarname(_cimg_math_parser& mp) {
24753         const unsigned int siz = (unsigned int)mp.opcode[3];
24754         const double *ptrs = &_mp_arg(2) + (siz?1:0);
24755         if (!siz) {
24756           const char c = (char)*ptrs;
24757           return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_';
24758         }
24759         if (*ptrs>='0' && *ptrs<='9') return 0;
24760         for (unsigned int k = 0; k<siz; ++k) if (!is_varchar((char)ptrs[k])) return 0;
24761         return 1;
24762       }
24763 
24764       static double mp_ixyzc(_cimg_math_parser& mp) {
24765         const unsigned int
24766           interpolation = (unsigned int)_mp_arg(6),
24767           boundary_conditions = (unsigned int)_mp_arg(7);
24768         const CImg<T> &img = mp.imgin;
24769         const double
24770           x = _mp_arg(2), y = _mp_arg(3),
24771           z = _mp_arg(4), c = _mp_arg(5);
24772         switch (interpolation) {
24773         case 2 : // Cubic interpolation
24774           switch (boundary_conditions) {
24775           case 3 : { // Mirror
24776             const float
24777               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24778               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24779               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24780             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24781                                             my<img.height()?my:h2 - my - 1,
24782                                             mz<img.depth()?mz:d2 - mz - 1,
24783                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24784           }
24785           case 2 : // Periodic
24786             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24787                                               (int)cimg::mod(c,(double)img._spectrum));
24788           case 1 : // Neumann
24789             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24790                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24791           default : // Dirichlet
24792             if (c<0 || c>=img._spectrum) return (T)0;
24793             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24794           }
24795         case 1 : // Linear interpolation
24796           switch (boundary_conditions) {
24797           case 3 : { // Mirror
24798             const float
24799               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24800               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24801               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24802             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24803                                              my<img.height()?my:h2 - my - 1,
24804                                              mz<img.depth()?mz:d2 - mz - 1,
24805                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24806           }
24807           case 2 : // Periodic
24808             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24809                                                (int)cimg::mod(c,(double)img._spectrum));
24810           case 1 : // Neumann
24811             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24812                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24813           default : // Dirichlet
24814             if (c<0 || c>=img._spectrum) return (T)0;
24815             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24816           }
24817         default : // Nearest neighbor interpolation
24818           switch (boundary_conditions) {
24819           case 3 : { // Mirror
24820             const int
24821               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24822               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24823               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24824             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24825                                my<img.height()?my:h2 - my - 1,
24826                                mz<img.depth()?mz:d2 - mz - 1,
24827                                mc<img.spectrum()?mc:s2 - mc - 1);
24828           }
24829           case 2 : // Periodic
24830             return (double)img((int)cimg::mod(x,(double)img._width),
24831                                (int)cimg::mod(y,(double)img._height),
24832                                (int)cimg::mod(z,(double)img._depth),
24833                                (int)cimg::mod(c,(double)img._spectrum));
24834           case 1 : // Neumann
24835             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24836           default : // Dirichlet
24837             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24838           }
24839         }
24840       }
24841 
24842       static double mp_joff(_cimg_math_parser& mp) {
24843         const unsigned int
24844           boundary_conditions = (unsigned int)_mp_arg(3);
24845         const int
24846           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24847           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24848         const CImg<T> &img = mp.imgin;
24849         const longT
24850           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
24851           whds = (longT)img.size();
24852         if (off>=0 && off<whds) return (double)img[off];
24853         if (img._data) switch (boundary_conditions) {
24854           case 3 : { // Mirror
24855             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24856             return (double)img[moff<whds?moff:whds2 - moff - 1];
24857           }
24858           case 2 : // Periodic
24859             return (double)img[cimg::mod(off,whds)];
24860           case 1 : // Neumann
24861             return (double)img[off<0?0:whds - 1];
24862           default : // Dirichlet
24863             return 0;
24864           }
24865         return 0;
24866       }
24867 
24868       static double mp_jxyzc(_cimg_math_parser& mp) {
24869         const unsigned int
24870           interpolation = (unsigned int)_mp_arg(6),
24871           boundary_conditions = (unsigned int)_mp_arg(7);
24872         const CImg<T> &img = mp.imgin;
24873         const double
24874           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24875           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
24876           x = ox + _mp_arg(2), y = oy + _mp_arg(3),
24877           z = oz + _mp_arg(4), c = oc + _mp_arg(5);
24878         switch (interpolation) {
24879         case 2 : // Cubic interpolation
24880           switch (boundary_conditions) {
24881           case 3 : { // Mirror
24882             const float
24883               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24884               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24885               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24886             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24887                                             my<img.height()?my:h2 - my - 1,
24888                                             mz<img.depth()?mz:d2 - mz - 1,
24889                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24890           }
24891           case 2 : // Periodic
24892             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24893                                               (int)cimg::mod(c,(double)img._spectrum));
24894           case 1 : // Neumann
24895             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24896                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24897           default : // Dirichlet
24898             if (c<0 || c>=img._spectrum) return (T)0;
24899             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24900           }
24901         case 1 : // Linear interpolation
24902           switch (boundary_conditions) {
24903           case 3 : { // Mirror
24904             const float
24905               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24906               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24907               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24908             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24909                                              my<img.height()?my:h2 - my - 1,
24910                                              mz<img.depth()?mz:d2 - mz - 1,
24911                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24912           }
24913           case 2 : // Periodic
24914             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24915                                                (int)cimg::mod(c,(double)img._spectrum));
24916           case 1 : // Neumann
24917             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24918                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24919           default : // Dirichlet
24920             if (c<0 || c>=img._spectrum) return (T)0;
24921             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24922           }
24923         default : // Nearest neighbor interpolation
24924           switch (boundary_conditions) {
24925           case 3 : { // Mirror
24926             const int
24927               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24928               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24929               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24930             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24931                                my<img.height()?my:h2 - my - 1,
24932                                mz<img.depth()?mz:d2 - mz - 1,
24933                                mc<img.spectrum()?mc:s2 - mc - 1);
24934           }
24935           case 2 : // Periodic
24936             return (double)img((int)cimg::mod(x,(double)img._width),
24937                                (int)cimg::mod(y,(double)img._height),
24938                                (int)cimg::mod(z,(double)img._depth),
24939                                (int)cimg::mod(c,(double)img._spectrum));
24940           case 1 : // Neumann
24941             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24942           default : // Dirichlet
24943             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24944           }
24945         }
24946       }
24947 
24948       static double mp_kth(_cimg_math_parser& mp) {
24949         const unsigned int i_end = (unsigned int)mp.opcode[2];
24950         CImg<doubleT> vals(i_end - 4);
24951         double *p = vals.data();
24952         for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
24953         longT ind = (longT)cimg::round(_mp_arg(3));
24954         if (ind<0) ind+=vals.width() + 1;
24955         ind = cimg::cut(ind,(longT)1,(longT)vals.width());
24956         return vals.kth_smallest((ulongT)(ind - 1));
24957       }
24958 
24959       static double mp_lerp(_cimg_math_parser& mp) {
24960         const double t = _mp_arg(4);
24961         return _mp_arg(2)*(1-t) + _mp_arg(3)*t;
24962       }
24963 
24964       static double mp_linear_add(_cimg_math_parser& mp) {
24965         return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
24966       }
24967 
24968       static double mp_linear_sub_left(_cimg_math_parser& mp) {
24969         return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
24970       }
24971 
24972       static double mp_linear_sub_right(_cimg_math_parser& mp) {
24973         return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
24974       }
24975 
24976       static double mp_list_depth(_cimg_math_parser& mp) {
24977         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24978         return (double)mp.listin[ind]._depth;
24979       }
24980 
24981       static double mp_list_find(_cimg_math_parser& mp) {
24982         const unsigned int
24983           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24984         const CImg<T> &img = mp.listin[indi];
24985         const int _step = (int)_mp_arg(5), step = _step?_step:-1;
24986         const ulongT siz = (ulongT)img.size();
24987         longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1);
24988         if (ind<0 || ind>=(longT)siz) return -1.;
24989         const T
24990           *const ptrb = img.data(),
24991           *const ptre = img.end(),
24992           *ptr = ptrb + ind;
24993         const double val = _mp_arg(3);
24994 
24995         // Forward search
24996         if (step>0) {
24997           while (ptr<ptre && (double)*ptr!=val) ptr+=step;
24998           return ptr>=ptre?-1.:(double)(ptr - ptrb);
24999         }
25000 
25001         // Backward search.
25002         while (ptr>=ptrb && (double)*ptr!=val) ptr+=step;
25003         return ptr<ptrb?-1.:(double)(ptr - ptrb);
25004       }
25005 
25006       static double mp_list_find_seq(_cimg_math_parser& mp) {
25007         const unsigned int
25008           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25009         const CImg<T> &img = mp.listin[indi];
25010         const int _step = (bool)_mp_arg(6), step = _step?_step:-1;
25011         const ulongT
25012           siz1 = (ulongT)img.size(),
25013           siz2 = (ulongT)mp.opcode[4];
25014         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1);
25015         if (ind<0 || ind>=(longT)siz1) return -1.;
25016         const T
25017           *const ptr1b = img.data(),
25018           *const ptr1e = ptr1b + siz1,
25019           *ptr1 = ptr1b + ind,
25020           *p1 = 0;
25021         const double
25022           *const ptr2b = &_mp_arg(3) + 1,
25023           *const ptr2e = ptr2b + siz2,
25024           *p2 = 0;
25025 
25026         // Forward search.
25027         if (step>0) {
25028           do {
25029             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
25030             if (ptr1>=ptr1e) return -1.;
25031             p1 = ptr1 + 1;
25032             p2 = ptr2b + 1;
25033             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
25034           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
25035           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
25036         }
25037 
25038         // Backward search.
25039         do {
25040           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
25041           if (ptr1<ptr1b) return -1.;
25042           p1 = ptr1 + 1;
25043           p2 = ptr2b + 1;
25044           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
25045         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
25046         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
25047       }
25048 
25049       static double mp_list_height(_cimg_math_parser& mp) {
25050         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25051         return (double)mp.listin[ind]._height;
25052       }
25053 
25054       static double mp_list_ioff(_cimg_math_parser& mp) {
25055         const unsigned int
25056           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25057           boundary_conditions = (unsigned int)_mp_arg(4);
25058         const CImg<T> &img = mp.listin[ind];
25059         const longT
25060           off = (longT)_mp_arg(3),
25061           whds = (longT)img.size();
25062         if (off>=0 && off<whds) return (double)img[off];
25063         if (img._data) switch (boundary_conditions) {
25064           case 3 : { // Mirror
25065             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
25066             return (double)img[moff<whds?moff:whds2 - moff - 1];
25067           }
25068           case 2 : // Periodic
25069             return (double)img[cimg::mod(off,whds)];
25070           case 1 : // Neumann
25071             return (double)img[off<0?0:whds - 1];
25072           default : // Dirichlet
25073             return 0;
25074           }
25075         return 0;
25076       }
25077 
25078       static double mp_list_is_shared(_cimg_math_parser& mp) {
25079         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25080         return (double)mp.listin[ind]._is_shared;
25081       }
25082 
25083       static double mp_list_ixyzc(_cimg_math_parser& mp) {
25084         const unsigned int
25085           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25086           interpolation = (unsigned int)_mp_arg(7),
25087           boundary_conditions = (unsigned int)_mp_arg(8);
25088         const CImg<T> &img = mp.listin[ind];
25089         const double
25090           x = _mp_arg(3), y = _mp_arg(4),
25091           z = _mp_arg(5), c = _mp_arg(6);
25092         switch (interpolation) {
25093         case 2 : // Cubic interpolation
25094           switch (boundary_conditions) {
25095           case 3 : { // Mirror
25096             const float
25097               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
25098               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
25099               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
25100             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
25101                                             my<img.height()?my:h2 - my - 1,
25102                                             mz<img.depth()?mz:d2 - mz - 1,
25103                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
25104           }
25105           case 2 : // Periodic
25106             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
25107                                               (int)cimg::mod(c,(double)img._spectrum));
25108           case 1 : // Neumann
25109             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
25110                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
25111           default : // Dirichlet
25112             if (c<0 || c>=img._spectrum) return (T)0;
25113             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
25114           }
25115         case 1 : // Linear interpolation
25116           switch (boundary_conditions) {
25117           case 3 : { // Mirror
25118             const float
25119               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
25120               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
25121               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
25122             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
25123                                              my<img.height()?my:h2 - my - 1,
25124                                              mz<img.depth()?mz:d2 - mz - 1,
25125                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
25126           }
25127           case 2 : // Periodic
25128             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
25129                                                (int)cimg::mod(c,(double)img._spectrum));
25130           case 1 : // Neumann
25131             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
25132                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
25133           default : // Dirichlet
25134             if (c<0 || c>=img._spectrum) return (T)0;
25135             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
25136           }
25137         default : // Nearest neighbor interpolation
25138           switch (boundary_conditions) {
25139           case 3 : { // Mirror
25140             const int
25141               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
25142               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
25143               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
25144             return (double)img(mx<img.width()?mx:w2 - mx - 1,
25145                                my<img.height()?my:h2 - my - 1,
25146                                mz<img.depth()?mz:d2 - mz - 1,
25147                                mc<img.spectrum()?mc:s2 - mc - 1);
25148           }
25149           case 2 : // Periodic
25150             return (double)img((int)cimg::mod(x,(double)img._width),
25151                                (int)cimg::mod(y,(double)img._height),
25152                                (int)cimg::mod(z,(double)img._depth),
25153                                (int)cimg::mod(c,(double)img._spectrum));
25154           case 1 : // Neumann
25155             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
25156           default : // Dirichlet
25157             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
25158           }
25159         }
25160       }
25161 
25162       static double mp_list_joff(_cimg_math_parser& mp) {
25163         const unsigned int
25164           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25165           boundary_conditions = (unsigned int)_mp_arg(4);
25166         const int
25167           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25168           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25169         const CImg<T> &img = mp.listin[ind];
25170         const longT
25171           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25172           whds = (longT)img.size();
25173         if (off>=0 && off<whds) return (double)img[off];
25174         if (img._data) switch (boundary_conditions) {
25175           case 3 : { // Mirror
25176             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
25177             return (double)img[moff<whds?moff:whds2 - moff - 1];
25178           }
25179           case 2 : // Periodic
25180             return (double)img[cimg::mod(off,whds)];
25181           case 1 : // Neumann
25182             return (double)img[off<0?0:whds - 1];
25183           default : // Dirichlet
25184             return 0;
25185           }
25186         return 0;
25187       }
25188 
25189       static double mp_list_jxyzc(_cimg_math_parser& mp) {
25190         const unsigned int
25191           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25192           interpolation = (unsigned int)_mp_arg(7),
25193           boundary_conditions = (unsigned int)_mp_arg(8);
25194         const CImg<T> &img = mp.listin[ind];
25195         const double
25196           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
25197           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
25198           x = ox + _mp_arg(3), y = oy + _mp_arg(4),
25199           z = oz + _mp_arg(5), c = oc + _mp_arg(6);
25200         switch (interpolation) {
25201         case 2 : // Cubic interpolation
25202           switch (boundary_conditions) {
25203           case 3 : { // Mirror
25204             const float
25205               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
25206               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
25207               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
25208             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
25209                                             my<img.height()?my:h2 - my - 1,
25210                                             mz<img.depth()?mz:d2 - mz - 1,
25211                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
25212           }
25213           case 2 : // Periodic
25214             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
25215                                               (int)cimg::mod(c,(double)img._spectrum));
25216           case 1 : // Neumann
25217             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
25218                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
25219           default : // Dirichlet
25220             if (c<0 || c>=img._spectrum) return (T)0;
25221             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
25222           }
25223         case 1 : // Linear interpolation
25224           switch (boundary_conditions) {
25225           case 3 : { // Mirror
25226             const float
25227               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
25228               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
25229               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
25230             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
25231                                              my<img.height()?my:h2 - my - 1,
25232                                              mz<img.depth()?mz:d2 - mz - 1,
25233                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
25234           }
25235           case 2 : // Periodic
25236             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
25237                                                (int)cimg::mod(c,(double)img._spectrum));
25238           case 1 : // Neumann
25239             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
25240                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
25241           default : // Dirichlet
25242             if (c<0 || c>=img._spectrum) return (T)0;
25243             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
25244           }
25245         default : // Nearest neighbor interpolation
25246           switch (boundary_conditions) {
25247           case 3 : { // Mirror
25248             const int
25249               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
25250               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
25251               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
25252             return (double)img(mx<img.width()?mx:w2 - mx - 1,
25253                                my<img.height()?my:h2 - my - 1,
25254                                mz<img.depth()?mz:d2 - mz - 1,
25255                                mc<img.spectrum()?mc:s2 - mc - 1);
25256           }
25257           case 2 : // Periodic
25258             return (double)img((int)cimg::mod(x,(double)img._width),
25259                                (int)cimg::mod(y,(double)img._height),
25260                                (int)cimg::mod(z,(double)img._depth),
25261                                (int)cimg::mod(c,(double)img._spectrum));
25262           case 1 : // Neumann
25263             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
25264           default : // Dirichlet
25265             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
25266           }
25267         }
25268       }
25269 
25270       static double mp_list_l(_cimg_math_parser& mp) {
25271         return (double)mp.listout.width();
25272       }
25273 
25274       static double mp_list_median(_cimg_math_parser& mp) {
25275         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25276         if (!mp.list_median) mp.list_median.assign(mp.listin._width);
25277         if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
25278         return *mp.list_median[ind];
25279       }
25280 
25281       static double mp_list_norm(_cimg_math_parser& mp) {
25282         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25283         if (!mp.list_norm) mp.list_norm.assign(mp.listin._width);
25284         if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]);
25285         return *mp.list_norm[ind];
25286       }
25287 
25288       static double mp_list_set_ioff(_cimg_math_parser& mp) {
25289         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25290         CImg<T> &img = mp.listout[ind];
25291         const longT
25292           off = (longT)_mp_arg(3),
25293           whds = (longT)img.size();
25294         const double val = _mp_arg(1);
25295         if (off>=0 && off<whds) img[off] = (T)val;
25296         return val;
25297       }
25298 
25299       static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
25300         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25301         CImg<T> &img = mp.listout[ind];
25302         const int
25303           x = (int)_mp_arg(3), y = (int)_mp_arg(4),
25304           z = (int)_mp_arg(5), c = (int)_mp_arg(6);
25305         const double val = _mp_arg(1);
25306         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25307             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25308           img(x,y,z,c) = (T)val;
25309         return val;
25310       }
25311 
25312       static double mp_list_set_joff(_cimg_math_parser& mp) {
25313         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25314         CImg<T> &img = mp.listout[ind];
25315         const int
25316           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25317           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25318         const longT
25319           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25320           whds = (longT)img.size();
25321         const double val = _mp_arg(1);
25322         if (off>=0 && off<whds) img[off] = (T)val;
25323         return val;
25324       }
25325 
25326       static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
25327         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25328         CImg<T> &img = mp.listout[ind];
25329         const double
25330           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
25331           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
25332         const int
25333           x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
25334           z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
25335         const double val = _mp_arg(1);
25336         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25337             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25338           img(x,y,z,c) = (T)val;
25339         return val;
25340       }
25341 
25342       static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
25343         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25344         CImg<T> &img = mp.listout[ind];
25345         const longT
25346           off = (longT)_mp_arg(3),
25347           whd = (longT)img.width()*img.height()*img.depth();
25348         const T val = (T)_mp_arg(1);
25349         if (off>=0 && off<whd) {
25350           T *ptrd = &img[off];
25351           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25352         }
25353         return _mp_arg(1);
25354       }
25355 
25356       static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
25357         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25358         CImg<T> &img = mp.listout[ind];
25359         const longT
25360           off = (longT)_mp_arg(3),
25361           whd = (longT)img.width()*img.height()*img.depth();
25362         const double *ptrs = &_mp_arg(1) + 1;
25363         if (off>=0 && off<whd) {
25364           const unsigned int vsiz = (unsigned int)mp.opcode[4];
25365           T *ptrd = &img[off];
25366           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25367         }
25368         return cimg::type<double>::nan();
25369       }
25370 
25371       static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
25372         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25373         CImg<T> &img = mp.listout[ind];
25374         const int
25375           x = (int)_mp_arg(3),
25376           y = (int)_mp_arg(4),
25377           z = (int)_mp_arg(5);
25378         const T val = (T)_mp_arg(1);
25379         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25380           T *ptrd = &img(x,y,z);
25381           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25382           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25383         }
25384         return _mp_arg(1);
25385       }
25386 
25387       static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
25388         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25389         CImg<T> &img = mp.listout[ind];
25390         const int
25391           x = (int)_mp_arg(3),
25392           y = (int)_mp_arg(4),
25393           z = (int)_mp_arg(5);
25394         const double *ptrs = &_mp_arg(1) + 1;
25395         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25396           const unsigned int vsiz = (unsigned int)mp.opcode[6];
25397           T *ptrd = &img(x,y,z);
25398           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25399           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25400         }
25401         return cimg::type<double>::nan();
25402       }
25403 
25404       static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
25405         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25406         CImg<T> &img = mp.listout[ind];
25407         const int
25408           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25409           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25410         const longT
25411           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25412           whd = (longT)img.width()*img.height()*img.depth();
25413         const T val = (T)_mp_arg(1);
25414         if (off>=0 && off<whd) {
25415           T *ptrd = &img[off];
25416           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25417         }
25418         return _mp_arg(1);
25419       }
25420 
25421       static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
25422         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25423         CImg<T> &img = mp.listout[ind];
25424         const int
25425           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25426           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25427         const longT
25428           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
25429           whd = (longT)img.width()*img.height()*img.depth();
25430         const double *ptrs = &_mp_arg(1) + 1;
25431         if (off>=0 && off<whd) {
25432           const unsigned int vsiz = (unsigned int)mp.opcode[4];
25433           T *ptrd = &img[off];
25434           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25435         }
25436         return cimg::type<double>::nan();
25437       }
25438 
25439       static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
25440         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25441         CImg<T> &img = mp.listout[ind];
25442         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25443         const int
25444           x = (int)(ox + _mp_arg(3)),
25445           y = (int)(oy + _mp_arg(4)),
25446           z = (int)(oz + _mp_arg(5));
25447         const T val = (T)_mp_arg(1);
25448         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25449           T *ptrd = &img(x,y,z);
25450           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25451           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25452         }
25453         return _mp_arg(1);
25454       }
25455 
25456       static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
25457         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25458         CImg<T> &img = mp.listout[ind];
25459         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25460         const int
25461           x = (int)(ox + _mp_arg(3)),
25462           y = (int)(oy + _mp_arg(4)),
25463           z = (int)(oz + _mp_arg(5));
25464         const double *ptrs = &_mp_arg(1) + 1;
25465         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25466           const unsigned int vsiz = (unsigned int)mp.opcode[6];
25467           T *ptrd = &img(x,y,z);
25468           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25469           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25470         }
25471         return cimg::type<double>::nan();
25472       }
25473 
25474       static double mp_list_spectrum(_cimg_math_parser& mp) {
25475         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25476         return (double)mp.listin[ind]._spectrum;
25477       }
25478 
25479       static double mp_list_stats(_cimg_math_parser& mp) {
25480         const unsigned int
25481           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25482           k = (unsigned int)mp.opcode[3];
25483         if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
25484         if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
25485         return mp.list_stats(ind,k);
25486       }
25487 
25488       static double mp_list_wh(_cimg_math_parser& mp) {
25489         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25490         return (double)mp.listin[ind]._width*mp.listin[ind]._height;
25491       }
25492 
25493       static double mp_list_whd(_cimg_math_parser& mp) {
25494         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25495         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
25496       }
25497 
25498       static double mp_list_whds(_cimg_math_parser& mp) {
25499         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25500         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
25501       }
25502 
25503       static double mp_list_width(_cimg_math_parser& mp) {
25504         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
25505         return (double)mp.listin[ind]._width;
25506       }
25507 
25508       static double mp_list_Ioff(_cimg_math_parser& mp) {
25509         double *ptrd = &_mp_arg(1) + 1;
25510         const unsigned int
25511           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25512           boundary_conditions = (unsigned int)_mp_arg(4),
25513           vsiz = (unsigned int)mp.opcode[5];
25514         const CImg<T> &img = mp.listin[ind];
25515         const longT
25516           off = (longT)_mp_arg(3),
25517           whd = (longT)img.width()*img.height()*img.depth();
25518         const T *ptrs;
25519         if (off>=0 && off<whd) {
25520           ptrs = &img[off];
25521           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25522           return cimg::type<double>::nan();
25523         }
25524         if (img._data) switch (boundary_conditions) {
25525           case 3 : { // Mirror
25526             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
25527             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
25528             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25529             return cimg::type<double>::nan();
25530           }
25531           case 2 : // Periodic
25532             ptrs = &img[cimg::mod(off,whd)];
25533             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25534             return cimg::type<double>::nan();
25535           case 1 : // Neumann
25536             ptrs = off<0?&img[0]:&img[whd - 1];
25537             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25538             return cimg::type<double>::nan();
25539           default : // Dirichlet
25540             std::memset(ptrd,0,vsiz*sizeof(double));
25541             return cimg::type<double>::nan();
25542           }
25543         std::memset(ptrd,0,vsiz*sizeof(double));
25544         return cimg::type<double>::nan();
25545       }
25546 
25547       static double mp_list_Ixyz(_cimg_math_parser& mp) {
25548         double *ptrd = &_mp_arg(1) + 1;
25549         const unsigned int
25550           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25551           interpolation = (unsigned int)_mp_arg(6),
25552           boundary_conditions = (unsigned int)_mp_arg(7),
25553           vsiz = (unsigned int)mp.opcode[8];
25554         const CImg<T> &img = mp.listin[ind];
25555         const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
25556         const ulongT whd = (ulongT)img._width*img._height*img._depth;
25557         const T *ptrs;
25558         switch (interpolation) {
25559         case 2 : // Cubic interpolation
25560           switch (boundary_conditions) {
25561           case 3 : { // Mirror
25562             const float
25563               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25564               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25565               cx = mx<img.width()?mx:w2 - mx - 1,
25566               cy = my<img.height()?my:h2 - my - 1,
25567               cz = mz<img.depth()?mz:d2 - mz - 1;
25568             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
25569           } break;
25570           case 2 : // Periodic
25571             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
25572             break;
25573           case 1 : // Neumann
25574             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
25575             break;
25576           default : // Dirichlet
25577             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25578           } break;
25579         case 1 : // Linear interpolation
25580           switch (boundary_conditions) {
25581           case 3 : { // Mirror
25582             const float
25583               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25584               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25585               cx = mx<img.width()?mx:w2 - mx - 1,
25586               cy = my<img.height()?my:h2 - my - 1,
25587               cz = mz<img.depth()?mz:d2 - mz - 1;
25588             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
25589           } break;
25590           case 2 : // Periodic
25591             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
25592             break;
25593           case 1 : // Neumann
25594             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
25595             break;
25596           default : // Dirichlet
25597             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25598           } break;
25599         default : // Nearest neighbor interpolation
25600           switch (boundary_conditions) {
25601           case 3 : { // Mirror
25602             const int
25603               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
25604               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
25605               cx = mx<img.width()?mx:w2 - mx - 1,
25606               cy = my<img.height()?my:h2 - my - 1,
25607               cz = mz<img.depth()?mz:d2 - mz - 1;
25608             ptrs = &img(cx,cy,cz);
25609             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25610           } break;
25611           case 2 : { // Periodic
25612             const int
25613               cx = (int)cimg::mod(x,(double)img._width),
25614               cy = (int)cimg::mod(y,(double)img._height),
25615               cz = (int)cimg::mod(z,(double)img._depth);
25616             ptrs = &img(cx,cy,cz);
25617             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25618           } break;
25619           case 1 : { // Neumann
25620             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25621             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25622           } break;
25623           default : // Dirichlet
25624             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25625               ptrs = &img((int)x,(int)y,(int)z);
25626               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25627             } else std::memset(ptrd,0,vsiz*sizeof(double));
25628           }
25629         }
25630         return cimg::type<double>::nan();
25631       }
25632 
25633       static double mp_list_Joff(_cimg_math_parser& mp) {
25634         double *ptrd = &_mp_arg(1) + 1;
25635         const unsigned int
25636           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25637           boundary_conditions = (unsigned int)_mp_arg(4),
25638           vsiz = (unsigned int)mp.opcode[5];
25639         const int
25640           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
25641         const CImg<T> &img = mp.listin[ind];
25642         const longT
25643           off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
25644           whd = (longT)img.width()*img.height()*img.depth();
25645         const T *ptrs;
25646         if (off>=0 && off<whd) {
25647           ptrs = &img[off];
25648           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25649           return cimg::type<double>::nan();
25650         }
25651         if (img._data) switch (boundary_conditions) {
25652           case 3 : { // Mirror
25653             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
25654             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
25655             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25656             return cimg::type<double>::nan();
25657           }
25658           case 2 : // Periodic
25659             ptrs = &img[cimg::mod(off,whd)];
25660             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25661             return cimg::type<double>::nan();
25662           case 1 : // Neumann
25663             ptrs = off<0?&img[0]:&img[whd - 1];
25664             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25665             return cimg::type<double>::nan();
25666           default : // Dirichlet
25667             std::memset(ptrd,0,vsiz*sizeof(double));
25668             return cimg::type<double>::nan();
25669           }
25670         std::memset(ptrd,0,vsiz*sizeof(double));
25671         return cimg::type<double>::nan();
25672       }
25673 
25674       static double mp_list_Jxyz(_cimg_math_parser& mp) {
25675         double *ptrd = &_mp_arg(1) + 1;
25676         const unsigned int
25677           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25678           interpolation = (unsigned int)_mp_arg(6),
25679           boundary_conditions = (unsigned int)_mp_arg(7),
25680           vsiz = (unsigned int)mp.opcode[8];
25681         const CImg<T> &img = mp.listin[ind];
25682         const double
25683           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
25684           x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
25685         const ulongT whd = (ulongT)img._width*img._height*img._depth;
25686         const T *ptrs;
25687         switch (interpolation) {
25688         case 2 : // Cubic interpolation
25689           switch (boundary_conditions) {
25690           case 3 : { // Mirror
25691             const float
25692               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25693               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25694               cx = mx<img.width()?mx:w2 - mx - 1,
25695               cy = my<img.height()?my:h2 - my - 1,
25696               cz = mz<img.depth()?mz:d2 - mz - 1;
25697             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
25698           } break;
25699           case 2 : // Periodic
25700             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
25701             break;
25702           case 1 : // Neumann
25703             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
25704             break;
25705           default : // Dirichlet
25706             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25707           } break;
25708         case 1 : // Linear interpolation
25709           switch (boundary_conditions) {
25710           case 3 : { // Mirror
25711             const float
25712               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25713               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25714               cx = mx<img.width()?mx:w2 - mx - 1,
25715               cy = my<img.height()?my:h2 - my - 1,
25716               cz = mz<img.depth()?mz:d2 - mz - 1;
25717             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
25718           } break;
25719           case 2 : // Periodic
25720             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
25721             break;
25722           case 1 : // Neumann
25723             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
25724             break;
25725           default : // Dirichlet
25726             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25727           } break;
25728         case 0 : // Nearest neighbor interpolation
25729           switch (boundary_conditions) {
25730           case 3 : { // Mirror
25731             const int
25732               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
25733               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
25734               cx = mx<img.width()?mx:w2 - mx - 1,
25735               cy = my<img.height()?my:h2 - my - 1,
25736               cz = mz<img.depth()?mz:d2 - mz - 1;
25737             ptrs = &img(cx,cy,cz);
25738             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25739           } break;
25740           case 2 : { // Periodic
25741             const int
25742               cx = (int)cimg::mod(x,(double)img._width),
25743               cy = (int)cimg::mod(y,(double)img._height),
25744               cz = (int)cimg::mod(z,(double)img._depth);
25745             ptrs = &img(cx,cy,cz);
25746             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25747           } break;
25748           case 1 : { // Neumann
25749             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25750             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25751           } break;
25752           default : // Dirichlet
25753             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25754               ptrs = &img((int)x,(int)y,(int)z);
25755               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25756             } else std::memset(ptrd,0,vsiz*sizeof(double));
25757           }
25758         }
25759         return cimg::type<double>::nan();
25760       }
25761 
25762       static double mp_log(_cimg_math_parser& mp) {
25763         return std::log(_mp_arg(2));
25764       }
25765 
25766       static double mp_log10(_cimg_math_parser& mp) {
25767         return std::log10(_mp_arg(2));
25768       }
25769 
25770       static double mp_log2(_cimg_math_parser& mp) {
25771         return cimg::log2(_mp_arg(2));
25772       }
25773 
25774       static double mp_logical_and(_cimg_math_parser& mp) {
25775         const bool val_left = (bool)_mp_arg(2);
25776         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25777         if (!val_left) { mp.p_code = p_end - 1; return 0; }
25778         const ulongT mem_right = mp.opcode[3];
25779         for ( ; mp.p_code<p_end; ++mp.p_code) {
25780           mp.opcode._data = mp.p_code->_data;
25781           const ulongT target = mp.opcode[1];
25782           mp.mem[target] = _cimg_mp_defunc(mp);
25783         }
25784         --mp.p_code;
25785         return (double)(bool)mp.mem[mem_right];
25786       }
25787 
25788       static double mp_logical_not(_cimg_math_parser& mp) {
25789         return (double)!_mp_arg(2);
25790       }
25791 
25792       static double mp_logical_or(_cimg_math_parser& mp) {
25793         const bool val_left = (bool)_mp_arg(2);
25794         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25795         if (val_left) { mp.p_code = p_end - 1; return 1; }
25796         const ulongT mem_right = mp.opcode[3];
25797         for ( ; mp.p_code<p_end; ++mp.p_code) {
25798           mp.opcode._data = mp.p_code->_data;
25799           const ulongT target = mp.opcode[1];
25800           mp.mem[target] = _cimg_mp_defunc(mp);
25801         }
25802         --mp.p_code;
25803         return (double)(bool)mp.mem[mem_right];
25804       }
25805 
25806       static double mp_lowercase(_cimg_math_parser& mp) {
25807         return cimg::lowercase(_mp_arg(2));
25808       }
25809 
25810       static double mp_lt(_cimg_math_parser& mp) {
25811         return (double)(_mp_arg(2)<_mp_arg(3));
25812       }
25813 
25814       static double mp_lte(_cimg_math_parser& mp) {
25815         return (double)(_mp_arg(2)<=_mp_arg(3));
25816       }
25817 
25818       static double mp_matrix_eig(_cimg_math_parser& mp) {
25819         double *ptrd = &_mp_arg(1) + 1;
25820         const double *ptr1 = &_mp_arg(2) + 1;
25821         const unsigned int k = (unsigned int)mp.opcode[3];
25822         CImg<doubleT> val, vec;
25823         CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
25824         CImg<doubleT>(ptrd,1,k,1,1,true) = val;
25825         CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
25826         return cimg::type<double>::nan();
25827       }
25828 
25829       static double mp_matrix_invert(_cimg_math_parser& mp) {
25830         double *const ptrd = &_mp_arg(1) + 1;
25831         const double *const ptr1 = &_mp_arg(2) + 1;
25832         const unsigned int k = (unsigned int)mp.opcode[3];
25833         const bool use_LU = (bool)_mp_arg(4);
25834         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert(use_LU);
25835         return cimg::type<double>::nan();
25836       }
25837 
25838       static double mp_matrix_mul(_cimg_math_parser& mp) {
25839         double *ptrd = &_mp_arg(1) + 1;
25840         const double
25841           *ptr1 = &_mp_arg(2) + 1,
25842           *ptr2 = &_mp_arg(3) + 1;
25843         const unsigned int
25844           k = (unsigned int)mp.opcode[4],
25845           l = (unsigned int)mp.opcode[5],
25846           m = (unsigned int)mp.opcode[6];
25847         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
25848         return cimg::type<double>::nan();
25849       }
25850 
25851       static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) {
25852         double *ptrd = &_mp_arg(1) + 1;
25853         const double *ptr1 = &_mp_arg(2) + 1;
25854         const unsigned int
25855           k = (unsigned int)mp.opcode[3],
25856           l = (unsigned int)mp.opcode[4];
25857         const bool use_LU = (bool)_mp_arg(5);
25858         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU);
25859         return cimg::type<double>::nan();
25860       }
25861 
25862       static double mp_matrix_svd(_cimg_math_parser& mp) {
25863         double *ptrd = &_mp_arg(1) + 1;
25864         const double *ptr1 = &_mp_arg(2) + 1;
25865         const unsigned int
25866           k = (unsigned int)mp.opcode[3],
25867           l = (unsigned int)mp.opcode[4];
25868         CImg<doubleT> U, S, V;
25869         CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
25870         CImg<doubleT>(ptrd,k,l,1,1,true) = U;
25871         CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
25872         CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
25873         return cimg::type<double>::nan();
25874       }
25875 
25876       static double mp_max(_cimg_math_parser& mp) {
25877         const unsigned int i_end = (unsigned int)mp.opcode[2];
25878         double val = _mp_arg(3);
25879         for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
25880         return val;
25881       }
25882 
25883       static double mp_maxabs(_cimg_math_parser& mp) {
25884         const unsigned int i_end = (unsigned int)mp.opcode[2];
25885         double val = _mp_arg(3), absval = cimg::abs(val);
25886         for (unsigned int i = 4; i<i_end; ++i) {
25887           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
25888           if (_absval>absval) { val = _val; absval = _absval; }
25889         }
25890         return val;
25891       }
25892 
25893       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
25894                                         const longT siz, const long inc) {
25895         const longT
25896           off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
25897           eoff = off + (siz - 1)*inc;
25898         if (off<0 || eoff>=mp.mem.width())
25899           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25900                                       "Out-of-bounds variable pointer "
25901                                       "(length: %ld, increment: %ld, offset start: %ld, "
25902                                       "offset end: %ld, offset max: %u).",
25903                                       mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
25904         return &mp.mem[off];
25905       }
25906 
25907       static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
25908                                       const longT siz, const long inc, const bool is_out) {
25909         const unsigned ind = (unsigned int)p_ref[1];
25910         const CImg<T> &img = is_out?
25911           (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]):
25912           (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]);
25913         const bool is_relative = (bool)p_ref[2];
25914         int ox, oy, oz, oc;
25915         longT off = 0;
25916         if (is_relative) {
25917           ox = (int)mp.mem[_cimg_mp_slot_x];
25918           oy = (int)mp.mem[_cimg_mp_slot_y];
25919           oz = (int)mp.mem[_cimg_mp_slot_z];
25920           oc = (int)mp.mem[_cimg_mp_slot_c];
25921           off = img.offset(ox,oy,oz,oc);
25922         }
25923         if ((*p_ref)%2) {
25924           const int
25925             x = (int)mp.mem[p_ref[3]],
25926             y = (int)mp.mem[p_ref[4]],
25927             z = (int)mp.mem[p_ref[5]],
25928             c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
25929           off+=img.offset(x,y,z,c);
25930         } else off+=(longT)mp.mem[p_ref[3]];
25931         const longT eoff = off + (siz - 1)*inc;
25932         if (off<0 || eoff>=(longT)img.size())
25933           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25934                                       "Out-of-bounds image pointer "
25935                                       "(length: %ld, increment: %ld, offset start: %ld, "
25936                                       "offset end: %ld, offset max: %lu).",
25937                                       mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
25938         return (float*)&img[off];
25939       }
25940 
25941       static double mp_memcopy(_cimg_math_parser& mp) {
25942         longT siz = (longT)_mp_arg(4);
25943         const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
25944         const float
25945           _opacity = (float)_mp_arg(7),
25946           opacity = (float)cimg::abs(_opacity),
25947           omopacity = 1 - std::max(_opacity,0.f);
25948         if (siz>0) {
25949           const bool
25950             is_doubled = mp.opcode[8]<=1,
25951             is_doubles = mp.opcode[15]<=1;
25952           if (is_doubled && is_doubles) { // (double*) <- (double*)
25953             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25954             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25955             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25956               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
25957               else std::memmove(ptrd,ptrs,siz*sizeof(double));
25958             } else {
25959               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25960                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25961                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25962               } else { // Overlapping buffers
25963                 CImg<doubleT> buf((unsigned int)siz);
25964                 cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
25965                 ptrs = buf;
25966                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25967                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25968               }
25969             }
25970           } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
25971             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25972             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25973             if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25974             else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25975           } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
25976             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25977             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25978             if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25979             else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
25980           } else { // (float*) <- (float*)
25981             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25982             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25983             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25984               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
25985               else std::memmove(ptrd,ptrs,siz*sizeof(float));
25986             } else {
25987               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25988                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25989                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25990               } else { // Overlapping buffers
25991                 CImg<floatT> buf((unsigned int)siz);
25992                 cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
25993                 ptrs = buf;
25994                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25995                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25996               }
25997             }
25998           }
25999         }
26000         return _mp_arg(1);
26001       }
26002 
26003       static double mp_min(_cimg_math_parser& mp) {
26004         const unsigned int i_end = (unsigned int)mp.opcode[2];
26005         double val = _mp_arg(3);
26006         for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
26007         return val;
26008       }
26009 
26010       static double mp_minabs(_cimg_math_parser& mp) {
26011         const unsigned int i_end = (unsigned int)mp.opcode[2];
26012         double val = _mp_arg(3), absval = cimg::abs(val);
26013         for (unsigned int i = 4; i<i_end; ++i) {
26014           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
26015           if (_absval<absval) { val = _val; absval = _absval; }
26016         }
26017         return val;
26018       }
26019 
26020       static double mp_minus(_cimg_math_parser& mp) {
26021         return -_mp_arg(2);
26022       }
26023 
26024       static double mp_median(_cimg_math_parser& mp) {
26025         const unsigned int i_end = (unsigned int)mp.opcode[2];
26026         switch (i_end - 3) {
26027         case 1 : return _mp_arg(3);
26028         case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
26029         case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
26030         case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
26031         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));
26032         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),
26033                                      _mp_arg(10),_mp_arg(11));
26034         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),
26035                                       _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
26036         }
26037         CImg<doubleT> vals(i_end - 3);
26038         double *p = vals.data();
26039         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26040         return vals.median();
26041       }
26042 
26043       static double mp_modulo(_cimg_math_parser& mp) {
26044         return cimg::mod(_mp_arg(2),_mp_arg(3));
26045       }
26046 
26047       static double mp_mproj(_cimg_math_parser& mp) {
26048         double *ptrd = &_mp_arg(1) + 1;
26049         const double
26050           *ptrS = &_mp_arg(2) + 1,
26051           *ptrD = &_mp_arg(5) + 1;
26052         const unsigned int
26053           wS = (unsigned int)mp.opcode[3],
26054           hS = (unsigned int)mp.opcode[4],
26055           wD = (unsigned int)mp.opcode[6];
26056         const int
26057           method = std::max(0,(int)_mp_arg(7)),
26058           max_iter = std::max(0,(int)_mp_arg(8));
26059         const double
26060           max_residual = std::max(0.,_mp_arg(9));
26061 
26062         CImg<doubleT>(ptrd,wS,wD,1,1,true) = CImg<doubleT>(ptrS,wS,hS,1,1,false).
26063           project_matrix(CImg<doubleT>(ptrD,wD,hS,1,1,true),method,max_iter,max_residual);
26064         return cimg::type<double>::nan();
26065       }
26066 
26067       static double mp_mul(_cimg_math_parser& mp) {
26068         return _mp_arg(2)*_mp_arg(3);
26069       }
26070 
26071       static double mp_mul2(_cimg_math_parser& mp) {
26072         return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
26073       }
26074 
26075       static double mp_neq(_cimg_math_parser& mp) {
26076         return (double)(_mp_arg(2)!=_mp_arg(3));
26077       }
26078 
26079       static double mp_norm0(_cimg_math_parser& mp) {
26080         const unsigned int i_end = (unsigned int)mp.opcode[2];
26081         switch (i_end - 3) {
26082         case 1 : return _mp_arg(3)!=0;
26083         case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
26084         }
26085         double res = 0;
26086         for (unsigned int i = 3; i<i_end; ++i)
26087           res+=_mp_arg(i)==0?0:1;
26088         return res;
26089       }
26090 
26091       static double mp_norm1(_cimg_math_parser& mp) {
26092         const unsigned int i_end = (unsigned int)mp.opcode[2];
26093         switch (i_end - 3) {
26094         case 1 : return cimg::abs(_mp_arg(3));
26095         case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
26096         }
26097         double res = 0;
26098         for (unsigned int i = 3; i<i_end; ++i)
26099           res+=cimg::abs(_mp_arg(i));
26100         return res;
26101       }
26102 
26103       static double mp_norm2(_cimg_math_parser& mp) {
26104         const unsigned int i_end = (unsigned int)mp.opcode[2];
26105         switch (i_end - 3) {
26106         case 1 : return cimg::abs(_mp_arg(3));
26107         case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
26108         }
26109         double res = 0;
26110         for (unsigned int i = 3; i<i_end; ++i)
26111           res+=cimg::sqr(_mp_arg(i));
26112         return std::sqrt(res);
26113       }
26114 
26115       static double mp_norminf(_cimg_math_parser& mp) {
26116         const unsigned int i_end = (unsigned int)mp.opcode[2];
26117         switch (i_end - 3) {
26118         case 1 : return cimg::abs(_mp_arg(3));
26119         case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
26120         }
26121         double res = 0;
26122         for (unsigned int i = 3; i<i_end; ++i) {
26123           const double val = cimg::abs(_mp_arg(i));
26124           if (val>res) res = val;
26125         }
26126         return res;
26127       }
26128 
26129       static double mp_normp(_cimg_math_parser& mp) {
26130         const unsigned int i_end = (unsigned int)mp.opcode[2];
26131         if (i_end==4) return cimg::abs(_mp_arg(3));
26132         const double p = (double)mp.opcode[3];
26133         double res = 0;
26134         for (unsigned int i = 4; i<i_end; ++i)
26135           res+=std::pow(cimg::abs(_mp_arg(i)),p);
26136         res = std::pow(res,1/p);
26137         return res>0?res:0.;
26138       }
26139 
26140       static double mp_permutations(_cimg_math_parser& mp) {
26141         return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
26142       }
26143 
26144       static double mp_polygon(_cimg_math_parser& mp) {
26145         const unsigned int i_end = (unsigned int)mp.opcode[2];
26146         unsigned int ind = (unsigned int)mp.opcode[3];
26147         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
26148         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
26149         bool is_invalid_arguments = i_end<=4, is_outlined = false;
26150         if (!is_invalid_arguments) {
26151           int nbv = (int)_mp_arg(4);
26152           if (!nbv) is_invalid_arguments = true;
26153           else {
26154             if (nbv<0) { nbv = -nbv; is_outlined = true; }
26155             CImg<intT> points(nbv,2,1,1,0);
26156             CImg<T> color(img._spectrum,1,1,1,0);
26157             float opacity = 1;
26158             unsigned int i = 5, pattern=~0U;
26159             cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
26160             else { is_invalid_arguments = true; break; }
26161             if (!is_invalid_arguments) {
26162               if (i<i_end) opacity = (float)_mp_arg(i++);
26163               if (is_outlined && i<i_end) pattern = (unsigned int)_mp_arg(i++);
26164               cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
26165               else { color.resize(k,1,1,1,-1); break; }
26166               color.resize(img._spectrum,1,1,1,0,2);
26167               if (is_outlined) img.draw_polygon(points,color._data,opacity,pattern);
26168               else img.draw_polygon(points,color._data,opacity);
26169             }
26170           }
26171         }
26172         if (is_invalid_arguments) {
26173           CImg<doubleT> args(i_end - 4);
26174           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
26175           if (ind==~0U)
26176             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
26177                                         "Invalid arguments '%s'. ",
26178                                         mp.imgin.pixel_type(),args.value_string()._data);
26179           else
26180             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
26181                                         "Invalid arguments '#%u%s%s'. ",
26182                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
26183         }
26184         return cimg::type<double>::nan();
26185       }
26186 
26187       static double mp_pow(_cimg_math_parser& mp) {
26188         const double v = _mp_arg(2), p = _mp_arg(3);
26189         return std::pow(v,p);
26190       }
26191 
26192       static double mp_pow0_25(_cimg_math_parser& mp) {
26193         const double val = _mp_arg(2);
26194         return std::sqrt(std::sqrt(val));
26195       }
26196 
26197       static double mp_pow3(_cimg_math_parser& mp) {
26198         const double val = _mp_arg(2);
26199         return val*val*val;
26200       }
26201 
26202       static double mp_pow4(_cimg_math_parser& mp) {
26203         const double val = _mp_arg(2);
26204         return val*val*val*val;
26205       }
26206 
26207       static double mp_print(_cimg_math_parser& mp) {
26208           const double val = _mp_arg(1);
26209           const bool print_char = (bool)mp.opcode[3];
26210           cimg_pragma_openmp(critical(mp_print))
26211           {
26212             CImg<charT> _expr(mp.opcode[2] - 4);
26213             const ulongT *ptrs = mp.opcode._data + 4;
26214             cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
26215             cimg::strellipsize(_expr);
26216             cimg::mutex(6);
26217             if (print_char)
26218               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'",
26219                            _expr._data,val,(int)val);
26220             else
26221               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g",
26222                            _expr._data,val);
26223             std::fflush(cimg::output());
26224             cimg::mutex(6,0);
26225           }
26226           return val;
26227       }
26228 
26229       static double mp_prod(_cimg_math_parser& mp) {
26230         const unsigned int i_end = (unsigned int)mp.opcode[2];
26231         double val = _mp_arg(3);
26232         for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
26233         return val;
26234       }
26235 
26236       static double mp_rad2deg(_cimg_math_parser& mp) {
26237         return _mp_arg(2)*180/cimg::PI;
26238       }
26239 
26240       static double mp_repeat(_cimg_math_parser& mp) {
26241         const double nb_it = _mp_arg(2);
26242         double
26243           *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
26244           *const ptrs = &_mp_arg(1);
26245         const CImg<ulongT>
26246           *const p_body = ++mp.p_code,
26247           *const p_end = p_body + mp.opcode[4];
26248 
26249         if (nb_it>0) {
26250           const unsigned int _break_type = mp.break_type;
26251           mp.break_type = 0;
26252 
26253           double it = 0;
26254           if (ptrc) { // Version with loop variable (3 arguments)
26255             while (it<nb_it) {
26256               *ptrc = it;
26257               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
26258                 mp.opcode._data = mp.p_code->_data;
26259                 const ulongT target = mp.opcode[1];
26260                 mp.mem[target] = _cimg_mp_defunc(mp);
26261               }
26262               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
26263               ++it;
26264             }
26265             *ptrc = it;
26266           } else // Version without loop variable (2 arguments)
26267             while (it<nb_it) {
26268               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
26269                 mp.opcode._data = mp.p_code->_data;
26270                 const ulongT target = mp.opcode[1];
26271                 mp.mem[target] = _cimg_mp_defunc(mp);
26272               }
26273               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
26274               ++it;
26275             }
26276           mp.break_type = _break_type;
26277         }
26278 
26279         mp.p_code = p_end - 1;
26280         return *ptrs;
26281       }
26282 
26283       static double mp_rol(_cimg_math_parser& mp) {
26284         return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
26285       }
26286 
26287       static double mp_ror(_cimg_math_parser& mp) {
26288         return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
26289       }
26290 
26291       static double mp_rot2d(_cimg_math_parser& mp) {
26292         double *ptrd = &_mp_arg(1) + 1;
26293         const float
26294           theta = (float)_mp_arg(2),
26295           ca = std::cos(theta),
26296           sa = std::sin(theta);
26297         *(ptrd++) = ca;
26298         *(ptrd++) = -sa;
26299         *(ptrd++) = sa;
26300         *ptrd = ca;
26301         return cimg::type<double>::nan();
26302       }
26303 
26304       static double mp_rot3d(_cimg_math_parser& mp) {
26305         double *ptrd = &_mp_arg(1) + 1;
26306         const float
26307           x = (float)_mp_arg(2),
26308           y = (float)_mp_arg(3),
26309           z = (float)_mp_arg(4),
26310           theta = (float)_mp_arg(5);
26311         CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta*180/cimg::PI);
26312         return cimg::type<double>::nan();
26313       }
26314 
26315       static double mp_round(_cimg_math_parser& mp) {
26316         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
26317       }
26318 
26319       static double mp_self_add(_cimg_math_parser& mp) {
26320         return _mp_arg(1)+=_mp_arg(2);
26321       }
26322 
26323       static double mp_self_bitwise_and(_cimg_math_parser& mp) {
26324         double &val = _mp_arg(1);
26325         return val = (double)((longT)val & (longT)_mp_arg(2));
26326       }
26327 
26328       static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
26329         double &val = _mp_arg(1);
26330         return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
26331       }
26332 
26333       static double mp_self_bitwise_or(_cimg_math_parser& mp) {
26334         double &val = _mp_arg(1);
26335         return val = (double)((longT)val | (longT)_mp_arg(2));
26336       }
26337 
26338       static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
26339         double &val = _mp_arg(1);
26340         return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
26341       }
26342 
26343       static double mp_self_decrement(_cimg_math_parser& mp) {
26344         return --_mp_arg(1);
26345       }
26346 
26347       static double mp_self_increment(_cimg_math_parser& mp) {
26348         return ++_mp_arg(1);
26349       }
26350 
26351       static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
26352         unsigned int
26353           ptrd = (unsigned int)mp.opcode[1] + 1,
26354           siz = (unsigned int)mp.opcode[2];
26355         mp_func op = (mp_func)mp.opcode[3];
26356         CImg<ulongT> l_opcode(1,3);
26357         l_opcode[2] = mp.opcode[4]; // Scalar argument
26358         l_opcode.swap(mp.opcode);
26359         ulongT &target = mp.opcode[1];
26360         while (siz-->0) { target = ptrd++; (*op)(mp); }
26361         l_opcode.swap(mp.opcode);
26362         return cimg::type<double>::nan();
26363       }
26364 
26365       static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
26366         unsigned int
26367           ptrd = (unsigned int)mp.opcode[1] + 1,
26368           siz = (unsigned int)mp.opcode[2],
26369           ptrs = (unsigned int)mp.opcode[4] + 1;
26370         mp_func op = (mp_func)mp.opcode[3];
26371         CImg<ulongT> l_opcode(1,4);
26372         l_opcode.swap(mp.opcode);
26373         ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
26374         while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
26375         l_opcode.swap(mp.opcode);
26376         return cimg::type<double>::nan();
26377       }
26378 
26379       static double mp_self_mul(_cimg_math_parser& mp) {
26380         return _mp_arg(1)*=_mp_arg(2);
26381       }
26382 
26383       static double mp_self_div(_cimg_math_parser& mp) {
26384         return _mp_arg(1)/=_mp_arg(2);
26385       }
26386 
26387       static double mp_self_modulo(_cimg_math_parser& mp) {
26388         double &val = _mp_arg(1);
26389         return val = cimg::mod(val,_mp_arg(2));
26390       }
26391 
26392       static double mp_self_pow(_cimg_math_parser& mp) {
26393         double &val = _mp_arg(1);
26394         return val = std::pow(val,_mp_arg(2));
26395       }
26396 
26397       static double mp_self_sub(_cimg_math_parser& mp) {
26398         return _mp_arg(1)-=_mp_arg(2);
26399       }
26400 
26401 #ifdef cimg_mp_func_set
26402       static double mp_set(_cimg_math_parser& mp) {
26403         const double *ptrs = &_mp_arg(1);
26404         double *ptrd = &_mp_arg(3) + 1;
26405         const unsigned int
26406           sizs = (unsigned int)mp.opcode[2],
26407           sizd = (unsigned int)mp.opcode[4];
26408         CImg<charT> sd(sizd + 1);
26409         cimg_for_inX(sd,0,sd.width() - 1,i) sd[i] = (char)ptrd[i];
26410         sd.back() = 0;
26411         if (sizs) cimg_mp_func_set(ptrs + 1,sizs,sd._data);
26412         else cimg_mp_func_set(ptrs,0,sd._data);
26413         return *ptrs;
26414       }
26415 #endif
26416 
26417       static double mp_set_ioff(_cimg_math_parser& mp) {
26418         CImg<T> &img = mp.imgout;
26419         const longT
26420           off = (longT)_mp_arg(2),
26421           whds = (longT)img.size();
26422         const double val = _mp_arg(1);
26423         if (off>=0 && off<whds) img[off] = (T)val;
26424         return val;
26425       }
26426 
26427       static double mp_set_ixyzc(_cimg_math_parser& mp) {
26428         CImg<T> &img = mp.imgout;
26429         const int
26430           x = (int)_mp_arg(2), y = (int)_mp_arg(3),
26431           z = (int)_mp_arg(4), c = (int)_mp_arg(5);
26432         const double val = _mp_arg(1);
26433         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
26434             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
26435           img(x,y,z,c) = (T)val;
26436         return val;
26437       }
26438 
26439       static double mp_set_joff(_cimg_math_parser& mp) {
26440         CImg<T> &img = mp.imgout;
26441         const int
26442           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26443           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26444         const longT
26445           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26446           whds = (longT)img.size();
26447         const double val = _mp_arg(1);
26448         if (off>=0 && off<whds) img[off] = (T)val;
26449         return val;
26450       }
26451 
26452       static double mp_set_jxyzc(_cimg_math_parser& mp) {
26453         CImg<T> &img = mp.imgout;
26454         const double
26455           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
26456           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
26457         const int
26458           x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
26459           z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
26460         const double val = _mp_arg(1);
26461         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
26462             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
26463           img(x,y,z,c) = (T)val;
26464         return val;
26465       }
26466 
26467       static double mp_set_Ioff_s(_cimg_math_parser& mp) {
26468         CImg<T> &img = mp.imgout;
26469         const longT
26470           off = (longT)_mp_arg(2),
26471           whd = (longT)img.width()*img.height()*img.depth();
26472         const T val = (T)_mp_arg(1);
26473         if (off>=0 && off<whd) {
26474           T *ptrd = &img[off];
26475           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26476         }
26477         return _mp_arg(1);
26478       }
26479 
26480       static double mp_set_Ioff_v(_cimg_math_parser& mp) {
26481         CImg<T> &img = mp.imgout;
26482         const longT
26483           off = (longT)_mp_arg(2),
26484           whd = (longT)img.width()*img.height()*img.depth();
26485         const double *ptrs = &_mp_arg(1) + 1;
26486         if (off>=0 && off<whd) {
26487           const unsigned int vsiz = (unsigned int)mp.opcode[3];
26488           T *ptrd = &img[off];
26489           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26490         }
26491         return cimg::type<double>::nan();
26492       }
26493 
26494       static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
26495         CImg<T> &img = mp.imgout;
26496         const int
26497           x = (int)_mp_arg(2),
26498           y = (int)_mp_arg(3),
26499           z = (int)_mp_arg(4);
26500         const T val = (T)_mp_arg(1);
26501         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26502           T *ptrd = &img(x,y,z);
26503           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26504           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26505         }
26506         return _mp_arg(1);
26507       }
26508 
26509       static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
26510         CImg<T> &img = mp.imgout;
26511         const int
26512           x = (int)_mp_arg(2),
26513           y = (int)_mp_arg(3),
26514           z = (int)_mp_arg(4);
26515         const double *ptrs = &_mp_arg(1) + 1;
26516         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26517           const unsigned int vsiz = (unsigned int)mp.opcode[5];
26518           T *ptrd = &img(x,y,z);
26519           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26520           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26521         }
26522         return cimg::type<double>::nan();
26523       }
26524 
26525       static double mp_set_Joff_s(_cimg_math_parser& mp) {
26526         CImg<T> &img = mp.imgout;
26527         const int
26528           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26529           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26530         const longT
26531           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26532           whd = (longT)img.width()*img.height()*img.depth();
26533         const T val = (T)_mp_arg(1);
26534         if (off>=0 && off<whd) {
26535           T *ptrd = &img[off];
26536           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26537         }
26538         return _mp_arg(1);
26539       }
26540 
26541       static double mp_set_Joff_v(_cimg_math_parser& mp) {
26542         CImg<T> &img = mp.imgout;
26543         const int
26544           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
26545           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
26546         const longT
26547           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
26548           whd = (longT)img.width()*img.height()*img.depth();
26549         const double *ptrs = &_mp_arg(1) + 1;
26550         if (off>=0 && off<whd) {
26551           const unsigned int vsiz = (unsigned int)mp.opcode[3];
26552           T *ptrd = &img[off];
26553           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26554         }
26555         return cimg::type<double>::nan();
26556       }
26557 
26558       static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
26559         CImg<T> &img = mp.imgout;
26560         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
26561         const int
26562           x = (int)(ox + _mp_arg(2)),
26563           y = (int)(oy + _mp_arg(3)),
26564           z = (int)(oz + _mp_arg(4));
26565         const T val = (T)_mp_arg(1);
26566         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26567           T *ptrd = &img(x,y,z);
26568           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26569           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
26570         }
26571         return _mp_arg(1);
26572       }
26573 
26574       static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
26575         CImg<T> &img = mp.imgout;
26576         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
26577         const int
26578           x = (int)(ox + _mp_arg(2)),
26579           y = (int)(oy + _mp_arg(3)),
26580           z = (int)(oz + _mp_arg(4));
26581         const double *ptrs = &_mp_arg(1) + 1;
26582         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
26583           const unsigned int vsiz = (unsigned int)mp.opcode[5];
26584           T *ptrd = &img(x,y,z);
26585           const ulongT whd = (ulongT)img._width*img._height*img._depth;
26586           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
26587         }
26588         return cimg::type<double>::nan();
26589       }
26590 
26591       static double mp_shift(_cimg_math_parser& mp) {
26592         double *const ptrd = &_mp_arg(1) + 1;
26593         const double *const ptrs = &_mp_arg(2) + 1;
26594         const unsigned int siz = (unsigned int)mp.opcode[3];
26595         const int
26596           shift = (int)_mp_arg(4),
26597           boundary_conditions = (int)_mp_arg(5);
26598         CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
26599         return cimg::type<double>::nan();
26600       }
26601 
26602       static double mp_sign(_cimg_math_parser& mp) {
26603         return cimg::sign(_mp_arg(2));
26604       }
26605 
26606       static double mp_sin(_cimg_math_parser& mp) {
26607         return std::sin(_mp_arg(2));
26608       }
26609 
26610       static double mp_sinc(_cimg_math_parser& mp) {
26611         return cimg::sinc(_mp_arg(2));
26612       }
26613 
26614       static double mp_sinh(_cimg_math_parser& mp) {
26615         return std::sinh(_mp_arg(2));
26616       }
26617 
26618       static double mp_solve(_cimg_math_parser& mp) {
26619         double *ptrd = &_mp_arg(1) + 1;
26620         const double
26621           *ptr1 = &_mp_arg(2) + 1,
26622           *ptr2 = &_mp_arg(3) + 1;
26623         const unsigned int
26624           k = (unsigned int)mp.opcode[4],
26625           l = (unsigned int)mp.opcode[5],
26626           m = (unsigned int)mp.opcode[6];
26627         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));
26628         return cimg::type<double>::nan();
26629       }
26630 
26631       static double mp_sort(_cimg_math_parser& mp) {
26632         double *const ptrd = &_mp_arg(1) + 1;
26633         const double *const ptrs = &_mp_arg(2) + 1;
26634         const bool is_increasing = (bool)_mp_arg(4);
26635         const unsigned int
26636           siz = (unsigned int)mp.opcode[3],
26637           nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5),
26638           siz_elt = (unsigned int)_mp_arg(6);
26639         const ulongT sn = siz_elt*nb_elts;
26640         if (sn>siz || siz_elt<1)
26641           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': "
26642                                       "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid "
26643                                       "for sorting a vector of size %u.",
26644                                       mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz);
26645         CImg<doubleT>(ptrd,siz_elt,nb_elts,1,1,true) = CImg<doubleT>(ptrs,siz_elt,nb_elts,1,1,true).
26646           get_sort(is_increasing,siz_elt>1?'y':0);
26647         if (sn<siz) CImg<doubleT>(ptrd + sn,siz - sn,1,1,1,true) = CImg<doubleT>(ptrs + sn,siz - sn,1,1,1,true);
26648         return cimg::type<double>::nan();
26649       }
26650 
26651       static double mp_sqr(_cimg_math_parser& mp) {
26652         return cimg::sqr(_mp_arg(2));
26653       }
26654 
26655       static double mp_sqrt(_cimg_math_parser& mp) {
26656         return std::sqrt(_mp_arg(2));
26657       }
26658 
26659       static double mp_srand(_cimg_math_parser& mp) {
26660         mp.rng = (cimg_uint64)_mp_arg(2);
26661         return cimg::type<double>::nan();
26662       }
26663 
26664       static double mp_srand0(_cimg_math_parser& mp) {
26665         cimg::srand(&mp.rng);
26666 
26667 #if cimg_use_openmp!=0
26668         mp.rng+=omp_get_thread_num();
26669 #endif
26670         return cimg::type<double>::nan();
26671       }
26672 
26673       static double mp_std(_cimg_math_parser& mp) {
26674         const unsigned int i_end = (unsigned int)mp.opcode[2];
26675         CImg<doubleT> vals(i_end - 3);
26676         double *p = vals.data();
26677         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26678         return std::sqrt(vals.variance());
26679       }
26680 
26681       static double mp_string_init(_cimg_math_parser& mp) {
26682         const unsigned char *ptrs = (unsigned char*)&mp.opcode[3];
26683         unsigned int
26684           ptrd = (unsigned int)mp.opcode[1] + 1,
26685           siz = (unsigned int)mp.opcode[2];
26686         while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
26687         return cimg::type<double>::nan();
26688       }
26689 
26690 #ifdef cimg_mp_func_store
26691       static double mp_store(_cimg_math_parser& mp) {
26692         const double
26693           *ptr1 = &_mp_arg(2),
26694           *ptr2 = &_mp_arg(4) + 1;
26695         const unsigned int
26696           siz1 = (unsigned int)mp.opcode[3],
26697           siz2 = (unsigned int)mp.opcode[5];
26698         const int
26699           w = (int)_mp_arg(6),
26700           h = (int)_mp_arg(7),
26701           d = (int)_mp_arg(8),
26702           s = (int)_mp_arg(9);
26703 
26704         const bool is_compressed = (bool)_mp_arg(10);
26705         if (w<0 || h<0 || d<0 || s<0)
26706           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
26707                                       "Specified image dimensions (%d,%d,%d,%d) are invalid.",
26708                                       pixel_type(),w,h,d,s);
26709         CImg<charT> ss(siz2 + 1);
26710         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i];
26711         ss.back() = 0;
26712         if (siz1) cimg_mp_func_store(ptr1 + 1,siz1,
26713                                      (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
26714                                      is_compressed,ss._data);
26715         else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
26716                                 is_compressed,ss._data);
26717         return cimg::type<double>::nan();
26718       }
26719 #endif
26720 
26721       static double mp_stov(_cimg_math_parser& mp) {
26722         const double *ptrs = &_mp_arg(2);
26723         const ulongT siz = (ulongT)mp.opcode[3];
26724         longT ind = (longT)_mp_arg(4);
26725         const bool is_strict = (bool)_mp_arg(5);
26726         double val = cimg::type<double>::nan();
26727         if (ind<0 || ind>=(longT)siz) return val;
26728         if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
26729 
26730         CImg<charT> ss(siz + 1 - ind);
26731         ptrs+=1 + ind;
26732         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
26733         ss.back() = 0;
26734 
26735         const char *s = ss._data;
26736         while (*s && *s<=32) ++s;
26737         const bool is_negative = *s=='-';
26738         if (is_negative || *s=='+') ++s;
26739         int err = 0;
26740         char sep;
26741 
26742         if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number
26743           unsigned int ival;
26744           err = cimg_sscanf(s + 2,"%x%c",&ival,&sep);
26745           if (err>0) val = (double)ival;
26746         } else if (*s>32) { // Decimal number
26747           err = cimg_sscanf(s,"%lf%c",&val,&sep);
26748 #if cimg_OS==2
26749           // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
26750           // to read those particular values.
26751           if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) {
26752             if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type<double>::inf(); err = 1 + (s[3]!=0); }
26753             else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type<double>::nan(); err = 1 + (s[3]!=0); }
26754           }
26755 #endif
26756         }
26757         if (err<=0 || (is_strict && err!=1)) return cimg::type<double>::nan();
26758         if (is_negative) val = -val;
26759         return val;
26760       }
26761 
26762       static double mp_string(_cimg_math_parser& mp) {
26763         double *const ptrd = &_mp_arg(1) + 1;
26764         const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2;
26765         CImgList<charT> _str;
26766         CImg<charT> it;
26767         for (unsigned int n = 0; n<nb_args; ++n) {
26768           const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
26769           if (siz) { // Vector argument -> string
26770             const double *ptr = &_mp_arg(4 + 2*n) + 1;
26771             unsigned int l = 0;
26772             while (l<siz && ptr[l]) ++l;
26773             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
26774           } else { // Scalar argument -> number
26775             it.assign(24);
26776             cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n));
26777             CImg<charT>::string(it,false,true).move_to(_str);
26778           }
26779         }
26780         const CImg<charT> str = _str>'x';
26781         const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]);
26782         std::memset(ptrd,0,mp.opcode[2]*sizeof(double));
26783         for (unsigned int k = 0; k<sizd; ++k) ptrd[k] = (double)str[k];
26784         return cimg::type<double>::nan();
26785       }
26786 
26787       static double mp_sub(_cimg_math_parser& mp) {
26788         return _mp_arg(2) - _mp_arg(3);
26789       }
26790 
26791       static double mp_sum(_cimg_math_parser& mp) {
26792         const unsigned int i_end = (unsigned int)mp.opcode[2];
26793         double val = _mp_arg(3);
26794         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
26795         return val;
26796       }
26797 
26798       static double mp_swap(_cimg_math_parser& mp) {
26799         const unsigned int siz = (unsigned int)mp.opcode[3];
26800         if (!siz) { // Scalar
26801           double &arg1 = _mp_arg(1), &arg2 = _mp_arg(2);
26802           cimg::swap(arg1,arg2);
26803         } else { // Vector
26804           double *const ptr1 = &_mp_arg(1) + 1, *const ptr2 = &_mp_arg(2) + 1;
26805           for (unsigned int k = 0; k<siz; ++k) cimg::swap(ptr1[k],ptr2[k]);
26806         }
26807         return _mp_arg(1);
26808       }
26809 
26810       static double mp_tan(_cimg_math_parser& mp) {
26811         return std::tan(_mp_arg(2));
26812       }
26813 
26814       static double mp_tanh(_cimg_math_parser& mp) {
26815         return std::tanh(_mp_arg(2));
26816       }
26817 
26818       static double mp_trace(_cimg_math_parser& mp) {
26819         const double *ptrs = &_mp_arg(2) + 1;
26820         const unsigned int k = (unsigned int)mp.opcode[3];
26821         return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
26822       }
26823 
26824       static double mp_transpose(_cimg_math_parser& mp) {
26825         double *ptrd = &_mp_arg(1) + 1;
26826         const double *ptrs = &_mp_arg(2) + 1;
26827         const unsigned int
26828           k = (unsigned int)mp.opcode[3],
26829           l = (unsigned int)mp.opcode[4];
26830         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
26831         return cimg::type<double>::nan();
26832       }
26833 
26834       static double mp_u(_cimg_math_parser& mp) {
26835         return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng);
26836       }
26837 
26838       static double mp_ui2f(_cimg_math_parser& mp) {
26839         return (double)cimg::uint2float((unsigned int)_mp_arg(2));
26840       }
26841 
26842       static double mp_uppercase(_cimg_math_parser& mp) {
26843         return cimg::uppercase(_mp_arg(2));
26844       }
26845 
26846       static double mp_var(_cimg_math_parser& mp) {
26847         const unsigned int i_end = (unsigned int)mp.opcode[2];
26848         CImg<doubleT> vals(i_end - 3);
26849         double *p = vals.data();
26850         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26851         return vals.variance();
26852       }
26853 
26854       static double mp_vector_copy(_cimg_math_parser& mp) {
26855         std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
26856         return cimg::type<double>::nan();
26857       }
26858 
26859       static double mp_vector_crop(_cimg_math_parser& mp) {
26860         double *ptrd = &_mp_arg(1) + 1;
26861         const double *ptrs = &_mp_arg(2) + 1;
26862         const longT
26863           length = (longT)mp.opcode[3],
26864           start = (longT)_mp_arg(4),
26865           sublength = (longT)mp.opcode[5],
26866           step = (longT)_mp_arg(6);
26867         if (start<0 || start + step*(sublength-1)>=length)
26868           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
26869                                       "Out-of-bounds sub-vector request "
26870                                       "(length: %ld, start: %ld, sub-length: %ld, step: %ld).",
26871                                       mp.imgin.pixel_type(),length,start,sublength,step);
26872         ptrs+=start;
26873         if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double));
26874         else for (longT k = 0; k<sublength; ++k) { *(ptrd++) = *ptrs; ptrs+=step; }
26875         return cimg::type<double>::nan();
26876       }
26877 
26878       static double mp_vector_init(_cimg_math_parser& mp) {
26879         unsigned int
26880           ptrs = 4U,
26881           ptrd = (unsigned int)mp.opcode[1] + 1,
26882           siz = (unsigned int)mp.opcode[3];
26883         switch (mp.opcode[2] - 4) {
26884         case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
26885         case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
26886         default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
26887         }
26888         return cimg::type<double>::nan();
26889       }
26890 
26891       static double mp_vector_eq(_cimg_math_parser& mp) {
26892         const double
26893           *ptr1 = &_mp_arg(2) + 1,
26894           *ptr2 = &_mp_arg(4) + 1;
26895         unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
26896         const int N = (int)_mp_arg(6);
26897         const bool case_sensitive = (bool)_mp_arg(7);
26898         bool still_equal = true;
26899         double value;
26900         if (!N) return true;
26901 
26902         // Compare all values.
26903         if (N<0) {
26904           if (p1>0 && p2>0) { // Vector == vector
26905             if (p1!=p2) return false;
26906             if (case_sensitive)
26907               while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
26908             else
26909               while (still_equal && p1--)
26910                 still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26911             return still_equal;
26912           } else if (p1>0 && !p2) { // Vector == scalar
26913             value = _mp_arg(4);
26914             if (!case_sensitive) value = cimg::lowercase(value);
26915             while (still_equal && p1--) still_equal = *(ptr1++)==value;
26916             return still_equal;
26917           } else if (!p1 && p2>0) { // Scalar == vector
26918             value = _mp_arg(2);
26919             if (!case_sensitive) value = cimg::lowercase(value);
26920             while (still_equal && p2--) still_equal = *(ptr2++)==value;
26921             return still_equal;
26922           } else { // Scalar == scalar
26923             if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26924             else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26925           }
26926         }
26927 
26928         // Compare only first N values.
26929         if (p1>0 && p2>0) { // Vector == vector
26930           n = cimg::min((unsigned int)N,p1,p2);
26931           if (case_sensitive)
26932             while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
26933           else
26934             while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26935           return still_equal;
26936         } else if (p1>0 && !p2) { // Vector == scalar
26937           n = std::min((unsigned int)N,p1);
26938           value = _mp_arg(4);
26939           if (!case_sensitive) value = cimg::lowercase(value);
26940           while (still_equal && n--) still_equal = *(ptr1++)==value;
26941           return still_equal;
26942         } else if (!p1 && p2>0) { // Scalar == vector
26943           n = std::min((unsigned int)N,p2);
26944           value = _mp_arg(2);
26945           if (!case_sensitive) value = cimg::lowercase(value);
26946           while (still_equal && n--) still_equal = *(ptr2++)==value;
26947           return still_equal;
26948         }  // Scalar == scalar
26949         if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26950         return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26951       }
26952 
26953       static double mp_vector_lerp(_cimg_math_parser& mp) {
26954         unsigned int siz = (unsigned int)mp.opcode[2];
26955         double *ptrd = &_mp_arg(1) + 1;
26956         const double
26957           *ptrs1 = &_mp_arg(3) + 1,
26958           *ptrs2 = &_mp_arg(4) + 1,
26959           t = _mp_arg(5);
26960         for (unsigned int k = 0; k<siz; ++k) ptrd[k] = ptrs1[k]*(1-t) + ptrs2[k]*t;
26961         return cimg::type<double>::nan();
26962       }
26963 
26964       static double mp_vector_off(_cimg_math_parser& mp) {
26965         const unsigned int
26966           ptr = (unsigned int)mp.opcode[2] + 1,
26967           siz = (unsigned int)mp.opcode[3];
26968         const int off = (int)_mp_arg(4);
26969         return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
26970       }
26971 
26972       static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
26973         unsigned int
26974           siz = (unsigned int)mp.opcode[2],
26975           ptrs = (unsigned int)mp.opcode[5] + 1;
26976         double *ptrd = &_mp_arg(1) + 1;
26977         mp_func op = (mp_func)mp.opcode[3];
26978         CImg<ulongT> l_opcode(4);
26979         l_opcode[2] = mp.opcode[4]; // Scalar argument1
26980         l_opcode.swap(mp.opcode);
26981         ulongT &argument2 = mp.opcode[3];
26982         while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
26983         l_opcode.swap(mp.opcode);
26984         return cimg::type<double>::nan();
26985       }
26986 
26987       static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
26988         unsigned int
26989           siz = (unsigned int)mp.opcode[2],
26990           ptrs = (unsigned int)mp.opcode[4] + 1;
26991         double *ptrd = &_mp_arg(1) + 1;
26992         mp_func op = (mp_func)mp.opcode[3];
26993         CImg<ulongT> l_opcode(1,3);
26994         l_opcode.swap(mp.opcode);
26995         ulongT &argument = mp.opcode[2];
26996         while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
26997         l_opcode.swap(mp.opcode);
26998         return cimg::type<double>::nan();
26999       }
27000 
27001       static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
27002         unsigned int
27003           siz = (unsigned int)mp.opcode[2],
27004           ptrs = (unsigned int)mp.opcode[4] + 1;
27005         double *ptrd = &_mp_arg(1) + 1;
27006         mp_func op = (mp_func)mp.opcode[3];
27007         CImg<ulongT> l_opcode(1,4);
27008         l_opcode[3] = mp.opcode[5]; // Scalar argument2
27009         l_opcode.swap(mp.opcode);
27010         ulongT &argument1 = mp.opcode[2];
27011         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
27012         l_opcode.swap(mp.opcode);
27013         return cimg::type<double>::nan();
27014       }
27015 
27016       static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
27017         unsigned int
27018           siz = (unsigned int)mp.opcode[2],
27019           ptrs = (unsigned int)mp.opcode[4] + 1;
27020         double *ptrd = &_mp_arg(1) + 1;
27021         mp_func op = (mp_func)mp.opcode[3];
27022         CImg<ulongT> l_opcode(1,5);
27023         l_opcode[3] = mp.opcode[5]; // Scalar argument2
27024         l_opcode[4] = mp.opcode[6]; // Scalar argument3
27025         l_opcode.swap(mp.opcode);
27026         ulongT &argument1 = mp.opcode[2];
27027         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
27028         l_opcode.swap(mp.opcode);
27029         return cimg::type<double>::nan();
27030       }
27031 
27032       static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
27033         unsigned int
27034           siz = (unsigned int)mp.opcode[2],
27035           ptrs1 = (unsigned int)mp.opcode[4] + 1,
27036           ptrs2 = (unsigned int)mp.opcode[5] + 1;
27037         double *ptrd = &_mp_arg(1) + 1;
27038         mp_func op = (mp_func)mp.opcode[3];
27039         CImg<ulongT> l_opcode(1,4);
27040         l_opcode.swap(mp.opcode);
27041         ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
27042         while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
27043         l_opcode.swap(mp.opcode);
27044         return cimg::type<double>::nan();
27045       }
27046 
27047       static double mp_vector_neq(_cimg_math_parser& mp) {
27048         return !mp_vector_eq(mp);
27049       }
27050 
27051       static double mp_vector_print(_cimg_math_parser& mp) {
27052         const bool print_string = (bool)mp.opcode[4];
27053         cimg_pragma_openmp(critical(mp_vector_print))
27054         {
27055           CImg<charT> _expr(mp.opcode[2] - 5);
27056           const ulongT *ptrs = mp.opcode._data + 5;
27057           cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
27058           cimg::strellipsize(_expr);
27059           unsigned int
27060             ptr = (unsigned int)mp.opcode[1] + 1,
27061             siz0 = (unsigned int)mp.opcode[3],
27062             siz = siz0;
27063           cimg::mutex(6);
27064           std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data);
27065           unsigned int count = 0;
27066           while (siz-->0) {
27067             if (count>=64 && siz>=64) {
27068               std::fprintf(cimg::output(),"...,");
27069               ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
27070               siz = 64;
27071             } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":"");
27072             ++count;
27073           }
27074           if (print_string) {
27075             CImg<charT> str(siz0 + 1);
27076             ptr = (unsigned int)mp.opcode[1] + 1;
27077             for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
27078             str[siz0] = 0;
27079             cimg::strellipsize(str,1024,false);
27080             std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
27081           } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
27082           std::fflush(cimg::output());
27083           cimg::mutex(6,0);
27084         }
27085         return cimg::type<double>::nan();
27086       }
27087 
27088       static double mp_vector_resize(_cimg_math_parser& mp) {
27089         double *const ptrd = &_mp_arg(1) + 1;
27090         const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
27091         const int
27092           interpolation = (int)_mp_arg(5),
27093           boundary_conditions = (int)_mp_arg(6);
27094         if (p2) { // Resize vector
27095           const double *const ptrs = &_mp_arg(3) + 1;
27096           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
27097             get_resize(p1,1,1,1,interpolation,boundary_conditions);
27098         } else { // Resize scalar
27099           const double value = _mp_arg(3);
27100           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
27101                                                                                   boundary_conditions);
27102         }
27103         return cimg::type<double>::nan();
27104       }
27105 
27106       static double mp_vector_resize_ext(_cimg_math_parser& mp) {
27107         double *const ptrd = &_mp_arg(1) + 1;
27108         const unsigned int
27109           siz = (unsigned int)mp.opcode[2],
27110           ow = (unsigned int)mp.opcode[4],
27111           oh = (unsigned int)mp.opcode[5],
27112           od = (unsigned int)mp.opcode[6],
27113           os = (unsigned int)mp.opcode[7],
27114           nw = (unsigned int)mp.opcode[8],
27115           nh = (unsigned int)mp.opcode[9],
27116           nd = (unsigned int)mp.opcode[10],
27117           ns = (unsigned int)mp.opcode[11];
27118         const int
27119           interpolation = (int)_mp_arg(12),
27120           boundary_conditions = (int)_mp_arg(13);
27121         const float
27122           ax = (float)_mp_arg(14),
27123           ay = (float)_mp_arg(15),
27124           az = (float)_mp_arg(16),
27125           ac = (float)_mp_arg(17);
27126         if (siz) { // Resize vector
27127           const double *const ptrs = &_mp_arg(3) + 1;
27128           CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(ptrs,ow,oh,od,os,true).
27129             get_resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
27130         } else { // Resize scalar
27131           const double value = _mp_arg(3);
27132           CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(1,1,1,1,value).
27133             resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
27134         }
27135         return cimg::type<double>::nan();
27136       }
27137 
27138       static double mp_vector_reverse(_cimg_math_parser& mp) {
27139         double *const ptrd = &_mp_arg(1) + 1;
27140         const double *const ptrs = &_mp_arg(2) + 1;
27141         const unsigned int p1 = (unsigned int)mp.opcode[3];
27142         CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
27143         return cimg::type<double>::nan();
27144       }
27145 
27146       static double mp_vector_set_off(_cimg_math_parser& mp) {
27147         const unsigned int
27148           ptr = (unsigned int)mp.opcode[2] + 1,
27149           siz = (unsigned int)mp.opcode[3];
27150         const int off = (int)_mp_arg(4);
27151         if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1);
27152         return _mp_arg(1);
27153       }
27154 
27155 #define _cimg_mp_vfunc(func) \
27156       const longT sizd = (longT)mp.opcode[2];\
27157       const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
27158       double *const ptrd = &_mp_arg(1) + (sizd?1:0); \
27159       cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \
27160       { CImg<doubleT> vec(nbargs); double res; \
27161         cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \
27162           cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \
27163           func; ptrd[k] = res; \
27164       }} \
27165       return sizd?cimg::type<double>::nan():*ptrd;
27166 
27167       static double _mp_vargkth(CImg<doubleT>& vec) {
27168         const double val = (+vec).get_shared_points(1,vec.width() - 1).
27169           kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2));
27170         cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.;
27171         return 1.;
27172       }
27173 
27174       static double mp_vargkth(_cimg_math_parser& mp) {
27175         _cimg_mp_vfunc(res = _mp_vargkth(vec));
27176       }
27177 
27178       static double mp_vargmax(_cimg_math_parser& mp) {
27179         _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data()));
27180       }
27181 
27182       static double mp_vargmaxabs(_cimg_math_parser& mp) {
27183         _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data()));
27184       }
27185 
27186       static double mp_vargmin(_cimg_math_parser& mp) {
27187         _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data()));
27188       }
27189 
27190       static double mp_vargminabs(_cimg_math_parser& mp) {
27191         _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data()));
27192       }
27193 
27194       static double mp_vavg(_cimg_math_parser& mp) {
27195         _cimg_mp_vfunc(res = vec.mean());
27196       }
27197 
27198       static double mp_vkth(_cimg_math_parser& mp) {
27199         _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1).
27200                        kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)));
27201       }
27202 
27203       static double mp_vmax(_cimg_math_parser& mp) {
27204         _cimg_mp_vfunc(res = vec.max());
27205       }
27206 
27207       static double mp_vmaxabs(_cimg_math_parser& mp) {
27208         _cimg_mp_vfunc(res = vec.maxabs());
27209       }
27210 
27211       static double mp_vmedian(_cimg_math_parser& mp) {
27212         _cimg_mp_vfunc(res = vec.median());
27213       }
27214 
27215       static double mp_vmin(_cimg_math_parser& mp) {
27216         _cimg_mp_vfunc(res = vec.min());
27217       }
27218 
27219       static double mp_vminabs(_cimg_math_parser& mp) {
27220         _cimg_mp_vfunc(res = vec.minabs());
27221       }
27222 
27223       static double mp_vprod(_cimg_math_parser& mp) {
27224         _cimg_mp_vfunc(res = vec.product());
27225       }
27226 
27227       static double mp_vstd(_cimg_math_parser& mp) {
27228         _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3]));
27229       }
27230 
27231       static double mp_vsum(_cimg_math_parser& mp) {
27232         _cimg_mp_vfunc(res = vec.sum());
27233       }
27234 
27235       static double mp_vvar(_cimg_math_parser& mp) {
27236         _cimg_mp_vfunc(res = vec.get_stats()[3]);
27237       }
27238 
27239       static double mp_vtos(_cimg_math_parser& mp) {
27240         double *ptrd = &_mp_arg(1) + 1;
27241         const unsigned int
27242           sizd = (unsigned int)mp.opcode[2],
27243           sizs = (unsigned int)mp.opcode[4];
27244         std::memset(ptrd,0,sizd*sizeof(double));
27245         const int nb_digits = (int)_mp_arg(5);
27246         CImg<charT> format(8);
27247         switch (nb_digits) {
27248         case -1 : std::strcpy(format,"%g"); break;
27249         case 0 : std::strcpy(format,"%.17g"); break;
27250         default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
27251         }
27252         CImg<charT> str;
27253         if (sizs) { // Vector expression
27254           const double *ptrs = &_mp_arg(3) + 1;
27255           CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
27256         } else { // Scalar expression
27257           str.assign(sizd + 1);
27258           cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
27259         }
27260         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
27261         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
27262         return cimg::type<double>::nan();
27263      }
27264 
27265       static double mp_while(_cimg_math_parser& mp) {
27266         const ulongT
27267           mem_body = mp.opcode[1],
27268           mem_cond = mp.opcode[2];
27269         const CImg<ulongT>
27270           *const p_cond = ++mp.p_code,
27271           *const p_body = p_cond + mp.opcode[3],
27272           *const p_end = p_body + mp.opcode[4];
27273         const unsigned int vsiz = (unsigned int)mp.opcode[5];
27274         bool is_cond = false;
27275         if (mp.opcode[6]) { // Set default value for result and condition if necessary
27276           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
27277           else mp.mem[mem_body] = cimg::type<double>::nan();
27278         }
27279         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
27280         const unsigned int _break_type = mp.break_type;
27281         mp.break_type = 0;
27282         do {
27283           for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
27284             mp.opcode._data = mp.p_code->_data;
27285             const ulongT target = mp.opcode[1];
27286             mp.mem[target] = _cimg_mp_defunc(mp);
27287           }
27288           if (mp.break_type==1) break;
27289           is_cond = (bool)mp.mem[mem_cond];
27290           if (is_cond && !mp.break_type) // Evaluate body
27291             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
27292               mp.opcode._data = mp.p_code->_data;
27293               const ulongT target = mp.opcode[1];
27294               mp.mem[target] = _cimg_mp_defunc(mp);
27295             }
27296           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
27297         } while (is_cond);
27298 
27299         mp.break_type = _break_type;
27300         mp.p_code = p_end - 1;
27301         return mp.mem[mem_body];
27302       }
27303 
27304       static double mp_Ioff(_cimg_math_parser& mp) {
27305         double *ptrd = &_mp_arg(1) + 1;
27306         const unsigned int
27307           boundary_conditions = (unsigned int)_mp_arg(3),
27308           vsiz = (unsigned int)mp.opcode[4];
27309         const CImg<T> &img = mp.imgin;
27310         const longT
27311           off = (longT)_mp_arg(2),
27312           whd = (longT)img.width()*img.height()*img.depth();
27313         const T *ptrs;
27314         if (off>=0 && off<whd) {
27315           ptrs = &img[off];
27316           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27317           return cimg::type<double>::nan();
27318         }
27319         if (img._data) switch (boundary_conditions) {
27320           case 3 : { // Mirror
27321             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
27322             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
27323             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27324             return cimg::type<double>::nan();
27325           }
27326           case 2 : // Periodic
27327             ptrs = &img[cimg::mod(off,whd)];
27328             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27329             return cimg::type<double>::nan();
27330           case 1 : // Neumann
27331             ptrs = off<0?&img[0]:&img[whd - 1];
27332             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27333             return cimg::type<double>::nan();
27334           default : // Dirichlet
27335             std::memset(ptrd,0,vsiz*sizeof(double));
27336             return cimg::type<double>::nan();
27337           }
27338         std::memset(ptrd,0,vsiz*sizeof(double));
27339         return cimg::type<double>::nan();
27340       }
27341 
27342       static double mp_Ixyz(_cimg_math_parser& mp) {
27343         double *ptrd = &_mp_arg(1) + 1;
27344         const unsigned int
27345           interpolation = (unsigned int)_mp_arg(5),
27346           boundary_conditions = (unsigned int)_mp_arg(6),
27347           vsiz = (unsigned int)mp.opcode[7];
27348         const CImg<T> &img = mp.imgin;
27349         const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
27350         const ulongT whd = (ulongT)img._width*img._height*img._depth;
27351         const T *ptrs;
27352         switch (interpolation) {
27353         case 2 : // Cubic interpolation
27354           switch (boundary_conditions) {
27355           case 3 : { // Mirror
27356             const float
27357               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27358               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27359               cx = mx<img.width()?mx:w2 - mx - 1,
27360               cy = my<img.height()?my:h2 - my - 1,
27361               cz = mz<img.depth()?mz:d2 - mz - 1;
27362             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
27363           } break;
27364           case 2 : // Periodic
27365             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
27366             break;
27367           case 1 : // Neumann
27368             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
27369             break;
27370           default : // Dirichlet
27371             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27372           } break;
27373         case 1 : // Linear interpolation
27374           switch (boundary_conditions) {
27375           case 3 : { // Mirror
27376             const float
27377               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27378               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27379               cx = mx<img.width()?mx:w2 - mx - 1,
27380               cy = my<img.height()?my:h2 - my - 1,
27381               cz = mz<img.depth()?mz:d2 - mz - 1;
27382             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
27383           } break;
27384           case 2 : // Periodic
27385             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
27386             break;
27387           case 1 : // Neumann
27388             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
27389             break;
27390           default : // Dirichlet
27391             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27392           } break;
27393         default : // Nearest neighbor interpolation
27394           switch (boundary_conditions) {
27395           case 3 : { // Mirror
27396             const int
27397               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
27398               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
27399               cx = mx<img.width()?mx:w2 - mx - 1,
27400               cy = my<img.height()?my:h2 - my - 1,
27401               cz = mz<img.depth()?mz:d2 - mz - 1;
27402             ptrs = &img(cx,cy,cz);
27403             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27404           } break;
27405           case 2 : { // Periodic
27406             const int
27407               cx = (int)cimg::mod(x,(double)img._width),
27408               cy = (int)cimg::mod(y,(double)img._height),
27409               cz = (int)cimg::mod(z,(double)img._depth);
27410             ptrs = &img(cx,cy,cz);
27411             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27412           } break;
27413           case 1 : { // Neumann
27414             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
27415             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27416           } break;
27417           default : // Dirichlet
27418             if (img.containsXYZC((int)x,(int)y,(int)z)) {
27419               ptrs = &img((int)x,(int)y,(int)z);
27420               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27421             } else std::memset(ptrd,0,vsiz*sizeof(double));
27422           }
27423         }
27424         return cimg::type<double>::nan();
27425       }
27426 
27427       static double mp_Joff(_cimg_math_parser& mp) {
27428         double *ptrd = &_mp_arg(1) + 1;
27429         const unsigned int
27430           boundary_conditions = (unsigned int)_mp_arg(3),
27431           vsiz = (unsigned int)mp.opcode[4];
27432         const CImg<T> &img = mp.imgin;
27433         const int
27434           ox = (int)mp.mem[_cimg_mp_slot_x],
27435           oy = (int)mp.mem[_cimg_mp_slot_y],
27436           oz = (int)mp.mem[_cimg_mp_slot_z];
27437         const longT
27438           off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
27439           whd = (longT)img.width()*img.height()*img.depth();
27440         const T *ptrs;
27441         if (off>=0 && off<whd) {
27442           ptrs = &img[off];
27443           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27444           return cimg::type<double>::nan();
27445         }
27446         if (img._data) switch (boundary_conditions) {
27447           case 3 : { // Mirror
27448             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
27449             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
27450             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27451             return cimg::type<double>::nan();
27452           }
27453           case 2 : // Periodic
27454             ptrs = &img[cimg::mod(off,whd)];
27455             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27456             return cimg::type<double>::nan();
27457           case 1 : // Neumann
27458             ptrs = off<0?&img[0]:&img[whd - 1];
27459             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
27460             return cimg::type<double>::nan();
27461           default : // Dirichlet
27462             std::memset(ptrd,0,vsiz*sizeof(double));
27463             return cimg::type<double>::nan();
27464           }
27465         std::memset(ptrd,0,vsiz*sizeof(double));
27466         return cimg::type<double>::nan();
27467       }
27468 
27469       static double mp_Jxyz(_cimg_math_parser& mp) {
27470         double *ptrd = &_mp_arg(1) + 1;
27471         const unsigned int
27472           interpolation = (unsigned int)_mp_arg(5),
27473           boundary_conditions = (unsigned int)_mp_arg(6),
27474           vsiz = (unsigned int)mp.opcode[7];
27475         const CImg<T> &img = mp.imgin;
27476         const double
27477           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
27478           x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
27479         const ulongT whd = (ulongT)img._width*img._height*img._depth;
27480         const T *ptrs;
27481         switch (interpolation) {
27482         case 2 : // Cubic interpolation
27483           switch (boundary_conditions) {
27484           case 3 : { // Mirror
27485             const float
27486               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27487               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27488               cx = mx<img.width()?mx:w2 - mx - 1,
27489               cy = my<img.height()?my:h2 - my - 1,
27490               cz = mz<img.depth()?mz:d2 - mz - 1;
27491             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
27492           } break;
27493           case 2 : // Periodic
27494             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
27495             break;
27496           case 1 : // Neumann
27497             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
27498             break;
27499           default : // Dirichlet
27500             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27501           } break;
27502         case 1 : // Linear interpolation
27503           switch (boundary_conditions) {
27504           case 3 : { // Mirror
27505             const float
27506               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
27507               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
27508               cx = mx<img.width()?mx:w2 - mx - 1,
27509               cy = my<img.height()?my:h2 - my - 1,
27510               cz = mz<img.depth()?mz:d2 - mz - 1;
27511             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
27512           } break;
27513           case 2 : // Periodic
27514             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
27515             break;
27516           case 1 : // Neumann
27517             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
27518             break;
27519           default : // Dirichlet
27520             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
27521           } break;
27522         default : // Nearest neighbor interpolation
27523           switch (boundary_conditions) {
27524           case 3 : { // Mirror
27525             const int
27526               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
27527               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
27528               cx = mx<img.width()?mx:w2 - mx - 1,
27529               cy = my<img.height()?my:h2 - my - 1,
27530               cz = mz<img.depth()?mz:d2 - mz - 1;
27531             ptrs = &img(cx,cy,cz);
27532             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27533           } break;
27534           case 2 : { // Periodic
27535             const int
27536               cx = (int)cimg::mod(x,(double)img._width),
27537               cy = (int)cimg::mod(y,(double)img._height),
27538               cz = (int)cimg::mod(z,(double)img._depth);
27539             ptrs = &img(cx,cy,cz);
27540             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27541           } break;
27542           case 1 : { // Neumann
27543             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
27544             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27545           } break;
27546           default : // Dirichlet
27547             if (img.containsXYZC((int)x,(int)y,(int)z)) {
27548               ptrs = &img((int)x,(int)y,(int)z);
27549               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
27550             } else std::memset(ptrd,0,vsiz*sizeof(double));
27551           }
27552         }
27553         return cimg::type<double>::nan();
27554       }
27555 
27556 #undef _mp_arg
27557 
27558     }; // struct _cimg_math_parser {}
27559 
27560 #define _cimg_create_pointwise_functions(name,func,min_size) \
27561     CImg<T>& name() { \
27562       if (is_empty()) return *this; \
27563       cimg_openmp_for(*this,func((typename cimg::superset<T,float>::type)*ptr),min_size); \
27564       return *this; \
27565     } \
27566     CImg<Tfloat> get_##name() const { \
27567       return CImg<Tfloat>(*this,false).name(); \
27568     }
27569 
27570     //! Compute the square value of each pixel value.
27571     /**
27572        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$.
27573        \note
27574        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27575        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27576        \par Example
27577        \code
27578        const CImg<float> img("reference.jpg");
27579        (img,img.get_sqr().normalize(0,255)).display();
27580        \endcode
27581        \image html ref_sqr.jpg
27582     **/
27583     _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
27584 
27585     //! Compute the square root of each pixel value.
27586     /**
27587        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$.
27588        \note
27589        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27590        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27591        \par Example
27592        \code
27593        const CImg<float> img("reference.jpg");
27594        (img,img.get_sqrt().normalize(0,255)).display();
27595        \endcode
27596        \image html ref_sqrt.jpg
27597     **/
27598     _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
27599 
27600     //! Compute the exponential of each pixel value.
27601     /**
27602        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$.
27603        \note
27604        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27605        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27606     **/
27607     _cimg_create_pointwise_functions(exp,std::exp,4096)
27608 
27609     //! Compute the error function of each pixel value.
27610     /**
27611        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its error function.
27612        \note
27613        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27614        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27615     **/
27616     _cimg_create_pointwise_functions(erf,std::erf,4096)
27617 
27618     //! Compute the logarithm of each pixel value.
27619     /**
27620        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
27621        \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
27622        \note
27623        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27624        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27625     **/
27626     _cimg_create_pointwise_functions(log,std::log,262144)
27627 
27628     //! Compute the base-2 logarithm of each pixel value.
27629     /**
27630        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
27631        \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
27632        \note
27633        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27634        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27635     **/
27636     _cimg_create_pointwise_functions(log2,cimg::log2,4096)
27637 
27638     //! Compute the base-10 logarithm of each pixel value.
27639     /**
27640        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
27641        \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
27642        \note
27643        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27644        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27645     **/
27646     _cimg_create_pointwise_functions(log10,std::log10,4096)
27647 
27648     //! Compute the absolute value of each pixel value.
27649     /**
27650        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$.
27651        \note
27652        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27653        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27654     **/
27655     _cimg_create_pointwise_functions(abs,cimg::abs,524288)
27656 
27657     //! Compute the sign of each pixel value.
27658     /**
27659        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
27660        \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
27661        \note
27662        - The sign is set to:
27663          - \c 1 if pixel value is strictly positive.
27664          - \c -1 if pixel value is strictly negative.
27665          - \c 0 if pixel value is equal to \c 0.
27666        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27667        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27668     **/
27669     _cimg_create_pointwise_functions(sign,cimg::sign,32768)
27670 
27671     //! Compute the cosine of each pixel value.
27672     /**
27673        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$.
27674        \note
27675        - Pixel values are regarded as being in \e radian.
27676        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27677        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27678     **/
27679     _cimg_create_pointwise_functions(cos,std::cos,8192)
27680 
27681     //! Compute the sine of each pixel value.
27682     /**
27683        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$.
27684        \note
27685        - Pixel values are regarded as being in \e radian.
27686        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27687        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27688     **/
27689     _cimg_create_pointwise_functions(sin,std::sin,8192)
27690 
27691     //! Compute the sinc of each pixel value.
27692     /**
27693        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
27694        \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
27695        \note
27696        - Pixel values are regarded as being exin \e radian.
27697        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27698        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27699     **/
27700     _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
27701 
27702     //! Compute the tangent of each pixel value.
27703     /**
27704        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$.
27705        \note
27706        - Pixel values are regarded as being exin \e radian.
27707        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27708        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27709     **/
27710     _cimg_create_pointwise_functions(tan,std::tan,2048)
27711 
27712     //! Compute the hyperbolic cosine of each pixel value.
27713     /**
27714        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
27715        \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
27716        \note
27717        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27718        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27719     **/
27720     _cimg_create_pointwise_functions(cosh,std::cosh,2048)
27721 
27722     //! Compute the hyperbolic sine of each pixel value.
27723     /**
27724        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
27725        \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
27726        \note
27727        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27728        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27729     **/
27730     _cimg_create_pointwise_functions(sinh,std::sinh,2048)
27731 
27732     //! Compute the hyperbolic tangent of each pixel value.
27733     /**
27734        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
27735        \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
27736        \note
27737        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27738        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27739     **/
27740     _cimg_create_pointwise_functions(tanh,std::tanh,2048)
27741 
27742     //! Compute the arccosine of each pixel value.
27743     /**
27744        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
27745        \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
27746        \note
27747        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27748        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27749     **/
27750     _cimg_create_pointwise_functions(acos,std::acos,8192)
27751 
27752     //! Compute the arcsine of each pixel value.
27753     /**
27754        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
27755        \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
27756        \note
27757        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27758        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27759     **/
27760     _cimg_create_pointwise_functions(asin,std::asin,8192)
27761 
27762     //! Compute the arctangent of each pixel value.
27763     /**
27764        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
27765        \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
27766        \note
27767        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27768        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27769     **/
27770     _cimg_create_pointwise_functions(atan,std::atan,8192)
27771 
27772     //! Compute the arctangent2 of each pixel value.
27773     /**
27774        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
27775        \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
27776        \param img Image whose pixel values specify the second argument of the \c atan2() function.
27777        \note
27778        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27779        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27780        \par Example
27781        \code
27782        const CImg<float>
27783           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'
27784           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'
27785           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value
27786        (img_x,img_y,img_atan2).display();
27787        \endcode
27788     **/
27789     template<typename t>
27790     CImg<T>& atan2(const CImg<t>& img) {
27791       const ulongT siz = size(), isiz = img.size();
27792       if (siz && isiz) {
27793         if (is_overlapped(img)) return atan2(+img);
27794         T *ptrd = _data, *const ptre = _data + siz;
27795         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27796           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27797             *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27798         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27799       }
27800       return *this;
27801     }
27802 
27803     //! Compute the arctangent2 of each pixel value \newinstance.
27804     template<typename t>
27805     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
27806       return CImg<Tfloat>(*this,false).atan2(img);
27807     }
27808 
27809     //! Compute the hyperbolic arccosine of each pixel value.
27810     /**
27811        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
27812        \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
27813        \note
27814        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27815        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27816     **/
27817     _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
27818 
27819     //! Compute the hyperbolic arcsine of each pixel value.
27820     /**
27821        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
27822        \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
27823        \note
27824        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27825        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27826     **/
27827     _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
27828 
27829     //! Compute the hyperbolic arctangent of each pixel value.
27830     /**
27831        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
27832        \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
27833        \note
27834        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27835        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27836     **/
27837     _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
27838 
27839     //! In-place pointwise multiplication.
27840     /**
27841        Compute the pointwise multiplication between the image instance and the specified input image \c img.
27842        \param img Input image, as the second operand of the multiplication.
27843        \note
27844        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
27845          instead of an addition.
27846        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
27847        \par Example
27848        \code
27849        CImg<float>
27850          img("reference.jpg"),
27851          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
27852        shade.normalize(0,1);
27853        (img,shade,img.get_mul(shade)).display();
27854        \endcode
27855     **/
27856     template<typename t>
27857     CImg<T>& mul(const CImg<t>& img) {
27858       const ulongT siz = size(), isiz = img.size();
27859       if (siz && isiz) {
27860         if (is_overlapped(img)) return mul(+img);
27861         T *ptrd = _data, *const ptre = _data + siz;
27862         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27863           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27864             *ptrd = (T)(*ptrd * *(ptrs++));
27865         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
27866       }
27867       return *this;
27868     }
27869 
27870     //! In-place pointwise multiplication \newinstance.
27871     template<typename t>
27872     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
27873       return CImg<_cimg_Tt>(*this,false).mul(img);
27874     }
27875 
27876     //! In-place pointwise division.
27877     /**
27878        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
27879     **/
27880     template<typename t>
27881     CImg<T>& div(const CImg<t>& img) {
27882       const ulongT siz = size(), isiz = img.size();
27883       if (siz && isiz) {
27884         if (is_overlapped(img)) return div(+img);
27885         T *ptrd = _data, *const ptre = _data + siz;
27886         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27887           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27888             *ptrd = (T)(*ptrd / *(ptrs++));
27889         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
27890       }
27891       return *this;
27892     }
27893 
27894     //! In-place pointwise division \newinstance.
27895     template<typename t>
27896     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
27897       return CImg<_cimg_Tt>(*this,false).div(img);
27898     }
27899 
27900     //! Raise each pixel value to a specified power.
27901     /**
27902        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$.
27903        \param p Exponent value.
27904        \note
27905        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27906        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27907        \par Example
27908        \code
27909        const CImg<float>
27910          img0("reference.jpg"),           // Load reference color image
27911          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8
27912          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5
27913        (img0,img1,img2).display();
27914        \endcode
27915     **/
27916     CImg<T>& pow(const double p) {
27917       if (is_empty()) return *this;
27918       if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; }
27919       if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; }
27920       if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; }
27921       if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; }
27922       if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; }
27923       if (p==0) return fill((T)1);
27924       if (p==0.5) return sqrt();
27925       if (p==1) return *this;
27926       if (p==2) return sqr();
27927       if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; }
27928       if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; }
27929       cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024);
27930       return *this;
27931     }
27932 
27933     //! Raise each pixel value to a specified power \newinstance.
27934     CImg<Tfloat> get_pow(const double p) const {
27935       return CImg<Tfloat>(*this,false).pow(p);
27936     }
27937 
27938     //! Raise each pixel value to a power, specified from an expression.
27939     /**
27940        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
27941     **/
27942     CImg<T>& pow(const char *const expression) {
27943       return pow((+*this)._fill(expression,true,1,0,0,"pow",this));
27944     }
27945 
27946     //! Raise each pixel value to a power, specified from an expression \newinstance.
27947     CImg<Tfloat> get_pow(const char *const expression) const {
27948       return CImg<Tfloat>(*this,false).pow(expression);
27949     }
27950 
27951     //! Raise each pixel value to a power, pointwisely specified from another image.
27952     /**
27953        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
27954     **/
27955     template<typename t>
27956     CImg<T>& pow(const CImg<t>& img) {
27957       const ulongT siz = size(), isiz = img.size();
27958       if (siz && isiz) {
27959         if (is_overlapped(img)) return pow(+img);
27960         T *ptrd = _data, *const ptre = _data + siz;
27961         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27962           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27963             *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27964         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27965       }
27966       return *this;
27967     }
27968 
27969     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
27970     template<typename t>
27971     CImg<Tfloat> get_pow(const CImg<t>& img) const {
27972       return CImg<Tfloat>(*this,false).pow(img);
27973     }
27974 
27975     //! Compute the bitwise left rotation of each pixel value.
27976     /**
27977        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
27978     **/
27979     CImg<T>& rol(const unsigned int n=1) {
27980       if (is_empty()) return *this;
27981       cimg_openmp_for(*this,cimg::rol(*ptr,n),32768);
27982       return *this;
27983     }
27984 
27985     //! Compute the bitwise left rotation of each pixel value \newinstance.
27986     CImg<T> get_rol(const unsigned int n=1) const {
27987       return (+*this).rol(n);
27988     }
27989 
27990     //! Compute the bitwise left rotation of each pixel value.
27991     /**
27992        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
27993     **/
27994     CImg<T>& rol(const char *const expression) {
27995       return rol((+*this)._fill(expression,true,1,0,0,"rol",this));
27996     }
27997 
27998     //! Compute the bitwise left rotation of each pixel value \newinstance.
27999     CImg<T> get_rol(const char *const expression) const {
28000       return (+*this).rol(expression);
28001     }
28002 
28003     //! Compute the bitwise left rotation of each pixel value.
28004     /**
28005        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
28006     **/
28007     template<typename t>
28008     CImg<T>& rol(const CImg<t>& img) {
28009       const ulongT siz = size(), isiz = img.size();
28010       if (siz && isiz) {
28011         if (is_overlapped(img)) return rol(+img);
28012         T *ptrd = _data, *const ptre = _data + siz;
28013         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28014           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28015             *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
28016         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
28017       }
28018       return *this;
28019     }
28020 
28021     //! Compute the bitwise left rotation of each pixel value \newinstance.
28022     template<typename t>
28023     CImg<T> get_rol(const CImg<t>& img) const {
28024       return (+*this).rol(img);
28025     }
28026 
28027     //! Compute the bitwise right rotation of each pixel value.
28028     /**
28029        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
28030     **/
28031     CImg<T>& ror(const unsigned int n=1) {
28032       if (is_empty()) return *this;
28033       cimg_openmp_for(*this,cimg::ror(*ptr,n),32768);
28034       return *this;
28035     }
28036 
28037     //! Compute the bitwise right rotation of each pixel value \newinstance.
28038     CImg<T> get_ror(const unsigned int n=1) const {
28039       return (+*this).ror(n);
28040     }
28041 
28042     //! Compute the bitwise right rotation of each pixel value.
28043     /**
28044        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
28045     **/
28046     CImg<T>& ror(const char *const expression) {
28047       return ror((+*this)._fill(expression,true,1,0,0,"ror",this));
28048     }
28049 
28050     //! Compute the bitwise right rotation of each pixel value \newinstance.
28051     CImg<T> get_ror(const char *const expression) const {
28052       return (+*this).ror(expression);
28053     }
28054 
28055     //! Compute the bitwise right rotation of each pixel value.
28056     /**
28057        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
28058     **/
28059     template<typename t>
28060     CImg<T>& ror(const CImg<t>& img) {
28061       const ulongT siz = size(), isiz = img.size();
28062       if (siz && isiz) {
28063         if (is_overlapped(img)) return ror(+img);
28064         T *ptrd = _data, *const ptre = _data + siz;
28065         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28066           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28067             *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
28068         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
28069       }
28070       return *this;
28071     }
28072 
28073     //! Compute the bitwise right rotation of each pixel value \newinstance.
28074     template<typename t>
28075     CImg<T> get_ror(const CImg<t>& img) const {
28076       return (+*this).ror(img);
28077     }
28078 
28079     //! Pointwise min operator between instance image and a value.
28080     /**
28081        \param val Value used as the reference argument of the min operator.
28082        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28083        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
28084      **/
28085     CImg<T>& min(const T& value) {
28086       if (is_empty()) return *this;
28087       cimg_openmp_for(*this,std::min(*ptr,value),65536);
28088       return *this;
28089     }
28090 
28091     //! Pointwise min operator between instance image and a value \newinstance.
28092     CImg<T> get_min(const T& value) const {
28093       return (+*this).min(value);
28094     }
28095 
28096     //! Pointwise min operator between two images.
28097     /**
28098        \param img Image used as the reference argument of the min operator.
28099        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28100        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
28101      **/
28102     template<typename t>
28103     CImg<T>& min(const CImg<t>& img) {
28104       const ulongT siz = size(), isiz = img.size();
28105       if (siz && isiz) {
28106         if (is_overlapped(img)) return min(+img);
28107         T *ptrd = _data, *const ptre = _data + siz;
28108         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28109           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28110             *ptrd = std::min((T)*(ptrs++),*ptrd);
28111         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
28112       }
28113       return *this;
28114     }
28115 
28116     //! Pointwise min operator between two images \newinstance.
28117     template<typename t>
28118     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
28119       return CImg<_cimg_Tt>(*this,false).min(img);
28120     }
28121 
28122     //! Pointwise min operator between an image and an expression.
28123     /**
28124        \param expression Math formula as a C-string.
28125        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28126        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
28127     **/
28128     CImg<T>& min(const char *const expression) {
28129       return min((+*this)._fill(expression,true,1,0,0,"min",this));
28130     }
28131 
28132     //! Pointwise min operator between an image and an expression \newinstance.
28133     CImg<Tfloat> get_min(const char *const expression) const {
28134       return CImg<Tfloat>(*this,false).min(expression);
28135     }
28136 
28137     //! Pointwise max operator between instance image and a value.
28138     /**
28139        \param val Value used as the reference argument of the max operator.
28140        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28141        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
28142      **/
28143     CImg<T>& max(const T& value) {
28144       if (is_empty()) return *this;
28145       cimg_openmp_for(*this,std::max(*ptr,value),65536);
28146       return *this;
28147     }
28148 
28149     //! Pointwise max operator between instance image and a value \newinstance.
28150     CImg<T> get_max(const T& value) const {
28151       return (+*this).max(value);
28152     }
28153 
28154     //! Pointwise max operator between two images.
28155     /**
28156        \param img Image used as the reference argument of the max operator.
28157        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28158        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
28159      **/
28160     template<typename t>
28161     CImg<T>& max(const CImg<t>& img) {
28162       const ulongT siz = size(), isiz = img.size();
28163       if (siz && isiz) {
28164         if (is_overlapped(img)) return max(+img);
28165         T *ptrd = _data, *const ptre = _data + siz;
28166         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28167           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28168             *ptrd = std::max((T)*(ptrs++),*ptrd);
28169         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
28170       }
28171       return *this;
28172     }
28173 
28174     //! Pointwise max operator between two images \newinstance.
28175     template<typename t>
28176     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
28177       return CImg<_cimg_Tt>(*this,false).max(img);
28178     }
28179 
28180     //! Pointwise max operator between an image and an expression.
28181     /**
28182        \param expression Math formula as a C-string.
28183        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28184        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
28185     **/
28186     CImg<T>& max(const char *const expression) {
28187       return max((+*this)._fill(expression,true,1,0,0,"max",this));
28188     }
28189 
28190     //! Pointwise max operator between an image and an expression \newinstance.
28191     CImg<Tfloat> get_max(const char *const expression) const {
28192       return CImg<Tfloat>(*this,false).max(expression);
28193     }
28194 
28195     //! Pointwise minabs operator between instance image and a value.
28196     /**
28197        \param val Value used as the reference argument of the minabs operator.
28198        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28199        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
28200      **/
28201     CImg<T>& minabs(const T& value) {
28202       if (is_empty()) return *this;
28203       const T absvalue = cimg::abs(value);
28204       cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536);
28205       return *this;
28206     }
28207 
28208     //! Pointwise minabs operator between instance image and a value \newinstance.
28209     CImg<T> get_minabs(const T& value) const {
28210       return (+*this).minabs(value);
28211     }
28212 
28213     //! Pointwise minabs operator between two images.
28214     /**
28215        \param img Image used as the reference argument of the minabs operator.
28216        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28217        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
28218      **/
28219     template<typename t>
28220     CImg<T>& minabs(const CImg<t>& img) {
28221       const ulongT siz = size(), isiz = img.size();
28222       if (siz && isiz) {
28223         if (is_overlapped(img)) return minabs(+img);
28224         T *ptrd = _data, *const ptre = _data + siz;
28225         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28226           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28227             *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
28228         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
28229       }
28230       return *this;
28231     }
28232 
28233     //! Pointwise minabs operator between two images \newinstance.
28234     template<typename t>
28235     CImg<_cimg_Tt> get_minabs(const CImg<t>& img) const {
28236       return CImg<_cimg_Tt>(*this,false).minabs(img);
28237     }
28238 
28239     //! Pointwise minabs operator between an image and an expression.
28240     /**
28241        \param expression Math formula as a C-string.
28242        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28243        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
28244     **/
28245     CImg<T>& minabs(const char *const expression) {
28246       return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this));
28247     }
28248 
28249     //! Pointwise minabs operator between an image and an expression \newinstance.
28250     CImg<Tfloat> get_minabs(const char *const expression) const {
28251       return CImg<Tfloat>(*this,false).minabs(expression);
28252     }
28253 
28254     //! Pointwise maxabs operator between instance image and a value.
28255     /**
28256        \param val Value used as the reference argument of the maxabs operator.
28257        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28258        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
28259      **/
28260     CImg<T>& maxabs(const T& value) {
28261       if (is_empty()) return *this;
28262       const T absvalue = cimg::abs(value);
28263       cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536);
28264       return *this;
28265     }
28266 
28267     //! Pointwise maxabs operator between instance image and a value \newinstance.
28268     CImg<T> get_maxabs(const T& value) const {
28269       return (+*this).maxabs(value);
28270     }
28271 
28272     //! Pointwise maxabs operator between two images.
28273     /**
28274        \param img Image used as the reference argument of the maxabs operator.
28275        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28276        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
28277      **/
28278     template<typename t>
28279     CImg<T>& maxabs(const CImg<t>& img) {
28280       const ulongT siz = size(), isiz = img.size();
28281       if (siz && isiz) {
28282         if (is_overlapped(img)) return maxabs(+img);
28283         T *ptrd = _data, *const ptre = _data + siz;
28284         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
28285           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
28286             *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
28287         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
28288       }
28289       return *this;
28290     }
28291 
28292     //! Pointwise maxabs operator between two images \newinstance.
28293     template<typename t>
28294     CImg<_cimg_Tt> get_maxabs(const CImg<t>& img) const {
28295       return CImg<_cimg_Tt>(*this,false).maxabs(img);
28296     }
28297 
28298     //! Pointwise maxabs operator between an image and an expression.
28299     /**
28300        \param expression Math formula as a C-string.
28301        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
28302        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
28303     **/
28304     CImg<T>& maxabs(const char *const expression) {
28305       return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this));
28306     }
28307 
28308     //! Pointwise maxabs operator between an image and an expression \newinstance.
28309     CImg<Tfloat> get_maxabs(const char *const expression) const {
28310       return CImg<Tfloat>(*this,false).maxabs(expression);
28311     }
28312 
28313     //! Return a reference to the minimum pixel value.
28314     /**
28315      **/
28316     T& min() {
28317       if (is_empty())
28318         throw CImgInstanceException(_cimg_instance
28319                                     "min(): Empty instance.",
28320                                     cimg_instance);
28321       T *ptr_min = _data;
28322       T min_value = *ptr_min;
28323       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
28324       return *ptr_min;
28325     }
28326 
28327     //! Return a reference to the minimum pixel value \const.
28328     const T& min() const {
28329       if (is_empty())
28330         throw CImgInstanceException(_cimg_instance
28331                                     "min(): Empty instance.",
28332                                     cimg_instance);
28333       const T *ptr_min = _data;
28334       T min_value = *ptr_min;
28335       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
28336       return *ptr_min;
28337     }
28338 
28339     //! Return a reference to the minimum pixel value in absolute value.
28340     /**
28341      **/
28342     T& minabs() {
28343       if (is_empty())
28344         throw CImgInstanceException(_cimg_instance
28345                                     "minabs(): Empty instance.",
28346                                     cimg_instance);
28347       T *ptr_minabs = _data;
28348       T minabs_value = *ptr_minabs;
28349       cimg_for(*this,ptrs,T) {
28350         const T ma = cimg::abs(*ptrs);
28351         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
28352       }
28353       return *ptr_minabs;
28354     }
28355 
28356     //! Return a reference to the minimum pixel value in absolute value \const.
28357     const T& minabs() const {
28358       if (is_empty())
28359         throw CImgInstanceException(_cimg_instance
28360                                     "minabs(): Empty instance.",
28361                                     cimg_instance);
28362       const T *ptr_minabs = _data;
28363       T minabs_value = *ptr_minabs;
28364       cimg_for(*this,ptrs,T) {
28365         const T ma = cimg::abs(*ptrs);
28366         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
28367       }
28368       return *ptr_minabs;
28369     }
28370 
28371     //! Return a reference to the maximum pixel value.
28372     /**
28373      **/
28374     T& max() {
28375       if (is_empty())
28376         throw CImgInstanceException(_cimg_instance
28377                                     "max(): Empty instance.",
28378                                     cimg_instance);
28379       T *ptr_max = _data;
28380       T max_value = *ptr_max;
28381       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
28382       return *ptr_max;
28383     }
28384 
28385     //! Return a reference to the maximum pixel value \const.
28386     const T& max() const {
28387       if (is_empty())
28388         throw CImgInstanceException(_cimg_instance
28389                                     "max(): Empty instance.",
28390                                     cimg_instance);
28391       const T *ptr_max = _data;
28392       T max_value = *ptr_max;
28393       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
28394       return *ptr_max;
28395     }
28396 
28397     //! Return a reference to the maximum pixel value in absolute value.
28398     /**
28399      **/
28400     T& maxabs() {
28401       if (is_empty())
28402         throw CImgInstanceException(_cimg_instance
28403                                     "maxabs(): Empty instance.",
28404                                     cimg_instance);
28405       T *ptr_maxabs = _data;
28406       T maxabs_value = *ptr_maxabs;
28407       cimg_for(*this,ptrs,T) {
28408         const T ma = cimg::abs(*ptrs);
28409         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
28410       }
28411       return *ptr_maxabs;
28412     }
28413 
28414     //! Return a reference to the maximum pixel value in absolute value \const.
28415     const T& maxabs() const {
28416       if (is_empty())
28417         throw CImgInstanceException(_cimg_instance
28418                                     "maxabs(): Empty instance.",
28419                                     cimg_instance);
28420       const T *ptr_maxabs = _data;
28421       T maxabs_value = *ptr_maxabs;
28422       cimg_for(*this,ptrs,T) {
28423         const T ma = cimg::abs(*ptrs);
28424         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
28425       }
28426       return *ptr_maxabs;
28427     }
28428 
28429     //! Return a reference to the minimum pixel value as well as the maximum pixel value.
28430     /**
28431        \param[out] max_val Maximum pixel value.
28432     **/
28433     template<typename t>
28434     T& min_max(t& max_val) {
28435       if (is_empty())
28436         throw CImgInstanceException(_cimg_instance
28437                                     "min_max(): Empty instance.",
28438                                     cimg_instance);
28439       T *ptr_min = _data;
28440       T min_value = *ptr_min, max_value = min_value;
28441       cimg_for(*this,ptrs,T) {
28442         const T val = *ptrs;
28443         if (val<min_value) { min_value = val; ptr_min = ptrs; }
28444         if (val>max_value) max_value = val;
28445       }
28446       max_val = (t)max_value;
28447       return *ptr_min;
28448     }
28449 
28450     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
28451     template<typename t>
28452     const T& min_max(t& max_val) const {
28453       if (is_empty())
28454         throw CImgInstanceException(_cimg_instance
28455                                     "min_max(): Empty instance.",
28456                                     cimg_instance);
28457       const T *ptr_min = _data;
28458       T min_value = *ptr_min, max_value = min_value;
28459       cimg_for(*this,ptrs,T) {
28460         const T val = *ptrs;
28461         if (val<min_value) { min_value = val; ptr_min = ptrs; }
28462         if (val>max_value) max_value = val;
28463       }
28464       max_val = (t)max_value;
28465       return *ptr_min;
28466     }
28467 
28468     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
28469     /**
28470        \param[out] min_val Minimum pixel value.
28471     **/
28472     template<typename t>
28473     T& max_min(t& min_val) {
28474       if (is_empty())
28475         throw CImgInstanceException(_cimg_instance
28476                                     "max_min(): Empty instance.",
28477                                     cimg_instance);
28478       T *ptr_max = _data;
28479       T max_value = *ptr_max, min_value = max_value;
28480       cimg_for(*this,ptrs,T) {
28481         const T val = *ptrs;
28482         if (val>max_value) { max_value = val; ptr_max = ptrs; }
28483         if (val<min_value) min_value = val;
28484       }
28485       min_val = (t)min_value;
28486       return *ptr_max;
28487     }
28488 
28489     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
28490     template<typename t>
28491     const T& max_min(t& min_val) const {
28492       if (is_empty())
28493         throw CImgInstanceException(_cimg_instance
28494                                     "max_min(): Empty instance.",
28495                                     cimg_instance);
28496       const T *ptr_max = _data;
28497       T max_value = *ptr_max, min_value = max_value;
28498       cimg_for(*this,ptrs,T) {
28499         const T val = *ptrs;
28500         if (val>max_value) { max_value = val; ptr_max = ptrs; }
28501         if (val<min_value) min_value = val;
28502       }
28503       min_val = (t)min_value;
28504       return *ptr_max;
28505     }
28506 
28507     //! Return the kth smallest pixel value.
28508     /**
28509        \param k Rank of the smallest element searched.
28510     **/
28511     T kth_smallest(const ulongT k) const {
28512       if (is_empty())
28513         throw CImgInstanceException(_cimg_instance
28514                                     "kth_smallest(): Empty instance.",
28515                                     cimg_instance);
28516       if (k>=size()) return max();
28517       CImg<T> arr(*this,false);
28518       ulongT l = 0, ir = size() - 1;
28519       for ( ; ; ) {
28520         if (ir<=l + 1) {
28521           if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
28522           return arr[k];
28523         } else {
28524           const ulongT mid = (l + ir)>>1;
28525           cimg::swap(arr[mid],arr[l + 1]);
28526           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
28527           if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
28528           if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
28529           ulongT i = l + 1, j = ir;
28530           const T pivot = arr[l + 1];
28531           for ( ; ; ) {
28532             do ++i; while (arr[i]<pivot);
28533             do --j; while (arr[j]>pivot);
28534             if (j<i) break;
28535             cimg::swap(arr[i],arr[j]);
28536           }
28537           arr[l + 1] = arr[j];
28538           arr[j] = pivot;
28539           if (j>=k) ir = j - 1;
28540           if (j<=k) l = i;
28541         }
28542       }
28543     }
28544 
28545     //! Return the median pixel value.
28546     /**
28547      **/
28548     T median() const {
28549       if (is_empty())
28550         throw CImgInstanceException(_cimg_instance
28551                                     "median(): Empty instance.",
28552                                     cimg_instance);
28553       const ulongT s = size();
28554       switch (s) {
28555       case 1 : return _data[0];
28556       case 2 : return cimg::median(_data[0],_data[1]);
28557       case 3 : return cimg::median(_data[0],_data[1],_data[2]);
28558       case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
28559       case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
28560       case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
28561       case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
28562                                     _data[9],_data[10],_data[11],_data[12]);
28563       }
28564       const T res = kth_smallest(s>>1);
28565       return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
28566     }
28567 
28568     //! Return the product of all the pixel values.
28569     /**
28570      **/
28571     double product() const {
28572       if (is_empty()) return 0;
28573       double res = 1;
28574       cimg_for(*this,ptrs,T) res*=(double)*ptrs;
28575       return res;
28576     }
28577 
28578     //! Return the sum of all the pixel values.
28579     /**
28580      **/
28581     double sum() const {
28582       double res = 0;
28583       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
28584       return res;
28585     }
28586 
28587     //! Return the average pixel value.
28588     /**
28589      **/
28590     double mean() const {
28591       double res = 0;
28592       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
28593       return res/size();
28594     }
28595 
28596     //! Return the variance of the pixel values.
28597     /**
28598        \param variance_method Method used to estimate the variance. Can be:
28599        - \c 0: Second moment, computed as
28600        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
28601        1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
28602        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
28603        - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
28604        - \c 2: Least median of squares.
28605        - \c 3: Least trimmed of squares.
28606     **/
28607     double variance(const unsigned int variance_method=1) const {
28608       double foo;
28609       return variance_mean(variance_method,foo);
28610     }
28611 
28612     //! Return the variance as well as the average of the pixel values.
28613     /**
28614        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
28615        \param[out] mean Average pixel value.
28616     **/
28617     template<typename t>
28618     double variance_mean(const unsigned int variance_method, t& mean) const {
28619       if (is_empty())
28620         throw CImgInstanceException(_cimg_instance
28621                                     "variance_mean(): Empty instance.",
28622                                     cimg_instance);
28623 
28624       double variance = 0, average = 0;
28625       const ulongT siz = size();
28626       switch (variance_method) {
28627       case 0 : { // Least mean square (standard definition)
28628         double S = 0, S2 = 0;
28629         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
28630         variance = (S2 - S*S/siz)/siz;
28631         average = S;
28632       } break;
28633       case 1 : { // Least mean square (robust definition)
28634         double S = 0, S2 = 0;
28635         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
28636         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
28637         average = S;
28638       } break;
28639       case 2 : { // Least Median of Squares (MAD)
28640         CImg<Tfloat> buf(*this,false);
28641         buf.sort();
28642         const ulongT siz2 = siz>>1;
28643         const double med_i = (double)buf[siz2];
28644         cimg_for(buf,ptrs,Tfloat) {
28645           const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
28646         }
28647         buf.sort();
28648         const double sig = (double)(1.4828*buf[siz2]);
28649         variance = sig*sig;
28650       } break;
28651       default : { // Least trimmed of Squares
28652         CImg<Tfloat> buf(*this,false);
28653         const ulongT siz2 = siz>>1;
28654         cimg_for(buf,ptrs,Tfloat) {
28655           const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
28656         }
28657         buf.sort();
28658         double a = 0;
28659         const Tfloat *ptrs = buf._data;
28660         for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
28661         const double sig = (double)(2.6477*std::sqrt(a/siz2));
28662         variance = sig*sig;
28663       }
28664       }
28665       mean = (t)(average/siz);
28666       return variance>0?variance:0;
28667     }
28668 
28669     //! Return estimated variance of the noise.
28670     /**
28671        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
28672        \note Because of structures such as edges in images it is
28673        recommended to use a robust variance estimation. The variance of the
28674        noise is estimated by computing the variance of the Laplacian \f$(\Delta
28675        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
28676        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
28677     **/
28678     double variance_noise(const unsigned int variance_method=2) const {
28679       if (is_empty())
28680         throw CImgInstanceException(_cimg_instance
28681                                     "variance_noise(): Empty instance.",
28682                                     cimg_instance);
28683 
28684       const ulongT siz = size();
28685       if (!siz || !_data) return 0;
28686       if (variance_method>1) { // Compute a scaled version of the Laplacian
28687         CImg<Tdouble> tmp(*this,false);
28688         if (_depth==1) {
28689           const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed
28690           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 &&
28691                                                          _spectrum>=2))
28692           cimg_forC(*this,c) {
28693             CImg_3x3(I,T);
28694             cimg_for3x3(*this,x,y,0,c,I,T) {
28695               tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
28696                                  (double)Icp - 4*(double)Icc);
28697             }
28698           }
28699         } else {
28700           const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed
28701           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 &&
28702                                                          _spectrum>=2))
28703           cimg_forC(*this,c) {
28704             CImg_3x3x3(I,T);
28705             cimg_for3x3x3(*this,x,y,z,c,I,T) {
28706               tmp(x,y,z,c) = cste*(
28707                                    (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
28708                                    (double)Iccn + (double)Iccp - 6*(double)Iccc);
28709             }
28710           }
28711         }
28712         return tmp.variance(variance_method);
28713       }
28714 
28715       // Version that doesn't need intermediate images.
28716       double variance = 0, S = 0, S2 = 0;
28717       if (_depth==1) {
28718         const double cste = 1./std::sqrt(20.);
28719         CImg_3x3(I,T);
28720         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
28721           const double val = cste*((double)Inc + (double)Ipc +
28722                                    (double)Icn + (double)Icp - 4*(double)Icc);
28723           S+=val; S2+=val*val;
28724         }
28725       } else {
28726         const double cste = 1./std::sqrt(42.);
28727         CImg_3x3x3(I,T);
28728         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
28729           const double val = cste *
28730             ((double)Incc + (double)Ipcc + (double)Icnc +
28731              (double)Icpc +
28732              (double)Iccn + (double)Iccp - 6*(double)Iccc);
28733           S+=val; S2+=val*val;
28734         }
28735       }
28736       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
28737       else variance = (S2 - S*S/siz)/siz;
28738       return variance>0?variance:0;
28739     }
28740 
28741     //! Compute the MSE (Mean-Squared Error) between two images.
28742     /**
28743        \param img Image used as the second argument of the MSE operator.
28744     **/
28745     template<typename t>
28746     double MSE(const CImg<t>& img) const {
28747       if (img.size()!=size())
28748         throw CImgArgumentException(_cimg_instance
28749                                     "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
28750                                     cimg_instance,
28751                                     img._width,img._height,img._depth,img._spectrum,img._data);
28752       double vMSE = 0;
28753       const t* ptr2 = img._data;
28754       cimg_for(*this,ptr1,T) {
28755         const double diff = (double)*ptr1 - (double)*(ptr2++);
28756         vMSE+=diff*diff;
28757       }
28758       const ulongT siz = img.size();
28759       if (siz) vMSE/=siz;
28760       return vMSE;
28761     }
28762 
28763     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
28764     /**
28765        \param img Image used as the second argument of the PSNR operator.
28766        \param max_value Maximum theoretical value of the signal.
28767      **/
28768     template<typename t>
28769     double PSNR(const CImg<t>& img, const double max_value=255) const {
28770       const double vMSE = (double)std::sqrt(MSE(img));
28771       return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
28772     }
28773 
28774     //! Evaluate math formula.
28775     /**
28776        \param expression Math formula, as a C-string.
28777        \param x Value of the pre-defined variable \c x.
28778        \param y Value of the pre-defined variable \c y.
28779        \param z Value of the pre-defined variable \c z.
28780        \param c Value of the pre-defined variable \c c.
28781        \param list_inputs A list of input images attached to the specified math formula.
28782        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28783     **/
28784     double eval(const char *const expression,
28785                 const double x=0, const double y=0, const double z=0, const double c=0,
28786                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28787       return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
28788     }
28789 
28790     //! Evaluate math formula \const.
28791     double eval(const char *const expression,
28792                 const double x=0, const double y=0, const double z=0, const double c=0,
28793                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28794       return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
28795     }
28796 
28797     // Fast function to pre-evaluate common expressions.
28798     // (return 'true' in case of success, and set value of 'res').
28799     template<typename t>
28800     bool __eval(const char *const expression, t &res) const {
28801       if (!expression || !*expression) { res = (t)0; return true; }
28802       const char c = *expression;
28803       bool is_success = false;
28804       char sep, end;
28805       double val,val2;
28806       int err;
28807       if ((c>='0' && c<='9') || c=='.') { // Possible value
28808         if (!expression[1]) { // Single digit
28809           res = (t)(c - '0');
28810           is_success = true;
28811         } else if ((err = std::sscanf(expression,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // Single value
28812           res = (t)val;
28813           is_success = true;
28814         } else if (err==3) { // Value1 Operator Value2
28815           switch (sep) {
28816           case '+' : res = (t)(val + val2); is_success = true; break;
28817           case '-' : res = (t)(val - val2); is_success = true; break;
28818           case '*' : res = (t)(val*val2); is_success = true; break;
28819           case '/' : res = (t)(val/val2); is_success = true; break;
28820           case '%' : res = (t)cimg::mod(val,val2); is_success = true; break;
28821           case '&' : res = (t)((long)val & (long)val2); is_success = true; break;
28822           case '|' : res = (t)((long)val | (long)val2); is_success = true; break;
28823           case '>' : res = (t)(val>val2); is_success = true; break;
28824           case '<' : res = (t)(val<val2); is_success = true; break;
28825           case ';' : res = (t)val2; is_success = true; break;
28826           case '^' : res = (t)std::pow(val,val2); is_success = true; break;
28827           }
28828         }
28829       } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value
28830                  (((sep = expression[1])>='0' && sep<='9') || sep=='.')) {
28831         if (!expression[2]) { // [+-!] + Single digit
28832           const int ival = sep - '0';
28833           res = (t)(c=='+'?ival:c=='-'?-ival:!ival);
28834           is_success = true;
28835         } else if ((err = std::sscanf(expression + 1,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // [+-!] Single value
28836           res = (t)(c=='+'?val:c=='-'?-val:(double)!val);
28837           is_success = true;
28838         } else if (err==3) { // [+-!] Value1 Operator Value2
28839           const double val1 = c=='+'?val:c=='-'?-val:(double)!val;
28840           switch (sep) {
28841           case '+' : res = (t)(val1 + val2); is_success = true; break;
28842           case '-' : res = (t)(val1 - val2); is_success = true; break;
28843           case '*' : res = (t)(val1*val2); is_success = true; break;
28844           case '/' : res = (t)(val1/val2); is_success = true; break;
28845           case '%' : res = (t)cimg::mod(val1,val2); is_success = true; break;
28846           case '&' : res = (t)((long)val1 & (long)val2); is_success = true; break;
28847           case '|' : res = (t)((long)val1 | (long)val2); is_success = true; break;
28848           case '>' : res = (t)(val1>val2); is_success = true; break;
28849           case '<' : res = (t)(val1<val2); is_success = true; break;
28850           case ';' : res = (t)val2; is_success = true; break;
28851           case '^' : val = std::pow(val,val2); res = (t)(c=='+'?val:c=='-'?-val:!val); is_success = true; break;
28852           }
28853         }
28854       } else if (!expression[1]) switch (*expression) { // Other common single-char expressions
28855         case 'w' : res = (t)_width; is_success = true; break;
28856         case 'h' : res = (t)_height; is_success = true; break;
28857         case 'd' : res = (t)_depth; is_success = true; break;
28858         case 's' : res = (t)_spectrum; is_success = true; break;
28859         case 'r' : res = (t)_is_shared; is_success = true; break;
28860         }
28861       return is_success;
28862     }
28863 
28864     double _eval(CImg<T> *const img_output, const char *const expression,
28865                  const double x, const double y, const double z, const double c,
28866                  const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28867       if (!expression || !*expression) return 0;
28868       double _val = 0;
28869       if (__eval(expression,_val)) return _val;
28870       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28871                                          *expression=='*' || *expression==':'),"eval",
28872                            *this,img_output,list_inputs,list_outputs,false);
28873       mp.begin_t();
28874       const double val = mp(x,y,z,c);
28875       mp.end_t();
28876       mp.end();
28877       return val;
28878     }
28879 
28880     //! Evaluate math formula.
28881     /**
28882        \param[out] output Contains values of output vector returned by the evaluated expression
28883          (or is empty if the returned type is scalar).
28884        \param expression Math formula, as a C-string.
28885        \param x Value of the pre-defined variable \c x.
28886        \param y Value of the pre-defined variable \c y.
28887        \param z Value of the pre-defined variable \c z.
28888        \param c Value of the pre-defined variable \c c.
28889        \param list_inputs A list of input images attached to the specified math formula.
28890        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28891     **/
28892     template<typename t>
28893     void eval(CImg<t> &output, const char *const expression,
28894               const double x=0, const double y=0, const double z=0, const double c=0,
28895               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28896       _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
28897     }
28898 
28899     //! Evaluate math formula \const.
28900     template<typename t>
28901     void eval(CImg<t>& output, const char *const expression,
28902               const double x=0, const double y=0, const double z=0, const double c=0,
28903               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28904       _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
28905     }
28906 
28907     template<typename t>
28908     void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
28909                const double x, const double y, const double z, const double c,
28910                const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28911       if (!expression || !*expression) { output.assign(1); *output = 0; return; }
28912       double _val = 0;
28913       if (__eval(expression,_val)) { output.assign(1); *output = _val; return; }
28914       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28915                                          *expression=='*' || *expression==':'),"eval",
28916                            *this,img_output,list_inputs,list_outputs,false);
28917       output.assign(1,std::max(1U,mp.result_dim));
28918       mp.begin_t();
28919       mp(x,y,z,c,output._data);
28920       mp.end_t();
28921       mp.end();
28922     }
28923 
28924     //! Evaluate math formula on a set of variables.
28925     /**
28926        \param expression Math formula, as a C-string.
28927        \param xyzc Set of values (x,y,z,c) used for the evaluation.
28928        \param list_inputs A list of input images attached to the specified math formula.
28929        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28930     **/
28931     template<typename t>
28932     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28933                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28934       return _eval(this,expression,xyzc,list_inputs,list_outputs);
28935     }
28936 
28937     //! Evaluate math formula on a set of variables \const.
28938     template<typename t>
28939     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28940                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28941       return _eval(0,expression,xyzc,list_inputs,list_outputs);
28942     }
28943 
28944     template<typename t>
28945     CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
28946                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28947       CImg<doubleT> res(1,xyzc.size()/4);
28948       if (!expression || !*expression) return res.fill(0);
28949       _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
28950 
28951 #if cimg_use_openmp!=0
28952       cimg_pragma_openmp(parallel if (res._height>=512))
28953       {
28954         _cimg_math_parser
28955           *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
28956           &lmp = *_mp;
28957         cimg_pragma_openmp(barrier)
28958         lmp.begin_t();
28959         cimg_pragma_openmp(for)
28960           for (int i = 0; i<res.height(); ++i) {
28961             const unsigned int i4 = 4*i;
28962             const double
28963               x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
28964               z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
28965             res[i] = lmp(x,y,z,c);
28966           }
28967         lmp.end_t();
28968         cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
28969         if (&lmp!=&mp) delete &lmp;
28970       }
28971 #else
28972       mp.begin_t();
28973       const t *ps = xyzc._data;
28974       cimg_for(res,pd,double) {
28975         const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
28976         *pd = mp(x,y,z,c);
28977       }
28978       mp.end_t();
28979 #endif
28980       mp.end();
28981       return res;
28982     }
28983 
28984     //! Compute statistics vector from the pixel values.
28985     /**
28986        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
28987        \return Statistics vector as
28988          <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
28989     **/
28990     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
28991       if (is_empty()) return CImg<doubleT>();
28992       const ulongT siz = size();
28993       const longT off_end = (longT)siz;
28994       double S = 0, S2 = 0, P = 1;
28995       longT offm = 0, offM = 0;
28996       T m = *_data, M = m;
28997 
28998       cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) {
28999         longT loffm = 0, loffM = 0;
29000         T lm = *_data, lM = lm;
29001         cimg_pragma_openmp(for)
29002         for (longT off = 0; off<off_end; ++off) {
29003           const T val = _data[off];
29004           const double _val = (double)val;
29005           if (val<lm) { lm = val; loffm = off; }
29006           if (val>lM) { lM = val; loffM = off; }
29007           S+=_val;
29008           S2+=_val*_val;
29009           P*=_val;
29010         }
29011         cimg_pragma_openmp(critical(get_stats)) {
29012           if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
29013           if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
29014         }
29015       }
29016 
29017       const double
29018         mean_value = S/siz,
29019         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
29020         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
29021          variance(variance_method)),
29022         variance_value = _variance_value>0?_variance_value:0;
29023       int
29024         xm = 0, ym = 0, zm = 0, cm = 0,
29025         xM = 0, yM = 0, zM = 0, cM = 0;
29026       contains(_data[offm],xm,ym,zm,cm);
29027       contains(_data[offM],xM,yM,zM,cM);
29028       return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
29029                                       (double)xm,(double)ym,(double)zm,(double)cm,
29030                                       (double)xM,(double)yM,(double)zM,(double)cM,
29031                                       S,P);
29032     }
29033 
29034     //! Compute statistics vector from the pixel values \inplace.
29035     CImg<T>& stats(const unsigned int variance_method=1) {
29036       return get_stats(variance_method).move_to(*this);
29037     }
29038 
29039     //@}
29040     //-------------------------------------
29041     //
29042     //! \name Vector / Matrix Operations
29043     //@{
29044     //-------------------------------------
29045 
29046     //! Compute norm of the image, viewed as a matrix.
29047     /**
29048        \param magnitude_type Norm type. Can be:
29049        - \c -1: Linf-norm
29050        - \c 0: L0-norm
29051        - \c 1: L1-norm
29052        - \c 2: L2-norm
29053     **/
29054     double magnitude(const int magnitude_type=2) const {
29055       if (is_empty())
29056         throw CImgInstanceException(_cimg_instance
29057                                     "magnitude(): Empty instance.",
29058                                     cimg_instance);
29059       const ulongT siz = size();
29060       double res = 0;
29061       switch (magnitude_type) {
29062       case -1 : {
29063         cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
29064       } break;
29065       case 1 : {
29066         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
29067         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
29068       } break;
29069       default : {
29070         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
29071         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
29072         res = (double)std::sqrt(res);
29073       }
29074       }
29075       return res;
29076     }
29077 
29078     //! Compute the trace of the image, viewed as a matrix.
29079     /**
29080      **/
29081     double trace() const {
29082       if (is_empty())
29083         throw CImgInstanceException(_cimg_instance
29084                                     "trace(): Empty instance.",
29085                                     cimg_instance);
29086       double res = 0;
29087       cimg_forX(*this,k) res+=(double)(*this)(k,k);
29088       return res;
29089     }
29090 
29091     //! Compute the determinant of the image, viewed as a matrix.
29092     /**
29093      **/
29094     double det() const {
29095       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
29096         throw CImgInstanceException(_cimg_instance
29097                                     "det(): Instance is not a square matrix.",
29098                                     cimg_instance);
29099 
29100       switch (_width) {
29101       case 1 : return (double)((*this)(0,0));
29102       case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
29103       case 3 : {
29104         const double
29105           a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
29106           b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
29107           c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
29108         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
29109       }
29110       default : {
29111         CImg<Tfloat> lu(*this,false);
29112         CImg<uintT> indx;
29113         bool d;
29114         lu._LU(indx,d);
29115         double res = d?(double)1:(double)-1;
29116         cimg_forX(lu,i) res*=lu(i,i);
29117         return res;
29118       }
29119       }
29120     }
29121 
29122     //! Compute the dot product between instance and argument, viewed as matrices.
29123     /**
29124        \param img Image used as a second argument of the dot product.
29125     **/
29126     template<typename t>
29127     double dot(const CImg<t>& img) const {
29128       const ulongT nb = std::min(size(),img.size());
29129       double res = 0;
29130       cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192))
29131       for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off];
29132       return res;
29133     }
29134 
29135     //! Get vector-valued pixel located at specified position.
29136     /**
29137        \param x X-coordinate of the pixel value.
29138        \param y Y-coordinate of the pixel value.
29139        \param z Z-coordinate of the pixel value.
29140     **/
29141     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
29142       CImg<T> res;
29143       if (res._height!=_spectrum) res.assign(1,_spectrum);
29144       const ulongT whd = (ulongT)_width*_height*_depth;
29145       const T *ptrs = data(x,y,z);
29146       T *ptrd = res._data;
29147       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
29148       return res;
29149     }
29150 
29151     //! Get (square) matrix-valued pixel located at specified position.
29152     /**
29153        \param x X-coordinate of the pixel value.
29154        \param y Y-coordinate of the pixel value.
29155        \param z Z-coordinate of the pixel value.
29156        \note - The spectrum() of the image must be a square.
29157      **/
29158     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
29159       const int n = (int)cimg::round(std::sqrt((double)_spectrum));
29160       const T *ptrs = data(x,y,z,0);
29161       const ulongT whd = (ulongT)_width*_height*_depth;
29162       CImg<T> res(n,n);
29163       T *ptrd = res._data;
29164       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
29165       return res;
29166     }
29167 
29168     //! Get tensor-valued pixel located at specified position.
29169     /**
29170        \param x X-coordinate of the pixel value.
29171        \param y Y-coordinate of the pixel value.
29172        \param z Z-coordinate of the pixel value.
29173     **/
29174     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
29175       const T *ptrs = data(x,y,z,0);
29176       const ulongT whd = (ulongT)_width*_height*_depth;
29177       if (_spectrum==6)
29178         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
29179       if (_spectrum==3)
29180         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
29181       return tensor(*ptrs);
29182     }
29183 
29184     //! Set vector-valued pixel at specified position.
29185     /**
29186        \param vec Vector to put on the instance image.
29187        \param x X-coordinate of the pixel value.
29188        \param y Y-coordinate of the pixel value.
29189        \param z Z-coordinate of the pixel value.
29190     **/
29191     template<typename t>
29192     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
29193       if (x<_width && y<_height && z<_depth) {
29194         const t *ptrs = vec._data;
29195         const ulongT whd = (ulongT)_width*_height*_depth;
29196         T *ptrd = data(x,y,z);
29197         for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
29198           *ptrd = (T)*(ptrs++); ptrd+=whd;
29199         }
29200       }
29201       return *this;
29202     }
29203 
29204     //! Set (square) matrix-valued pixel at specified position.
29205     /**
29206        \param mat Matrix to put on the instance image.
29207        \param x X-coordinate of the pixel value.
29208        \param y Y-coordinate of the pixel value.
29209        \param z Z-coordinate of the pixel value.
29210     **/
29211     template<typename t>
29212     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
29213       return set_vector_at(mat,x,y,z);
29214     }
29215 
29216     //! Set tensor-valued pixel at specified position.
29217     /**
29218        \param ten Tensor to put on the instance image.
29219        \param x X-coordinate of the pixel value.
29220        \param y Y-coordinate of the pixel value.
29221        \param z Z-coordinate of the pixel value.
29222     **/
29223     template<typename t>
29224     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
29225       T *ptrd = data(x,y,z,0);
29226       const ulongT siz = (ulongT)_width*_height*_depth;
29227       if (ten._height==2) {
29228         *ptrd = (T)ten[0]; ptrd+=siz;
29229         *ptrd = (T)ten[1]; ptrd+=siz;
29230         *ptrd = (T)ten[3];
29231       }
29232       else {
29233         *ptrd = (T)ten[0]; ptrd+=siz;
29234         *ptrd = (T)ten[1]; ptrd+=siz;
29235         *ptrd = (T)ten[2]; ptrd+=siz;
29236         *ptrd = (T)ten[4]; ptrd+=siz;
29237         *ptrd = (T)ten[5]; ptrd+=siz;
29238         *ptrd = (T)ten[8];
29239       }
29240       return *this;
29241     }
29242 
29243     //! Resize image to become a diagonal matrix.
29244     /**
29245        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
29246     **/
29247     CImg<T>& diagonal() {
29248       return get_diagonal().move_to(*this);
29249     }
29250 
29251     //! Resize image to become a diagonal matrix \newinstance.
29252     CImg<T> get_diagonal() const {
29253       if (is_empty()) return *this;
29254       const unsigned int siz = (unsigned int)size();
29255       CImg<T> res(siz,siz,1,1,0);
29256       cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
29257       return res;
29258     }
29259 
29260     //! Replace the image by an identity matrix.
29261     /**
29262        \note If the instance image is not square, it is resized to a square matrix using its maximum
29263        dimension as a reference.
29264     **/
29265     CImg<T>& identity_matrix() {
29266       return identity_matrix(std::max(_width,_height)).move_to(*this);
29267     }
29268 
29269     //! Replace the image by an identity matrix \newinstance.
29270     CImg<T> get_identity_matrix() const {
29271       return identity_matrix(std::max(_width,_height));
29272     }
29273 
29274     //! Fill image with a linear sequence of values.
29275     /**
29276        \param a0 Starting value of the sequence.
29277        \param a1 Ending value of the sequence.
29278     **/
29279     CImg<T>& sequence(const T& a0, const T& a1) {
29280       if (is_empty()) return *this;
29281       const ulongT siz = size() - 1;
29282       T* ptr = _data;
29283       if (siz) {
29284         const double delta = (double)a1 - (double)a0;
29285         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
29286       } else *ptr = a0;
29287       return *this;
29288     }
29289 
29290     //! Fill image with a linear sequence of values \newinstance.
29291     CImg<T> get_sequence(const T& a0, const T& a1) const {
29292       return (+*this).sequence(a0,a1);
29293     }
29294 
29295     //! Transpose the image, viewed as a matrix.
29296     /**
29297        \note Equivalent to \code permute_axes("yxzc"); \endcode.
29298     **/
29299     CImg<T>& transpose() {
29300       if (_width==1) { _width = _height; _height = 1; return *this; }
29301       if (_height==1) { _height = _width; _width = 1; return *this; }
29302       if (_width==_height) {
29303         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));
29304         return *this;
29305       }
29306       return get_transpose().move_to(*this);
29307     }
29308 
29309     //! Transpose the image, viewed as a matrix \newinstance.
29310     CImg<T> get_transpose() const {
29311       return get_permute_axes("yxzc");
29312     }
29313 
29314     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors.
29315     /**
29316        \param img Image used as the second argument of the cross product.
29317        \note The first argument of the cross product is \c *this.
29318      **/
29319     template<typename t>
29320     CImg<T>& cross(const CImg<t>& img) {
29321       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
29322         throw CImgInstanceException(_cimg_instance
29323                                     "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.",
29324                                     cimg_instance,
29325                                     img._width,img._height,img._depth,img._spectrum,img._data);
29326 
29327       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
29328       (*this)[0] = (T)(y*img[2] - z*img[1]);
29329       (*this)[1] = (T)(z*img[0] - x*img[2]);
29330       (*this)[2] = (T)(x*img[1] - y*img[0]);
29331       return *this;
29332     }
29333 
29334     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance.
29335     template<typename t>
29336     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
29337       return CImg<_cimg_Tt>(*this).cross(img);
29338     }
29339 
29340     //! Invert the instance image, viewed as a matrix.
29341     /**
29342        \param use_LU Choose the inverting algorithm. Can be:
29343        - \c true: LU-based matrix inversion.
29344        - \c false: SVD-based matrix inversion.
29345     **/
29346     CImg<T>& invert(const bool use_LU=true) {
29347       if (_width!=_height || _depth!=1 || _spectrum!=1)
29348         throw CImgInstanceException(_cimg_instance
29349                                     "invert(): Instance is not a square matrix.",
29350                                     cimg_instance);
29351       const double dete = _width>3?-1.:det();
29352       if (dete!=0. && _width==2) {
29353         const double
29354           a = _data[0], c = _data[1],
29355           b = _data[2], d = _data[3];
29356         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
29357         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
29358       } else if (dete!=0. && _width==3) {
29359         const double
29360           a = _data[0], d = _data[1], g = _data[2],
29361           b = _data[3], e = _data[4], h = _data[5],
29362           c = _data[6], f = _data[7], i = _data[8];
29363         _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
29364         _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
29365         _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
29366       } else {
29367 
29368 #ifdef cimg_use_lapack
29369         int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
29370         Tfloat
29371           *const lapA = new Tfloat[N*N],
29372           *const WORK = new Tfloat[LWORK];
29373         cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
29374         cimg::getrf(N,lapA,IPIV,INFO);
29375         if (INFO)
29376           cimg::warn(_cimg_instance
29377                      "invert(): LAPACK function dgetrf_() returned error code %d.",
29378                      cimg_instance,
29379                      INFO);
29380         else {
29381           cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
29382           if (INFO)
29383             cimg::warn(_cimg_instance
29384                        "invert(): LAPACK function dgetri_() returned error code %d.",
29385                        cimg_instance,
29386                        INFO);
29387         }
29388         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
29389         delete[] IPIV; delete[] lapA; delete[] WORK;
29390 #else
29391         if (use_LU) { // LU-based
29392           CImg<Tfloat> A(*this,false), indx;
29393           bool d;
29394           A._LU(indx,d);
29395           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16))
29396           cimg_forX(*this,j) {
29397             CImg<Tfloat> col(1,_width,1,1,0);
29398             col(j) = 1;
29399             col._solve(A,indx);
29400             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
29401           }
29402         } else pseudoinvert(false); // SVD-based
29403 #endif
29404       }
29405       return *this;
29406     }
29407 
29408     //! Invert the instance image, viewed as a matrix \newinstance.
29409     CImg<Tfloat> get_invert(const bool use_LU=true) const {
29410       return CImg<Tfloat>(*this,false).invert(use_LU);
29411     }
29412 
29413     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
29414     /**
29415     **/
29416     CImg<T>& pseudoinvert(const bool use_LU=false) {
29417       return get_pseudoinvert(use_LU).move_to(*this);
29418     }
29419 
29420     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
29421     CImg<Tfloat> get_pseudoinvert(const bool use_LU=false) const {
29422 
29423       // LU-based method.
29424       if (use_LU) {
29425         CImg<Tfloat> AtA(width(),width());
29426         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
29427         cimg_forY(AtA,i)
29428           for (int j = 0; j<=i; ++j) {
29429             double res = 0;
29430             cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k);
29431             AtA(j,i) = AtA(i,j) = (Tfloat)res;
29432           }
29433         AtA.invert(true);
29434         return AtA*get_transpose();
29435       }
29436 
29437       // SVD-based method.
29438       CImg<Tfloat> U, S, V;
29439       SVD(U,S,V,false);
29440       const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
29441       cimg_forX(V,x) {
29442         const Tfloat s = S(x), invs = s>epsilon?1/s:0;
29443         cimg_forY(V,y) V(x,y)*=invs;
29444       }
29445       return V*U.transpose();
29446     }
29447 
29448     //! Solve a system of linear equations.
29449     /**
29450        \param A Matrix of the linear system.
29451        \param use_LU In case of non square system (least-square solution),
29452                      choose between SVD-based (\c false) or LU-based (\c true) method.
29453                      LU method is faster for large matrices, but numerically less stable.
29454        \note Solve \c AX = B where \c B=*this.
29455     **/
29456     template<typename t>
29457     CImg<T>& solve(const CImg<t>& A, const bool use_LU=false) {
29458       if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
29459         throw CImgArgumentException(_cimg_instance
29460                                     "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
29461                                     "incompatible dimensions.",
29462                                     cimg_instance,
29463                                     A._width,A._height,A._depth,A._spectrum,A._data);
29464       typedef _cimg_Ttfloat Ttfloat;
29465 
29466       if (A.size()==1) return (*this)/=A[0];
29467       if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system
29468         const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3],
29469           fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d),
29470           det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd);
29471         if (fM==fa)
29472           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29473           cimg_forX(*this,k) {
29474             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
29475             (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y;
29476           } else if (fM==fc)
29477           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29478           cimg_forX(*this,k) {
29479             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
29480             (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y;
29481           } else if (fM==fb)
29482           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29483           cimg_forX(*this,k) {
29484             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
29485             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b);
29486           } else
29487           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
29488           cimg_forX(*this,k) {
29489             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
29490             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d);
29491           }
29492         return *this;
29493       }
29494 
29495       if (A._width==A._height) { // Square linear system
29496 #ifdef cimg_use_lapack
29497         char TRANS = 'N';
29498         int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
29499         Ttfloat
29500           *const lapA = new Ttfloat[N*N],
29501           *const lapB = new Ttfloat[N],
29502           *const WORK = new Ttfloat[LWORK];
29503         cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
29504         cimg_forX(*this,i) {
29505           cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j));
29506           cimg::getrf(N,lapA,IPIV,INFO);
29507           if (INFO)
29508             cimg::warn(_cimg_instance
29509                        "solve(): LAPACK library function dgetrf_() returned error code %d.",
29510                        cimg_instance,
29511                        INFO);
29512           else {
29513             cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
29514             if (INFO)
29515               cimg::warn(_cimg_instance
29516                          "solve(): LAPACK library function dgetrs_() returned error code %d.",
29517                          cimg_instance,
29518                          INFO);
29519           }
29520           if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0;
29521         }
29522         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
29523 #else
29524         CImg<Ttfloat> lu(A,false);
29525         CImg<Ttfloat> indx;
29526         bool d;
29527         lu._LU(indx,d);
29528         CImg<T> res(_width,A._width);
29529         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16))
29530           cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx));
29531         res.move_to(*this);
29532 #endif
29533       } else { // Least-square solution for non-square systems
29534 
29535 #ifdef cimg_use_lapack
29536         char TRANS = 'N';
29537         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
29538         Ttfloat WORK_QUERY;
29539         Ttfloat
29540           * const lapA = new Ttfloat[M*N],
29541           * const lapB = new Ttfloat[M*NRHS];
29542         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
29543         LWORK = (int) WORK_QUERY;
29544         Ttfloat *const WORK = new Ttfloat[LWORK];
29545         cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
29546         cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
29547         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
29548         if (INFO != 0)
29549           cimg::warn(_cimg_instance
29550                      "solve(): LAPACK library function sgels() returned error code %d.",
29551                      cimg_instance,
29552                      INFO);
29553         assign(NRHS, N);
29554         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
29555         else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
29556         delete[] lapA; delete[] lapB; delete[] WORK;
29557 #else
29558         (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
29559 #endif
29560       }
29561       return *this;
29562     }
29563 
29564     //! Solve a system of linear equations \newinstance.
29565     template<typename t>
29566     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A, const bool use_LU=false) const {
29567       typedef _cimg_Ttfloat Ttfloat;
29568       return CImg<Ttfloat>(*this,false).solve(A,use_LU);
29569     }
29570 
29571     template<typename t, typename ti>
29572     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
29573       typedef _cimg_Ttfloat Ttfloat;
29574       const int N = height();
29575       int ii = -1;
29576       Ttfloat sum;
29577       for (int i = 0; i<N; ++i) {
29578         const int ip = (int)indx[i];
29579         sum = (*this)(ip);
29580         (*this)(ip) = (*this)(i);
29581         if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
29582         else if (sum!=0) ii = i;
29583         (*this)(i) = (T)sum;
29584       }
29585       for (int i = N - 1; i>=0; --i) {
29586         sum = (*this)(i);
29587         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
29588         (*this)(i) = (T)(sum/A(i,i));
29589       }
29590       return *this;
29591     }
29592 
29593     //! Solve a tridiagonal system of linear equations.
29594     /**
29595        \param A Coefficients of the tridiagonal system.
29596        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
29597        stored as a 3 columns matrix
29598        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
29599     **/
29600     template<typename t>
29601     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
29602       const unsigned int siz = (unsigned int)size();
29603       if (A._width!=3 || A._height!=siz)
29604         throw CImgArgumentException(_cimg_instance
29605                                     "solve_tridiagonal(): Instance and tridiagonal matrix "
29606                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
29607                                     cimg_instance,
29608                                     A._width,A._height,A._depth,A._spectrum,A._data);
29609       typedef _cimg_Ttfloat Ttfloat;
29610       const Ttfloat epsilon = 1e-4f;
29611       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
29612       for (int i = 1; i<(int)siz; ++i) {
29613         const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
29614         B[i] -= m*A(2,i - 1);
29615         V[i] -= m*V[i - 1];
29616       }
29617       (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
29618       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));
29619       return *this;
29620     }
29621 
29622     //! Solve a tridiagonal system of linear equations \newinstance.
29623     template<typename t>
29624     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
29625       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
29626     }
29627 
29628     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
29629     /**
29630        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
29631        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
29632     **/
29633     template<typename t>
29634     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
29635       if (is_empty()) { val.assign(); vec.assign(); }
29636       else {
29637         if (_width!=_height || _depth>1 || _spectrum>1)
29638           throw CImgInstanceException(_cimg_instance
29639                                       "eigen(): Instance is not a square matrix.",
29640                                       cimg_instance);
29641 
29642         if (val.size()<(ulongT)_width) val.assign(1,_width);
29643         if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
29644         switch (_width) {
29645         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
29646         case 2 : {
29647           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
29648           double f = e*e - 4*(a*d - b*c);
29649           if (f<0) cimg::warn(_cimg_instance
29650                               "eigen(): Complex eigenvalues found.",
29651                               cimg_instance);
29652           f = std::sqrt(f);
29653           const double
29654             l1 = 0.5*(e - f),
29655             l2 = 0.5*(e + f),
29656             b2 = b*b,
29657             norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
29658             norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
29659           val[0] = (t)l2;
29660           val[1] = (t)l1;
29661           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; }
29662           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; }
29663         } break;
29664         default :
29665           throw CImgInstanceException(_cimg_instance
29666                                       "eigen(): Eigenvalues computation of general matrices is limited "
29667                                       "to 2x2 matrices.",
29668                                       cimg_instance);
29669         }
29670       }
29671       return *this;
29672     }
29673 
29674     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
29675     /**
29676        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
29677     **/
29678     CImgList<Tfloat> get_eigen() const {
29679       CImgList<Tfloat> res(2);
29680       eigen(res[0],res[1]);
29681       return res;
29682     }
29683 
29684     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
29685     /**
29686        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
29687        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
29688     **/
29689     template<typename t>
29690     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
29691       if (is_empty()) { val.assign(); vec.assign(); return *this; }
29692       if (_width!=_height || _depth>1 || _spectrum>1)
29693         throw CImgInstanceException(_cimg_instance
29694                                     "eigen(): Instance is not a square matrix.",
29695                                     cimg_instance);
29696       val.assign(1,_width);
29697       vec.assign(_width,_width);
29698 
29699       if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; }
29700       if (_width==2) {
29701         const double
29702           a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3],
29703           e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)),
29704           l1 = 0.5*(e - f), l2 = 0.5*(e + f),
29705           n = std::sqrt(cimg::sqr(l2 - a) + b*b);
29706         val[0] = (t)l2;
29707         val[1] = (t)l1;
29708         if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; }
29709         vec[1] = -vec[2];
29710         vec[3] = vec[0];
29711         return *this;
29712       }
29713 
29714 #ifdef cimg_use_lapack
29715       char JOB = 'V', UPLO = 'U';
29716       int N = _width, LWORK = 4*N, INFO;
29717       Tfloat
29718         *const lapA = new Tfloat[N*N],
29719         *const lapW = new Tfloat[N],
29720         *const WORK = new Tfloat[LWORK];
29721       cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
29722       cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
29723       if (INFO)
29724         cimg::warn(_cimg_instance
29725                    "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
29726                    cimg_instance,
29727                    INFO);
29728       if (!INFO) {
29729         cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
29730         cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
29731       } else { val.fill(0); vec.fill(0); }
29732       delete[] lapA; delete[] lapW; delete[] WORK;
29733 
29734 #else
29735       CImg<t> V(_width,_width);
29736       Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
29737       (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
29738       if (maxabs!=1) val*=maxabs;
29739 
29740       bool is_ambiguous = false;
29741       float eig = 0;
29742       cimg_forY(val,p) { // Check for ambiguous cases
29743         if (val[p]>eig) eig = (float)val[p];
29744         t scal = 0;
29745         cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
29746         if (cimg::abs(scal)<0.9f) is_ambiguous = true;
29747         if (scal<0) val[p] = -val[p];
29748       }
29749       if (is_ambiguous) {
29750         ++(eig*=2);
29751         SVD(vec,val,V,false,40,eig);
29752         val-=eig;
29753       }
29754 
29755       CImg<intT> permutations; // Sort eigenvalues in decreasing order
29756       CImg<t> tmp(_width);
29757       val.sort(permutations,false);
29758       cimg_forY(vec,k) {
29759         cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
29760         std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
29761       }
29762 #endif
29763       return *this;
29764     }
29765 
29766     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
29767     /**
29768        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
29769          symmetric_eigen(CImg<t>&,CImg<t>&) const.
29770     **/
29771     CImgList<Tfloat> get_symmetric_eigen() const {
29772       CImgList<Tfloat> res(2);
29773       symmetric_eigen(res[0],res[1]);
29774       return res;
29775     }
29776 
29777     //! Sort pixel values and get sorting permutations.
29778     /**
29779        \param[out] permutations Permutation map used for the sorting.
29780        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29781     **/
29782     template<typename t>
29783     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
29784       permutations.assign(_width,_height,_depth,_spectrum);
29785       if (is_empty()) return *this;
29786       cimg_foroff(permutations,off) permutations[off] = (t)off;
29787       return _quicksort(0,size() - 1,permutations,is_increasing,true);
29788     }
29789 
29790     //! Sort pixel values and get sorting permutations \newinstance.
29791     template<typename t>
29792     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
29793       return (+*this).sort(permutations,is_increasing);
29794     }
29795 
29796     //! Sort pixel values.
29797     /**
29798        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29799        \param axis Tells if the value sorting must be done along a specific axis. Can be:
29800        - \c 0: All pixel values are sorted, independently on their initial position.
29801        - \c 'x': Image columns are sorted, according to the first value in each column.
29802        - \c 'y': Image rows are sorted, according to the first value in each row.
29803        - \c 'z': Image slices are sorted, according to the first value in each slice.
29804        - \c 'c': Image channels are sorted, according to the first value in each channel.
29805     **/
29806     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
29807       if (is_empty()) return *this;
29808       CImg<uintT> perm;
29809       switch (cimg::lowercase(axis)) {
29810       case 0 :
29811         _quicksort(0,size() - 1,perm,is_increasing,false);
29812         break;
29813       case 'x' : {
29814         perm.assign(_width);
29815         get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
29816         CImg<T> img(*this,false);
29817         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
29818       } break;
29819       case 'y' : {
29820         perm.assign(_height);
29821         get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
29822         CImg<T> img(*this,false);
29823         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
29824       } break;
29825       case 'z' : {
29826         perm.assign(_depth);
29827         get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
29828         CImg<T> img(*this,false);
29829         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
29830       } break;
29831       case 'c' : {
29832         perm.assign(_spectrum);
29833         get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
29834         CImg<T> img(*this,false);
29835         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
29836       } break;
29837       default :
29838         throw CImgArgumentException(_cimg_instance
29839                                     "sort(): Invalid specified axis '%c' "
29840                                     "(should be { x | y | z | c }).",
29841                                     cimg_instance,axis);
29842       }
29843       return *this;
29844     }
29845 
29846     //! Sort pixel values \newinstance.
29847     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
29848       return (+*this).sort(is_increasing,axis);
29849     }
29850 
29851     template<typename t>
29852     CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
29853                         const bool is_increasing, const bool is_permutations) {
29854       if (indm<indM) {
29855         const long mid = (indm + indM)/2;
29856         if (is_increasing) {
29857           if ((*this)[indm]>(*this)[mid]) {
29858             cimg::swap((*this)[indm],(*this)[mid]);
29859             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29860           }
29861           if ((*this)[mid]>(*this)[indM]) {
29862             cimg::swap((*this)[indM],(*this)[mid]);
29863             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29864           }
29865           if ((*this)[indm]>(*this)[mid]) {
29866             cimg::swap((*this)[indm],(*this)[mid]);
29867             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29868           }
29869         } else {
29870           if ((*this)[indm]<(*this)[mid]) {
29871             cimg::swap((*this)[indm],(*this)[mid]);
29872             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29873           }
29874           if ((*this)[mid]<(*this)[indM]) {
29875             cimg::swap((*this)[indM],(*this)[mid]);
29876             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29877           }
29878           if ((*this)[indm]<(*this)[mid]) {
29879             cimg::swap((*this)[indm],(*this)[mid]);
29880             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29881           }
29882         }
29883         if (indM - indm>=3) {
29884           const T pivot = (*this)[mid];
29885           long i = indm, j = indM;
29886           if (is_increasing) {
29887             do {
29888               while ((*this)[i]<pivot) ++i;
29889               while ((*this)[j]>pivot) --j;
29890               if (i<=j) {
29891                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29892                 cimg::swap((*this)[i++],(*this)[j--]);
29893               }
29894             } while (i<=j);
29895           } else {
29896             do {
29897               while ((*this)[i]>pivot) ++i;
29898               while ((*this)[j]<pivot) --j;
29899               if (i<=j) {
29900                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29901                 cimg::swap((*this)[i++],(*this)[j--]);
29902               }
29903             } while (i<=j);
29904           }
29905           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
29906           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
29907         }
29908       }
29909       return *this;
29910     }
29911 
29912     //! Compute the SVD of the instance image, viewed as a general matrix.
29913     /**
29914        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
29915        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
29916        \param[out] U First matrix of the SVD product.
29917        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
29918          These coefficients are stored as a vector.
29919        \param[out] V Third matrix of the SVD product.
29920        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
29921        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
29922        \param lambda Epsilon used for the algorithm convergence.
29923        \note The instance matrix can be computed from \c U,\c S and \c V by
29924        \code
29925        const CImg<> A;  // Input matrix (assumed to contain some values)
29926        CImg<> U,S,V;
29927        A.SVD(U,S,V)
29928        \endcode
29929     **/
29930     template<typename t>
29931     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
29932                        const unsigned int max_iteration=40, const float lambda=0) const {
29933       typedef _cimg_Ttfloat Ttfloat;
29934       const Ttfloat epsilon = (Ttfloat)1e-25;
29935 
29936       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
29937       else if (_depth!=1 || _spectrum!=1)
29938         throw CImgInstanceException(_cimg_instance
29939                                     "SVD(): Instance has invalid dimensions (depth or channels different from 1).",
29940                                     cimg_instance);
29941       else {
29942         U = *this;
29943         if (lambda!=0) {
29944           const unsigned int delta = std::min(U._width,U._height);
29945           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
29946         }
29947         if (S.size()<_width) S.assign(1,_width);
29948         if (V._width<_width || V._height<_height) V.assign(_width,_width);
29949         CImg<t> rv1(_width);
29950         Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0;
29951         int l = 0;
29952 
29953         cimg_forX(U,i) {
29954           l = i + 1;
29955           rv1[i] = scale*g;
29956           g = s = scale = 0;
29957           if (i<height()) {
29958             for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
29959             if (scale) {
29960               for (int k = i; k<height(); ++k) {
29961                 U(i,k)/=scale;
29962                 s+=U(i,k)*U(i,k);
29963               }
29964               f = U(i,i);
29965               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29966               h = f*g - s;
29967               U(i,i) = f - g;
29968               for (int j = l; j<width(); ++j) {
29969                 s = 0;
29970                 for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
29971                 f = s/h;
29972                 for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
29973               }
29974               for (int k = i; k<height(); ++k) U(i,k)*=scale;
29975             }
29976           }
29977           S[i] = scale*g;
29978 
29979           g = s = scale = 0;
29980           if (i<height() && i!=width() - 1) {
29981             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
29982             if (scale) {
29983               for (int k = l; k<width(); ++k) {
29984                 U(k,i)/=scale;
29985                 s+=U(k,i)*U(k,i);
29986               }
29987               f = U(l,i);
29988               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29989               h = f*g - s;
29990               U(l,i) = f - g;
29991               for (int k = l; k<width(); ++k) rv1[k] = U(k,i)/h;
29992               for (int j = l; j<height(); ++j) {
29993                 s = 0;
29994                 for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
29995                 for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
29996               }
29997               for (int k = l; k<width(); ++k) U(k,i)*=scale;
29998             }
29999           }
30000           anorm = (Ttfloat)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
30001         }
30002 
30003         for (int i = width() - 1; i>=0; --i) {
30004           if (i<width() - 1) {
30005             if (g) {
30006               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
30007               for (int j = l; j<width(); ++j) {
30008                 s = 0;
30009                 for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
30010                 for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
30011               }
30012             }
30013             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.;
30014           }
30015           V(i,i) = (t)1;
30016           g = rv1[i];
30017           l = i;
30018         }
30019 
30020         for (int i = std::min(width(),height()) - 1; i>=0; --i) {
30021           l = i + 1;
30022           g = S[i];
30023           for (int j = l; j<width(); ++j) U(j,i) = 0;
30024           if (g) {
30025             g = 1/g;
30026             for (int j = l; j<width(); ++j) {
30027               s = 0;
30028               for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
30029               f = (s/U(i,i))*g;
30030               for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
30031             }
30032             for (int j = i; j<height(); ++j) U(i,j)*= g;
30033           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
30034           ++U(i,i);
30035         }
30036 
30037         for (int k = width() - 1; k>=0; --k) {
30038           int nm = 0;
30039           for (unsigned int its = 0; its<max_iteration; ++its) {
30040             bool flag = true;
30041             for (l = k; l>=1; --l) {
30042               nm = l - 1;
30043               if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
30044               if ((cimg::abs(S[nm]) + anorm)==anorm) break;
30045             }
30046             if (flag) {
30047               c = 0;
30048               s = 1;
30049               for (int i = l; i<=k; ++i) {
30050                 f = s*rv1[i];
30051                 rv1[i] = c*rv1[i];
30052                 if ((cimg::abs(f) + anorm)==anorm) break;
30053                 g = S[i];
30054                 h = cimg::_hypot(f,g);
30055                 S[i] = h;
30056                 h = 1/h;
30057                 c = g*h;
30058                 s = -f*h;
30059                 cimg_forY(U,j) {
30060                   const t y = U(nm,j), z = U(i,j);
30061                   U(nm,j) = y*c + z*s;
30062                   U(i,j) = z*c - y*s;
30063                 }
30064               }
30065             }
30066 
30067             const t z = S[k];
30068             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
30069             nm = k - 1;
30070             t x = S[l], y = S[nm];
30071             g = rv1[nm];
30072             h = rv1[k];
30073             f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y);
30074             g = cimg::_hypot(f,(Ttfloat)1);
30075             f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x);
30076             c = s = 1;
30077             for (int j = l; j<=nm; ++j) {
30078               const int i = j + 1;
30079               g = rv1[i];
30080               h = s*g;
30081               g = c*g;
30082               t y1 = S[i], z1 = cimg::_hypot(f,h);
30083               rv1[j] = z1;
30084               c = f/std::max(epsilon,(Ttfloat)z1);
30085               s = h/std::max(epsilon,(Ttfloat)z1);
30086               f = x*c + g*s;
30087               g = g*c - x*s;
30088               h = y1*s;
30089               y1*=c;
30090               cimg_forX(U,jj) {
30091                 const t x2 = V(j,jj), z2 = V(i,jj);
30092                 V(j,jj) = x2*c + z2*s;
30093                 V(i,jj) = z2*c - x2*s;
30094               }
30095               z1 = cimg::_hypot(f,h);
30096               S[j] = z1;
30097               if (z1) {
30098                 z1 = 1/std::max(epsilon,(Ttfloat)z1);
30099                 c = f*z1;
30100                 s = h*z1;
30101               }
30102               f = c*g + s*y1;
30103               x = c*y1 - s*g;
30104               cimg_forY(U,jj) {
30105                 const t y2 = U(j,jj), z2 = U(i,jj);
30106                 U(j,jj) = y2*c + z2*s;
30107                 U(i,jj) = z2*c - y2*s;
30108               }
30109             }
30110             rv1[l] = 0;
30111             rv1[k] = f;
30112             S[k] = x;
30113           }
30114         }
30115 
30116         if (sorting) {
30117           CImg<intT> permutations;
30118           CImg<t> tmp(_width);
30119           S.sort(permutations,false);
30120           cimg_forY(U,k) {
30121             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
30122             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
30123           }
30124           cimg_forY(V,k) {
30125             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
30126             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
30127           }
30128         }
30129       }
30130       return *this;
30131     }
30132 
30133     //! Compute the SVD of the instance image, viewed as a general matrix.
30134     /**
30135        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
30136          SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
30137     **/
30138     CImgList<Tfloat> get_SVD(const bool sorting=true,
30139                              const unsigned int max_iteration=40, const float lambda=0) const {
30140       CImgList<Tfloat> res(3);
30141       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
30142       return res;
30143     }
30144 
30145     // [internal] Compute the LU decomposition of a permuted matrix.
30146     template<typename t>
30147     CImg<T>& _LU(CImg<t>& indx, bool& d) {
30148       const int N = width();
30149       int imax = 0;
30150       CImg<Tfloat> vv(N);
30151       indx.assign(N);
30152       d = true;
30153 
30154       bool return0 = false;
30155       cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512))
30156       cimg_forX(*this,i) {
30157         Tfloat vmax = 0;
30158         cimg_forX(*this,j) {
30159           const Tfloat tmp = cimg::abs((*this)(j,i));
30160           if (tmp>vmax) vmax = tmp;
30161         }
30162         if (vmax==0) return0 = true; else vv[i] = 1/vmax;
30163       }
30164       if (return0) { indx.fill(0); return fill(0); }
30165 
30166       cimg_forX(*this,j) {
30167         for (int i = 0; i<j; ++i) {
30168           Tfloat sum = (*this)(j,i);
30169           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
30170           (*this)(j,i) = (T)sum;
30171         }
30172 
30173         Tfloat vmax = 0;
30174         for (int i = j; i<width(); ++i) {
30175           Tfloat sum = (*this)(j,i);
30176           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
30177           (*this)(j,i) = (T)sum;
30178           const Tfloat tmp = vv[i]*cimg::abs(sum);
30179           if (tmp>=vmax) { vmax = tmp; imax = i; }
30180         }
30181         if (j!=imax) {
30182           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
30183           d = !d;
30184           vv[imax] = vv[j];
30185         }
30186         indx[j] = (t)imax;
30187         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
30188         if (j<N) {
30189           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
30190           for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
30191         }
30192       }
30193 
30194       return *this;
30195     }
30196 
30197     //! Compute the projection of the instance matrix onto the specified dictionary.
30198     /**
30199        Find the best matching projection of selected matrix onto the span of an over-complete dictionary D,
30200        using the orthogonal projection or (opt. Orthogonal) Matching Pursuit algorithm.
30201        Instance image must a 2D-matrix in which each column represent a signal to project.
30202        \param dictionary A matrix in which each column is an element of the dictionary D.
30203        \param method Tell what projection method is applied. It can be:
30204          - 0 = orthogonal projection (default).
30205          - 1 = matching pursuit.
30206          - 2 = matching pursuit, with a single orthogonal projection step at the end.
30207          - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
30208                  every 'method-2' iterations.
30209        \param max_iter Sets the max number of iterations processed for each signal.
30210                        If set to '0' (default), 'max_iter' is set to the number of dictionary columns.
30211                        (only meaningful for matching pursuit and its variants).
30212        \param max_residual Gives a stopping criterion on signal reconstruction accuracy.
30213                            (only meaningful for matching pursuit and its variants).
30214        \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column.
30215                Thus, the matrix product D*W is an approximation of the input matrix.
30216     **/
30217     template<typename t>
30218     CImg<T>& project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
30219                             const unsigned int max_iter=0, const double max_residual=1e-6) {
30220       return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this);
30221     }
30222 
30223     template<typename t>
30224     CImg<Tfloat> get_project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
30225                                     const unsigned int max_iter=0, const double max_residual=1e-6) const {
30226       if (_depth!=1 || _spectrum!=1)
30227         throw CImgInstanceException(_cimg_instance
30228                                     "project_matrix(): Instance image is not a matrix.",
30229                                     cimg_instance);
30230       if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1)
30231         throw CImgArgumentException(_cimg_instance
30232                                     "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.",
30233                                     cimg_instance,
30234                                     dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum);
30235 
30236       if (!method) return get_solve(dictionary,true);
30237       CImg<Tfloat> W(_width,dictionary._width,1,1,0);
30238 
30239       // Compute dictionary norm and normalize it.
30240       CImg<Tfloat> D(dictionary,false), Dnorm(D._width);
30241       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
30242       cimg_forX(Dnorm,d) {
30243         Tfloat norm = 0;
30244         cimg_forY(D,y) norm+=cimg::sqr(D(d,y));
30245         Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm));
30246       }
30247       cimg_forXY(D,d,y) D(d,y)/=Dnorm[d];
30248 
30249       // Matching pursuit.
30250       const unsigned int proj_step = method<3?1:method - 2;
30251       bool is_orthoproj = false;
30252 
30253       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
30254         cimg_forX(*this,x) {
30255         CImg<Tfloat> S = get_column(x);
30256         const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
30257         Tfloat residual = S.magnitude()/S._height;
30258         const unsigned int nmax = max_iter?max_iter:D._width;
30259 
30260         for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
30261 
30262           // Find best matching column in D.
30263           int dmax = 0;
30264           Tfloat absdotmax = 0, dotmax = 0;
30265           cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32))
30266           cimg_forX(D,d) {
30267             Tfloat _dot = 0;
30268             cimg_forY(D,y) _dot+=S[y]*D(d,y);
30269             Tfloat absdot = cimg::abs(_dot);
30270             cimg_pragma_openmp(critical(get_project_matrix)) {
30271               if (absdot>absdotmax) {
30272                 absdotmax = absdot;
30273                 dotmax = _dot;
30274                 dmax = d;
30275               }
30276             }
30277           }
30278 
30279           if (!n || method<3 || n%proj_step) {
30280             // Matching Pursuit: Subtract component to signal.
30281             W(x,dmax)+=dotmax;
30282             residual = 0;
30283             cimg_forY(S,y) {
30284               S[y]-=dotmax*D(dmax,y);
30285               residual+=cimg::sqr(S[y]);
30286             }
30287             residual = std::sqrt(residual)/S._height;
30288             is_orthoproj = false;
30289 
30290           } else {
30291             // Orthogonal Matching Pursuit: Orthogonal projection step.
30292             W(x,dmax) = 1; // Used as a marker only.
30293             unsigned int nbW = 0;
30294             cimg_forY(W,d) if (W(x,d)) ++nbW;
30295             CImg<Tfloat> sD(nbW,D._height);
30296             CImg<uintT> inds(nbW);
30297             int sd = 0;
30298             cimg_forY(W,d) if (W(x,d)) {
30299               cimg_forY(sD,y) sD(sd,y) = D(d,y);
30300               inds[sd++] = d;
30301             }
30302             S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights
30303 
30304             // Recompute residual signal.
30305             S = S0;
30306             cimg_forY(sD,k) {
30307               const Tfloat weight = sD[k];
30308               const unsigned int ind = inds[k];
30309               W(x,ind) = weight;
30310               cimg_forY(S,y) S[y]-=weight*D(ind,y);
30311             }
30312             residual = S.magnitude()/S._height;
30313             is_orthoproj = true;
30314           }
30315         }
30316 
30317         // Perform last orthoprojection step if needed.
30318         if (method>=2 && !is_orthoproj) {
30319           unsigned int nbW = 0;
30320           cimg_forY(W,d) if (W(x,d)) ++nbW;
30321           if (nbW) { // Avoid degenerated case where 0 coefs are used
30322             CImg<Tfloat> sD(nbW,D._height);
30323             CImg<uintT> inds(nbW);
30324             int sd = 0;
30325             cimg_forY(W,d) if (W(x,d)) {
30326               cimg_forY(sD,y) sD(sd,y) = D(d,y);
30327               inds[sd++] = d;
30328             }
30329             S0.get_solve(sD,true).move_to(sD);
30330             cimg_forY(sD,k) W(x,inds[k]) = sD[k];
30331           }
30332         }
30333       }
30334 
30335       // Normalize resulting coefficients according to initial (non-normalized) dictionary.
30336       cimg_forXY(W,x,y) W(x,y)/=Dnorm[y];
30337       return W;
30338     }
30339 
30340     //! Compute minimal path in a graph, using the Dijkstra algorithm.
30341     /**
30342        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
30343          between two nodes (i,j).
30344        \param nb_nodes Number of graph nodes.
30345        \param starting_node Index of the starting node.
30346        \param ending_node Index of the ending node (set to ~0U to ignore ending node).
30347        \param previous_node Array that gives the previous node index in the path to the starting node
30348          (optional parameter).
30349        \return Array of distances of each node to the starting node.
30350     **/
30351     template<typename tf, typename t>
30352     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
30353                             const unsigned int starting_node, const unsigned int ending_node,
30354                             CImg<t>& previous_node) {
30355       if (starting_node>=nb_nodes)
30356         throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher "
30357                                     "than number of nodes %u.",
30358                                     pixel_type(),starting_node,nb_nodes);
30359       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
30360       dist(starting_node) = 0;
30361       previous_node.assign(1,nb_nodes,1,1,(t)-1);
30362       previous_node(starting_node) = (t)starting_node;
30363       CImg<uintT> Q(nb_nodes);
30364       cimg_forX(Q,u) Q(u) = (unsigned int)u;
30365       cimg::swap(Q(starting_node),Q(0));
30366       unsigned int sizeQ = nb_nodes;
30367       while (sizeQ) {
30368         // Update neighbors from minimal vertex
30369         const unsigned int umin = Q(0);
30370         if (umin==ending_node) sizeQ = 0;
30371         else {
30372           const T dmin = dist(umin);
30373           const T infty = cimg::type<T>::max();
30374           for (unsigned int q = 1; q<sizeQ; ++q) {
30375             const unsigned int v = Q(q);
30376             const T d = (T)distance(v,umin);
30377             if (d<infty) {
30378               const T alt = dmin + d;
30379               if (alt<dist(v)) {
30380                 dist(v) = alt;
30381                 previous_node(v) = (t)umin;
30382                 const T distpos = dist(Q(q));
30383                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
30384                   cimg::swap(Q(pos),Q(par));
30385               }
30386             }
30387           }
30388           // Remove minimal vertex from queue
30389           Q(0) = Q(--sizeQ);
30390           const T distpos = dist(Q(0));
30391           for (unsigned int pos = 0, left = 0, right = 0;
30392                ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
30393                  (right<sizeQ && distpos>dist(Q(right)));) {
30394             if (right<sizeQ) {
30395               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
30396               else { cimg::swap(Q(pos),Q(right)); pos = right; }
30397             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
30398           }
30399         }
30400       }
30401       return dist;
30402     }
30403 
30404     //! Return minimal path in a graph, using the Dijkstra algorithm.
30405     template<typename tf, typename t>
30406     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
30407                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
30408       CImg<uintT> foo;
30409       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
30410     }
30411 
30412     //! Return minimal path in a graph, using the Dijkstra algorithm.
30413     /**
30414        \param starting_node Index of the starting node.
30415        \param ending_node Index of the ending node.
30416        \param previous_node Array that gives the previous node index in the path to the starting node
30417          (optional parameter).
30418        \return Array of distances of each node to the starting node.
30419        \note image instance corresponds to the adjacency matrix of the graph.
30420     **/
30421     template<typename t>
30422     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
30423                       CImg<t>& previous_node) {
30424       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
30425     }
30426 
30427     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
30428     template<typename t>
30429     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
30430                          CImg<t>& previous_node) const {
30431       if (_width!=_height || _depth!=1 || _spectrum!=1)
30432         throw CImgInstanceException(_cimg_instance
30433                                     "dijkstra(): Instance is not a graph adjacency matrix.",
30434                                     cimg_instance);
30435 
30436       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
30437     }
30438 
30439     //! Return minimal path in a graph, using the Dijkstra algorithm.
30440     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
30441       return get_dijkstra(starting_node,ending_node).move_to(*this);
30442     }
30443 
30444     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
30445     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
30446       CImg<uintT> foo;
30447       return get_dijkstra(starting_node,ending_node,foo);
30448     }
30449 
30450     //! Return an image containing the character codes of specified string.
30451     /**
30452        \param str input C-string to encode as an image.
30453        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
30454        \param is_shared Return result that shares its buffer with \p str.
30455     **/
30456     static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
30457       if (!str) return CImg<T>();
30458       return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
30459     }
30460 
30461     //! Return a \c 1x1 image containing specified value.
30462     /**
30463        \param a0 First vector value.
30464     **/
30465     static CImg<T> row_vector(const T& a0) {
30466       return vector(a0);
30467     }
30468 
30469     //! Return a \c 2x1 image containing specified values.
30470     /**
30471        \param a0 First vector value.
30472        \param a1 Second vector value.
30473     **/
30474     static CImg<T> row_vector(const T& a0, const T& a1) {
30475       CImg<T> r(2,1);
30476       r[0] = a0; r[1] = a1;
30477       return r;
30478     }
30479 
30480     //! Return a \c 3x1 image containing specified values.
30481     /**
30482        \param a0 First vector value.
30483        \param a1 Second vector value.
30484        \param a2 Third vector value.
30485     **/
30486     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2) {
30487       CImg<T> r(3,1);
30488       r[0] = a0; r[1] = a1; r[2] = a2;
30489       return r;
30490     }
30491 
30492     //! Return a \c 4x1 image containing specified values.
30493     /**
30494        \param a0 First vector value.
30495        \param a1 Second vector value.
30496        \param a2 Third vector value.
30497        \param a3 Fourth vector value.
30498     **/
30499     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3) {
30500       CImg<T> r(4,1);
30501       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
30502       return r;
30503     }
30504 
30505     //! Return a \c 5x1 image containing specified values.
30506     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30507       CImg<T> r(5,1);
30508       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
30509       return r;
30510     }
30511 
30512     //! Return a \c 6x1 image containing specified values.
30513     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30514       CImg<T> r(6,1);
30515       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
30516       return r;
30517     }
30518 
30519     //! Return a \c 7x1 image containing specified values.
30520     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30521                               const T& a4, const T& a5, const T& a6) {
30522       CImg<T> r(7,1);
30523       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
30524       return r;
30525     }
30526 
30527     //! Return a \c 8x1 image containing specified values.
30528     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30529                               const T& a4, const T& a5, const T& a6, const T& a7) {
30530       CImg<T> r(8,1);
30531       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
30532       return r;
30533     }
30534 
30535     //! Return a \c 9x1 image containing specified values.
30536     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30537                               const T& a4, const T& a5, const T& a6, const T& a7,
30538                               const T& a8) {
30539       CImg<T> r(9,1);
30540       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;
30541       return r;
30542     }
30543 
30544     //! Return a \c 10x1 image containing specified values.
30545     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30546                               const T& a4, const T& a5, const T& a6, const T& a7,
30547                               const T& a8, const T& a9) {
30548       CImg<T> r(10,1);
30549       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;
30550       return r;
30551     }
30552 
30553     //! Return a \c 11x1 image containing specified values.
30554     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30555                               const T& a4, const T& a5, const T& a6, const T& a7,
30556                               const T& a8, const T& a9, const T& a10) {
30557       CImg<T> r(11,1);
30558       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;
30559       r[10] = a10;
30560       return r;
30561     }
30562 
30563     //! Return a \c 12x1 image containing specified values.
30564     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30565                               const T& a4, const T& a5, const T& a6, const T& a7,
30566                               const T& a8, const T& a9, const T& a10, const T& a11) {
30567       CImg<T> r(12,1);
30568       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;
30569       r[10] = a10; r[11] = a11;
30570       return r;
30571     }
30572 
30573     //! Return a \c 13x1 image containing specified values.
30574     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30575                               const T& a4, const T& a5, const T& a6, const T& a7,
30576                               const T& a8, const T& a9, const T& a10, const T& a11,
30577                               const T& a12) {
30578       CImg<T> r(13,1);
30579       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;
30580       r[10] = a10; r[11] = a11; r[12] = a12;
30581       return r;
30582     }
30583 
30584     //! Return a \c 14x1 image containing specified values.
30585     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30586                               const T& a4, const T& a5, const T& a6, const T& a7,
30587                               const T& a8, const T& a9, const T& a10, const T& a11,
30588                               const T& a12, const T& a13) {
30589       CImg<T> r(14,1);
30590       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;
30591       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
30592       return r;
30593     }
30594 
30595     //! Return a \c 15x1 image containing specified values.
30596     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30597                               const T& a4, const T& a5, const T& a6, const T& a7,
30598                               const T& a8, const T& a9, const T& a10, const T& a11,
30599                               const T& a12, const T& a13, const T& a14) {
30600       CImg<T> r(15,1);
30601       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;
30602       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
30603       return r;
30604     }
30605 
30606     //! Return a \c 16x1 image containing specified values.
30607     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
30608                               const T& a4, const T& a5, const T& a6, const T& a7,
30609                               const T& a8, const T& a9, const T& a10, const T& a11,
30610                               const T& a12, const T& a13, const T& a14, const T& a15) {
30611       CImg<T> r(16,1);
30612       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;
30613       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
30614       return r;
30615     }
30616 
30617     //! Return a \c 1x1 image containing specified value.
30618     /**
30619        \param a0 First vector value.
30620     **/
30621     static CImg<T> vector(const T& a0) {
30622       CImg<T> r(1,1);
30623       r[0] = a0;
30624       return r;
30625     }
30626 
30627     //! Return a \c 1x2 image containing specified values.
30628     /**
30629        \param a0 First vector value.
30630        \param a1 Second vector value.
30631     **/
30632     static CImg<T> vector(const T& a0, const T& a1) {
30633       CImg<T> r(1,2);
30634       r[0] = a0; r[1] = a1;
30635       return r;
30636     }
30637 
30638     //! Return a \c 1x3 image containing specified values.
30639     /**
30640        \param a0 First vector value.
30641        \param a1 Second vector value.
30642        \param a2 Third vector value.
30643     **/
30644     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
30645       CImg<T> r(1,3);
30646       r[0] = a0; r[1] = a1; r[2] = a2;
30647       return r;
30648     }
30649 
30650     //! Return a \c 1x4 image containing specified values.
30651     /**
30652        \param a0 First vector value.
30653        \param a1 Second vector value.
30654        \param a2 Third vector value.
30655        \param a3 Fourth vector value.
30656     **/
30657     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
30658       CImg<T> r(1,4);
30659       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
30660       return r;
30661     }
30662 
30663     //! Return a \c 1x5 image containing specified values.
30664     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30665       CImg<T> r(1,5);
30666       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
30667       return r;
30668     }
30669 
30670     //! Return a \c 1x6 image containing specified values.
30671     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30672       CImg<T> r(1,6);
30673       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
30674       return r;
30675     }
30676 
30677     //! Return a \c 1x7 image containing specified values.
30678     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30679                           const T& a4, const T& a5, const T& a6) {
30680       CImg<T> r(1,7);
30681       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
30682       return r;
30683     }
30684 
30685     //! Return a \c 1x8 image containing specified values.
30686     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30687                           const T& a4, const T& a5, const T& a6, const T& a7) {
30688       CImg<T> r(1,8);
30689       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
30690       return r;
30691     }
30692 
30693     //! Return a \c 1x9 image containing specified values.
30694     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30695                           const T& a4, const T& a5, const T& a6, const T& a7,
30696                           const T& a8) {
30697       CImg<T> r(1,9);
30698       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;
30699       return r;
30700     }
30701 
30702     //! Return a \c 1x10 image containing specified values.
30703     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30704                           const T& a4, const T& a5, const T& a6, const T& a7,
30705                           const T& a8, const T& a9) {
30706       CImg<T> r(1,10);
30707       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;
30708       return r;
30709     }
30710 
30711     //! Return a \c 1x11 image containing specified values.
30712     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30713                           const T& a4, const T& a5, const T& a6, const T& a7,
30714                           const T& a8, const T& a9, const T& a10) {
30715       CImg<T> r(1,11);
30716       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;
30717       r[10] = a10;
30718       return r;
30719     }
30720 
30721     //! Return a \c 1x12 image containing specified values.
30722     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30723                           const T& a4, const T& a5, const T& a6, const T& a7,
30724                           const T& a8, const T& a9, const T& a10, const T& a11) {
30725       CImg<T> r(1,12);
30726       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;
30727       r[10] = a10; r[11] = a11;
30728       return r;
30729     }
30730 
30731     //! Return a \c 1x13 image containing specified values.
30732     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30733                           const T& a4, const T& a5, const T& a6, const T& a7,
30734                           const T& a8, const T& a9, const T& a10, const T& a11,
30735                           const T& a12) {
30736       CImg<T> r(1,13);
30737       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;
30738       r[10] = a10; r[11] = a11; r[12] = a12;
30739       return r;
30740     }
30741 
30742     //! Return a \c 1x14 image containing specified values.
30743     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30744                           const T& a4, const T& a5, const T& a6, const T& a7,
30745                           const T& a8, const T& a9, const T& a10, const T& a11,
30746                           const T& a12, const T& a13) {
30747       CImg<T> r(1,14);
30748       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;
30749       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
30750       return r;
30751     }
30752 
30753     //! Return a \c 1x15 image containing specified values.
30754     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30755                           const T& a4, const T& a5, const T& a6, const T& a7,
30756                           const T& a8, const T& a9, const T& a10, const T& a11,
30757                           const T& a12, const T& a13, const T& a14) {
30758       CImg<T> r(1,15);
30759       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;
30760       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
30761       return r;
30762     }
30763 
30764     //! Return a \c 1x16 image containing specified values.
30765     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30766                           const T& a4, const T& a5, const T& a6, const T& a7,
30767                           const T& a8, const T& a9, const T& a10, const T& a11,
30768                           const T& a12, const T& a13, const T& a14, const T& a15) {
30769       CImg<T> r(1,16);
30770       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;
30771       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
30772       return r;
30773     }
30774 
30775     //! Return a 1x1 matrix containing specified coefficients.
30776     /**
30777        \param a0 First matrix value.
30778        \note Equivalent to vector(const T&).
30779     **/
30780     static CImg<T> matrix(const T& a0) {
30781       return vector(a0);
30782     }
30783 
30784     //! Return a 2x2 matrix containing specified coefficients.
30785     /**
30786        \param a0 First matrix value.
30787        \param a1 Second matrix value.
30788        \param a2 Third matrix value.
30789        \param a3 Fourth matrix value.
30790     **/
30791     static CImg<T> matrix(const T& a0, const T& a1,
30792                           const T& a2, const T& a3) {
30793       CImg<T> r(2,2); T *ptr = r._data;
30794       *(ptr++) = a0; *(ptr++) = a1;
30795       *(ptr++) = a2; *(ptr++) = a3;
30796       return r;
30797     }
30798 
30799     //! Return a 3x3 matrix containing specified coefficients.
30800     /**
30801        \param a0 First matrix value.
30802        \param a1 Second matrix value.
30803        \param a2 Third matrix value.
30804        \param a3 Fourth matrix value.
30805        \param a4 Fifth matrix value.
30806        \param a5 Sixth matrix value.
30807        \param a6 Seventh matrix value.
30808        \param a7 Eighth matrix value.
30809        \param a8 Ninth matrix value.
30810     **/
30811     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
30812                           const T& a3, const T& a4, const T& a5,
30813                           const T& a6, const T& a7, const T& a8) {
30814       CImg<T> r(3,3); T *ptr = r._data;
30815       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
30816       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
30817       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
30818       return r;
30819     }
30820 
30821     //! Return a 4x4 matrix containing specified coefficients.
30822     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
30823                           const T& a4, const T& a5, const T& a6, const T& a7,
30824                           const T& a8, const T& a9, const T& a10, const T& a11,
30825                           const T& a12, const T& a13, const T& a14, const T& a15) {
30826       CImg<T> r(4,4); T *ptr = r._data;
30827       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
30828       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
30829       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
30830       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
30831       return r;
30832     }
30833 
30834     //! Return a 5x5 matrix containing specified coefficients.
30835     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
30836                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
30837                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
30838                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
30839                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
30840       CImg<T> r(5,5); T *ptr = r._data;
30841       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
30842       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
30843       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
30844       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
30845       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
30846       return r;
30847     }
30848 
30849     //! Return a 1x1 symmetric matrix containing specified coefficients.
30850     /**
30851        \param a0 First matrix value.
30852        \note Equivalent to vector(const T&).
30853     **/
30854     static CImg<T> tensor(const T& a0) {
30855       return matrix(a0);
30856     }
30857 
30858     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
30859     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
30860       return matrix(a0,a1,a1,a2);
30861     }
30862 
30863     //! Return a 3x3 symmetric matrix containing specified coefficients.
30864     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30865       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
30866     }
30867 
30868     //! Return a 1x1 diagonal matrix containing specified coefficients.
30869     static CImg<T> diagonal(const T& a0) {
30870       return matrix(a0);
30871     }
30872 
30873     //! Return a 2x2 diagonal matrix containing specified coefficients.
30874     static CImg<T> diagonal(const T& a0, const T& a1) {
30875       return matrix(a0,0,0,a1);
30876     }
30877 
30878     //! Return a 3x3 diagonal matrix containing specified coefficients.
30879     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
30880       return matrix(a0,0,0,0,a1,0,0,0,a2);
30881     }
30882 
30883     //! Return a 4x4 diagonal matrix containing specified coefficients.
30884     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
30885       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
30886     }
30887 
30888     //! Return a 5x5 diagonal matrix containing specified coefficients.
30889     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30890       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);
30891     }
30892 
30893     //! Return a NxN identity matrix.
30894     /**
30895        \param N Dimension of the matrix.
30896     **/
30897     static CImg<T> identity_matrix(const unsigned int N) {
30898       CImg<T> res(N,N,1,1,0);
30899       cimg_forX(res,x) res(x,x) = 1;
30900       return res;
30901     }
30902 
30903     //! Return a N-numbered sequence vector from \p a0 to \p a1.
30904     /**
30905        \param N Size of the resulting vector.
30906        \param a0 Starting value of the sequence.
30907        \param a1 Ending value of the sequence.
30908      **/
30909     static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
30910       if (N) return CImg<T>(1,N).sequence(a0,a1);
30911       return CImg<T>();
30912     }
30913 
30914     //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
30915     /**
30916        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
30917        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
30918        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
30919        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
30920        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
30921      **/
30922     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
30923                                    const bool is_quaternion=false) {
30924       double X, Y, Z, W, N;
30925       if (is_quaternion) {
30926         N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
30927         if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
30928         else { X = Y = Z = 0; W = 1; }
30929         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),
30930                                (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
30931                                (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
30932       }
30933       N = cimg::hypot((double)x,(double)y,(double)z);
30934       if (N>0) { X = x/N; Y = y/N; Z = z/N; }
30935       else { X = Y = 0; Z = 1; }
30936       const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
30937       return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
30938                              (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
30939                              (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
30940     }
30941 
30942     //@}
30943     //-----------------------------------
30944     //
30945     //! \name Value Manipulation
30946     //@{
30947     //-----------------------------------
30948 
30949     //! Fill all pixel values with specified value.
30950     /**
30951        \param val Fill value.
30952     **/
30953     CImg<T>& fill(const T& val) {
30954       if (is_empty()) return *this;
30955       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
30956       else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
30957       return *this;
30958     }
30959 
30960     //! Fill all pixel values with specified value \newinstance.
30961     CImg<T> get_fill(const T& val) const {
30962       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
30963     }
30964 
30965     //! Fill sequentially all pixel values with specified values.
30966     /**
30967        \param val0 First fill value.
30968        \param val1 Second fill value.
30969     **/
30970     CImg<T>& fill(const T& val0, const T& val1) {
30971       if (is_empty()) return *this;
30972       T *ptrd, *ptre = end() - 1;
30973       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
30974       if (ptrd!=ptre + 1) *(ptrd++) = val0;
30975       return *this;
30976     }
30977 
30978     //! Fill sequentially all pixel values with specified values \newinstance.
30979     CImg<T> get_fill(const T& val0, const T& val1) const {
30980       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
30981     }
30982 
30983     //! Fill sequentially all pixel values with specified values \overloading.
30984     CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
30985       if (is_empty()) return *this;
30986       T *ptrd, *ptre = end() - 2;
30987       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
30988       ptre+=2;
30989       switch (ptre - ptrd) {
30990       case 2 : *(--ptre) = val1; // fallthrough
30991       case 1 : *(--ptre) = val0; // fallthrough
30992       }
30993       return *this;
30994     }
30995 
30996     //! Fill sequentially all pixel values with specified values \newinstance.
30997     CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
30998       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
30999     }
31000 
31001     //! Fill sequentially all pixel values with specified values \overloading.
31002     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
31003       if (is_empty()) return *this;
31004       T *ptrd, *ptre = end() - 3;
31005       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
31006       ptre+=3;
31007       switch (ptre - ptrd) {
31008       case 3 : *(--ptre) = val2; // fallthrough
31009       case 2 : *(--ptre) = val1; // fallthrough
31010       case 1 : *(--ptre) = val0; // fallthrough
31011       }
31012       return *this;
31013     }
31014 
31015     //! Fill sequentially all pixel values with specified values \newinstance.
31016     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
31017       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
31018     }
31019 
31020     //! Fill sequentially all pixel values with specified values \overloading.
31021     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
31022       if (is_empty()) return *this;
31023       T *ptrd, *ptre = end() - 4;
31024       for (ptrd = _data; ptrd<ptre; ) {
31025         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
31026       }
31027       ptre+=4;
31028       switch (ptre - ptrd) {
31029       case 4 : *(--ptre) = val3; // fallthrough
31030       case 3 : *(--ptre) = val2; // fallthrough
31031       case 2 : *(--ptre) = val1; // fallthrough
31032       case 1 : *(--ptre) = val0; // fallthrough
31033       }
31034       return *this;
31035     }
31036 
31037     //! Fill sequentially all pixel values with specified values \newinstance.
31038     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
31039       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
31040     }
31041 
31042     //! Fill sequentially all pixel values with specified values \overloading.
31043     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
31044       if (is_empty()) return *this;
31045       T *ptrd, *ptre = end() - 5;
31046       for (ptrd = _data; ptrd<ptre; ) {
31047         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31048       }
31049       ptre+=5;
31050       switch (ptre - ptrd) {
31051       case 5 : *(--ptre) = val4; // fallthrough
31052       case 4 : *(--ptre) = val3; // fallthrough
31053       case 3 : *(--ptre) = val2; // fallthrough
31054       case 2 : *(--ptre) = val1; // fallthrough
31055       case 1 : *(--ptre) = val0; // fallthrough
31056       }
31057       return *this;
31058     }
31059 
31060     //! Fill sequentially all pixel values with specified values \newinstance.
31061     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
31062       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
31063     }
31064 
31065     //! Fill sequentially all pixel values with specified values \overloading.
31066     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31067                   const T& val6) {
31068       if (is_empty()) return *this;
31069       T *ptrd, *ptre = end() - 6;
31070       for (ptrd = _data; ptrd<ptre; ) {
31071         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31072         *(ptrd++) = val6;
31073       }
31074       ptre+=6;
31075       switch (ptre - ptrd) {
31076       case 6 : *(--ptre) = val5; // fallthrough
31077       case 5 : *(--ptre) = val4; // fallthrough
31078       case 4 : *(--ptre) = val3; // fallthrough
31079       case 3 : *(--ptre) = val2; // fallthrough
31080       case 2 : *(--ptre) = val1; // fallthrough
31081       case 1 : *(--ptre) = val0; // fallthrough
31082       }
31083       return *this;
31084     }
31085 
31086     //! Fill sequentially all pixel values with specified values \newinstance.
31087     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31088                      const T& val6) const {
31089       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
31090     }
31091 
31092     //! Fill sequentially all pixel values with specified values \overloading.
31093     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31094                   const T& val6, const T& val7) {
31095       if (is_empty()) return *this;
31096       T *ptrd, *ptre = end() - 7;
31097       for (ptrd = _data; ptrd<ptre; ) {
31098         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
31099         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
31100       }
31101       ptre+=7;
31102       switch (ptre - ptrd) {
31103       case 7 : *(--ptre) = val6; // fallthrough
31104       case 6 : *(--ptre) = val5; // fallthrough
31105       case 5 : *(--ptre) = val4; // fallthrough
31106       case 4 : *(--ptre) = val3; // fallthrough
31107       case 3 : *(--ptre) = val2; // fallthrough
31108       case 2 : *(--ptre) = val1; // fallthrough
31109       case 1 : *(--ptre) = val0; // fallthrough
31110       }
31111       return *this;
31112     }
31113 
31114     //! Fill sequentially all pixel values with specified values \newinstance.
31115     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31116                      const T& val6, const T& val7) const {
31117       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
31118     }
31119 
31120     //! Fill sequentially all pixel values with specified values \overloading.
31121     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31122                   const T& val6, const T& val7, const T& val8) {
31123       if (is_empty()) return *this;
31124       T *ptrd, *ptre = end() - 8;
31125       for (ptrd = _data; ptrd<ptre; ) {
31126         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
31127         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31128         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
31129       }
31130       ptre+=8;
31131       switch (ptre - ptrd) {
31132       case 8 : *(--ptre) = val7; // fallthrough
31133       case 7 : *(--ptre) = val6; // fallthrough
31134       case 6 : *(--ptre) = val5; // fallthrough
31135       case 5 : *(--ptre) = val4; // fallthrough
31136       case 4 : *(--ptre) = val3; // fallthrough
31137       case 3 : *(--ptre) = val2; // fallthrough
31138       case 2 : *(--ptre) = val1; // fallthrough
31139       case 1 : *(--ptre) = val0; // fallthrough
31140       }
31141       return *this;
31142     }
31143 
31144     //! Fill sequentially all pixel values with specified values \newinstance.
31145     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31146                      const T& val6, const T& val7, const T& val8) const {
31147       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
31148     }
31149 
31150     //! Fill sequentially all pixel values with specified values \overloading.
31151     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31152                   const T& val6, const T& val7, const T& val8, const T& val9) {
31153       if (is_empty()) return *this;
31154       T *ptrd, *ptre = end() - 9;
31155       for (ptrd = _data; ptrd<ptre; ) {
31156         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
31157         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
31158       }
31159       ptre+=9;
31160       switch (ptre - ptrd) {
31161       case 9 : *(--ptre) = val8; // fallthrough
31162       case 8 : *(--ptre) = val7; // fallthrough
31163       case 7 : *(--ptre) = val6; // fallthrough
31164       case 6 : *(--ptre) = val5; // fallthrough
31165       case 5 : *(--ptre) = val4; // fallthrough
31166       case 4 : *(--ptre) = val3; // fallthrough
31167       case 3 : *(--ptre) = val2; // fallthrough
31168       case 2 : *(--ptre) = val1; // fallthrough
31169       case 1 : *(--ptre) = val0; // fallthrough
31170       }
31171       return *this;
31172     }
31173 
31174     //! Fill sequentially all pixel values with specified values \newinstance.
31175     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31176                      const T& val6, const T& val7, const T& val8, const T& val9) const {
31177       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
31178     }
31179 
31180     //! Fill sequentially all pixel values with specified values \overloading.
31181     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31182                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
31183       if (is_empty()) return *this;
31184       T *ptrd, *ptre = end() - 10;
31185       for (ptrd = _data; ptrd<ptre; ) {
31186         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
31187         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
31188         *(ptrd++) = val10;
31189       }
31190       ptre+=10;
31191       switch (ptre - ptrd) {
31192       case 10 : *(--ptre) = val9; // fallthrough
31193       case 9 : *(--ptre) = val8; // fallthrough
31194       case 8 : *(--ptre) = val7; // fallthrough
31195       case 7 : *(--ptre) = val6; // fallthrough
31196       case 6 : *(--ptre) = val5; // fallthrough
31197       case 5 : *(--ptre) = val4; // fallthrough
31198       case 4 : *(--ptre) = val3; // fallthrough
31199       case 3 : *(--ptre) = val2; // fallthrough
31200       case 2 : *(--ptre) = val1; // fallthrough
31201       case 1 : *(--ptre) = val0; // fallthrough
31202       }
31203       return *this;
31204     }
31205 
31206     //! Fill sequentially all pixel values with specified values \newinstance.
31207     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31208                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
31209       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
31210     }
31211 
31212     //! Fill sequentially all pixel values with specified values \overloading.
31213     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31214                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
31215       if (is_empty()) return *this;
31216       T *ptrd, *ptre = end() - 11;
31217       for (ptrd = _data; ptrd<ptre; ) {
31218         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31219         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31220       }
31221       ptre+=11;
31222       switch (ptre - ptrd) {
31223       case 11 : *(--ptre) = val10; // fallthrough
31224       case 10 : *(--ptre) = val9; // fallthrough
31225       case 9 : *(--ptre) = val8; // fallthrough
31226       case 8 : *(--ptre) = val7; // fallthrough
31227       case 7 : *(--ptre) = val6; // fallthrough
31228       case 6 : *(--ptre) = val5; // fallthrough
31229       case 5 : *(--ptre) = val4; // fallthrough
31230       case 4 : *(--ptre) = val3; // fallthrough
31231       case 3 : *(--ptre) = val2; // fallthrough
31232       case 2 : *(--ptre) = val1; // fallthrough
31233       case 1 : *(--ptre) = val0; // fallthrough
31234       }
31235       return *this;
31236     }
31237 
31238     //! Fill sequentially all pixel values with specified values \newinstance.
31239     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31240                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
31241       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31242                                                            val11);
31243     }
31244 
31245     //! Fill sequentially all pixel values with specified values \overloading.
31246     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31247                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31248                   const T& val12) {
31249       if (is_empty()) return *this;
31250       T *ptrd, *ptre = end() - 12;
31251       for (ptrd = _data; ptrd<ptre; ) {
31252         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31253         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31254         *(ptrd++) = val12;
31255       }
31256       ptre+=12;
31257       switch (ptre - ptrd) {
31258       case 12 : *(--ptre) = val11; // fallthrough
31259       case 11 : *(--ptre) = val10; // fallthrough
31260       case 10 : *(--ptre) = val9; // fallthrough
31261       case 9 : *(--ptre) = val8; // fallthrough
31262       case 8 : *(--ptre) = val7; // fallthrough
31263       case 7 : *(--ptre) = val6; // fallthrough
31264       case 6 : *(--ptre) = val5; // fallthrough
31265       case 5 : *(--ptre) = val4; // fallthrough
31266       case 4 : *(--ptre) = val3; // fallthrough
31267       case 3 : *(--ptre) = val2; // fallthrough
31268       case 2 : *(--ptre) = val1; // fallthrough
31269       case 1 : *(--ptre) = val0; // fallthrough
31270       }
31271       return *this;
31272     }
31273 
31274     //! Fill sequentially all pixel values with specified values \newinstance.
31275     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31276                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31277                      const T& val12) const {
31278       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31279                                                            val11,val12);
31280     }
31281 
31282     //! Fill sequentially all pixel values with specified values \overloading.
31283     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31284                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31285                   const T& val12, const T& val13) {
31286       if (is_empty()) return *this;
31287       T *ptrd, *ptre = end() - 13;
31288       for (ptrd = _data; ptrd<ptre; ) {
31289         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31290         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31291         *(ptrd++) = val12; *(ptrd++) = val13;
31292       }
31293       ptre+=13;
31294       switch (ptre - ptrd) {
31295       case 13 : *(--ptre) = val12; // fallthrough
31296       case 12 : *(--ptre) = val11; // fallthrough
31297       case 11 : *(--ptre) = val10; // fallthrough
31298       case 10 : *(--ptre) = val9; // fallthrough
31299       case 9 : *(--ptre) = val8; // fallthrough
31300       case 8 : *(--ptre) = val7; // fallthrough
31301       case 7 : *(--ptre) = val6; // fallthrough
31302       case 6 : *(--ptre) = val5; // fallthrough
31303       case 5 : *(--ptre) = val4; // fallthrough
31304       case 4 : *(--ptre) = val3; // fallthrough
31305       case 3 : *(--ptre) = val2; // fallthrough
31306       case 2 : *(--ptre) = val1; // fallthrough
31307       case 1 : *(--ptre) = val0; // fallthrough
31308       }
31309       return *this;
31310     }
31311 
31312     //! Fill sequentially all pixel values with specified values \newinstance.
31313     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31314                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31315                      const T& val12, const T& val13) const {
31316       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31317                                                            val11,val12,val13);
31318     }
31319 
31320     //! Fill sequentially all pixel values with specified values \overloading.
31321     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31322                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31323                   const T& val12, const T& val13, const T& val14) {
31324       if (is_empty()) return *this;
31325       T *ptrd, *ptre = end() - 14;
31326       for (ptrd = _data; ptrd<ptre; ) {
31327         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31328         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31329         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
31330       }
31331       ptre+=14;
31332       switch (ptre - ptrd) {
31333       case 14 : *(--ptre) = val13; // fallthrough
31334       case 13 : *(--ptre) = val12; // fallthrough
31335       case 12 : *(--ptre) = val11; // fallthrough
31336       case 11 : *(--ptre) = val10; // fallthrough
31337       case 10 : *(--ptre) = val9; // fallthrough
31338       case 9 : *(--ptre) = val8; // fallthrough
31339       case 8 : *(--ptre) = val7; // fallthrough
31340       case 7 : *(--ptre) = val6; // fallthrough
31341       case 6 : *(--ptre) = val5; // fallthrough
31342       case 5 : *(--ptre) = val4; // fallthrough
31343       case 4 : *(--ptre) = val3; // fallthrough
31344       case 3 : *(--ptre) = val2; // fallthrough
31345       case 2 : *(--ptre) = val1; // fallthrough
31346       case 1 : *(--ptre) = val0; // fallthrough
31347       }
31348       return *this;
31349     }
31350 
31351     //! Fill sequentially all pixel values with specified values \newinstance.
31352     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31353                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31354                      const T& val12, const T& val13, const T& val14) const {
31355       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31356                                                            val11,val12,val13,val14);
31357     }
31358 
31359     //! Fill sequentially all pixel values with specified values \overloading.
31360     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31361                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31362                   const T& val12, const T& val13, const T& val14, const T& val15) {
31363       if (is_empty()) return *this;
31364       T *ptrd, *ptre = end() - 15;
31365       for (ptrd = _data; ptrd<ptre; ) {
31366         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
31367         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
31368         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
31369       }
31370       ptre+=15;
31371       switch (ptre - ptrd) {
31372       case 15 : *(--ptre) = val14; // fallthrough
31373       case 14 : *(--ptre) = val13; // fallthrough
31374       case 13 : *(--ptre) = val12; // fallthrough
31375       case 12 : *(--ptre) = val11; // fallthrough
31376       case 11 : *(--ptre) = val10; // fallthrough
31377       case 10 : *(--ptre) = val9; // fallthrough
31378       case 9 : *(--ptre) = val8; // fallthrough
31379       case 8 : *(--ptre) = val7; // fallthrough
31380       case 7 : *(--ptre) = val6; // fallthrough
31381       case 6 : *(--ptre) = val5; // fallthrough
31382       case 5 : *(--ptre) = val4; // fallthrough
31383       case 4 : *(--ptre) = val3; // fallthrough
31384       case 3 : *(--ptre) = val2; // fallthrough
31385       case 2 : *(--ptre) = val1; // fallthrough
31386       case 1 : *(--ptre) = val0; // fallthrough
31387       }
31388       return *this;
31389     }
31390 
31391     //! Fill sequentially all pixel values with specified values \newinstance.
31392     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
31393                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
31394                      const T& val12, const T& val13, const T& val14, const T& val15) const {
31395       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
31396                                                            val11,val12,val13,val14,val15);
31397     }
31398 
31399     //! Fill sequentially pixel values according to a given expression.
31400     /**
31401        \param expression C-string describing a math formula, or a sequence of values.
31402        \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
31403        \param allow_formula Tells that mathematical formulas are authorized for the filling.
31404        \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
31405        \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
31406     **/
31407     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
31408                   const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
31409       return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0);
31410     }
31411 
31412     // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula |
31413     //                    2 = allow formula and do not fill image values }.
31414     CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode,
31415                    const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
31416                    const char *const calling_function, const CImg<T> *provides_copy) {
31417       if (is_empty() || !expression || !*expression) return *this;
31418       const unsigned int omode = cimg::exception_mode();
31419       cimg::exception_mode(0);
31420       CImg<charT> is_error;
31421       bool is_value_sequence = false;
31422       cimg_abort_init;
31423 
31424       if (formula_mode) {
31425 
31426         // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
31427         double value;
31428         char sep;
31429         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
31430         if (err==1 || (err==2 && sep==',')) {
31431           if (err==1) { if (formula_mode==2) return *this; return fill((T)value); }
31432           else is_value_sequence = true;
31433         }
31434 
31435         // Try to fill values according to a formula.
31436         _cimg_abort_init_openmp;
31437         if (!is_value_sequence) try {
31438             CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
31439             _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
31440                                                *expression=='*' || *expression==':'),
31441                                  calling_function,base,this,list_inputs,list_outputs,true);
31442             if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
31443                 mp.need_input_copy)
31444               base.assign().assign(*this,false); // Needs input copy
31445 
31446             // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation).
31447             unsigned int M;
31448             if (mp.result_dim) {
31449               M = cimg::max(_width,_height,_depth);
31450               M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height);
31451             } else {
31452               M = cimg::max(_width,_height,_depth,_spectrum);
31453               M = M==_width?cimg::max(_height,_depth,_spectrum):
31454                 M==_height?cimg::max(_width,_depth,_spectrum):
31455                 M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth);
31456             }
31457 
31458             bool do_in_parallel = false;
31459 #if cimg_use_openmp!=0
31460             cimg_openmp_if(*expression=='*' || *expression==':' ||
31461                            (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))
31462               do_in_parallel = true;
31463 #endif
31464             if (mp.result_dim) { // Vector-valued expression
31465               const unsigned int N = std::min(mp.result_dim,_spectrum);
31466               const ulongT whd = (ulongT)_width*_height*_depth;
31467               T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
31468 
31469               if (*expression=='<') {
31470                 CImg<doubleT> res(1,mp.result_dim);
31471                 mp.begin_t();
31472                 cimg_rofYZ(*this,y,z) {
31473                   cimg_abort_test;
31474                   if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0);
31475                   else cimg_rofX(*this,x) {
31476                       mp(x,y,z,0,res._data);
31477                       const double *ptrs = res._data;
31478                       T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
31479                     }
31480                 }
31481                 mp.end_t();
31482 
31483               } else if (*expression=='>' || !do_in_parallel) {
31484                 CImg<doubleT> res(1,mp.result_dim);
31485                 mp.begin_t();
31486                 cimg_forYZ(*this,y,z) {
31487                   cimg_abort_test;
31488                   if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0);
31489                   else cimg_forX(*this,x) {
31490                       mp(x,y,z,0,res._data);
31491                       const double *ptrs = res._data;
31492                       T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
31493                     }
31494                 }
31495                 mp.end_t();
31496 
31497               } else {
31498 
31499 #if cimg_use_openmp!=0
31500                 cimg_pragma_openmp(parallel)
31501                 {
31502                   _cimg_math_parser
31503                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
31504                     &lmp = *_mp;
31505                   lmp.is_fill = true;
31506                   cimg_pragma_openmp(barrier)
31507                   lmp.begin_t();
31508 
31509 #define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \
31510   cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
31511   cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
31512     cimg_abort_test; \
31513     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
31514     else { \
31515       CImg<doubleT> res(1,lmp.result_dim); \
31516       T *__ptrd = data(_sx,_sy,_sz,0); \
31517       const ulongT off = (ulongT)_off; \
31518       cimg_for##_X(*this,_x) { \
31519         lmp(x,y,z,0,res._data); \
31520         const double *ptrs = res._data; \
31521         T *_ptrd = __ptrd; \
31522         for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \
31523         __ptrd+=off; \
31524       } \
31525     } \
31526   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
31527 
31528                   if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
31529                   else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
31530                   else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
31531 
31532                   lmp.end_t();
31533                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
31534                   if (&lmp!=&mp) delete &lmp;
31535                 }
31536 #endif
31537               }
31538 
31539             } else { // Scalar-valued expression
31540               T *ptrd = *expression=='<'?end() - 1:_data;
31541               if (*expression=='<') {
31542                 mp.begin_t();
31543                 if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
31544                 else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
31545                 mp.end_t();
31546 
31547               } else if (*expression=='>' || !do_in_parallel) {
31548                 mp.begin_t();
31549                 if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
31550                 else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
31551                 mp.end_t();
31552 
31553               } else {
31554 
31555 #if cimg_use_openmp!=0
31556                 cimg_pragma_openmp(parallel)
31557                 {
31558                   _cimg_math_parser
31559                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
31560                     &lmp = *_mp;
31561                   lmp.is_fill = true;
31562                   cimg_pragma_openmp(barrier)
31563                   lmp.begin_t();
31564 
31565 #define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \
31566   cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
31567   cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
31568     cimg_abort_test; \
31569     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
31570     else { \
31571       T *_ptrd = data(_sx,_sy,_sz,_sc); \
31572       const ulongT off = (ulongT)_off; \
31573       cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \
31574     } \
31575   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
31576 
31577                   if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
31578                   else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
31579                   else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
31580                   else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
31581 
31582                   lmp.end_t();
31583                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
31584                   if (&lmp!=&mp) delete &lmp;
31585                 }
31586 #endif
31587               }
31588             }
31589             mp.end();
31590           } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
31591       }
31592 
31593       // Try to fill values according to a value sequence.
31594       if (!formula_mode || is_value_sequence || is_error) {
31595         CImg<charT> item(256);
31596         char sep = 0;
31597         const char *nexpression = expression;
31598         ulongT nb = 0;
31599         const ulongT siz = size();
31600         T *ptrd = _data;
31601         for (double val = 0; *nexpression && nb<siz; ++nb) {
31602           sep = 0;
31603           const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
31604           if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
31605             nexpression+=std::strlen(item) + (err>1);
31606             *(ptrd++) = (T)val;
31607           } else break;
31608         }
31609         cimg::exception_mode(omode);
31610         if (nb<siz && (sep || *nexpression)) {
31611           if (is_error) throw CImgArgumentException("%s",is_error._data);
31612           else throw CImgArgumentException(_cimg_instance
31613                                            "%s(): Invalid sequence of filling values '%s'.",
31614                                            cimg_instance,calling_function,expression);
31615         }
31616         if (repeat_values && nb && nb<siz)
31617           for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
31618       }
31619 
31620       cimg::exception_mode(omode);
31621       cimg_abort_test;
31622       return *this;
31623     }
31624 
31625     //! Fill sequentially pixel values according to a given expression \newinstance.
31626     CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
31627                      const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
31628       return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs);
31629     }
31630 
31631     //! Fill sequentially pixel values according to the values found in another image.
31632     /**
31633        \param values Image containing the values used for the filling.
31634        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
31635          repeated for the filling.
31636     **/
31637     template<typename t>
31638     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
31639       if (is_empty() || !values) return *this;
31640       T *ptrd = _data, *ptre = ptrd + size();
31641       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
31642         *(ptrd++) = (T)*ptrs;
31643       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
31644       return *this;
31645     }
31646 
31647     //! Fill sequentially pixel values according to the values found in another image \newinstance.
31648     template<typename t>
31649     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
31650       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
31651         (+*this).fill(values,repeat_values);
31652     }
31653 
31654     //! Fill pixel values along the X-axis at a specified pixel position.
31655     /**
31656        \param y Y-coordinate of the filled column.
31657        \param z Z-coordinate of the filled column.
31658        \param c C-coordinate of the filled column.
31659        \param a0 First fill value.
31660     **/
31661     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
31662 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
31663     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
31664     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
31665     va_end(ap); }
31666       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
31667       return *this;
31668     }
31669 
31670     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
31671     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
31672       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
31673       return *this;
31674     }
31675 
31676     //! Fill pixel values along the Y-axis at a specified pixel position.
31677     /**
31678        \param x X-coordinate of the filled row.
31679        \param z Z-coordinate of the filled row.
31680        \param c C-coordinate of the filled row.
31681        \param a0 First fill value.
31682     **/
31683     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
31684       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
31685       return *this;
31686     }
31687 
31688     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
31689     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
31690       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
31691       return *this;
31692     }
31693 
31694     //! Fill pixel values along the Z-axis at a specified pixel position.
31695     /**
31696        \param x X-coordinate of the filled slice.
31697        \param y Y-coordinate of the filled slice.
31698        \param c C-coordinate of the filled slice.
31699        \param a0 First fill value.
31700     **/
31701     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
31702       const ulongT wh = (ulongT)_width*_height;
31703       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
31704       return *this;
31705     }
31706 
31707     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
31708     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
31709       const ulongT wh = (ulongT)_width*_height;
31710       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
31711       return *this;
31712     }
31713 
31714     //! Fill pixel values along the C-axis at a specified pixel position.
31715     /**
31716        \param x X-coordinate of the filled channel.
31717        \param y Y-coordinate of the filled channel.
31718        \param z Z-coordinate of the filled channel.
31719        \param a0 First filling value.
31720     **/
31721     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
31722       const ulongT whd = (ulongT)_width*_height*_depth;
31723       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
31724       return *this;
31725     }
31726 
31727     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
31728     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
31729       const ulongT whd = (ulongT)_width*_height*_depth;
31730       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
31731       return *this;
31732     }
31733 
31734     //! Discard specified sequence of values in the image buffer, along a specific axis.
31735     /**
31736        \param values Sequence of values to discard.
31737        \param axis Axis along which the values are discarded. If set to \c 0 (default value)
31738          the method does it for all the buffer values and returns a one-column vector.
31739        \note Discarded values will change the image geometry, so the resulting image
31740          is returned as a one-column vector.
31741     **/
31742     template<typename t>
31743     CImg<T>& discard(const CImg<t>& values, const char axis=0) {
31744       if (is_empty() || !values) return *this;
31745       return get_discard(values,axis).move_to(*this);
31746     }
31747 
31748     template<typename t>
31749     CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
31750       if (!values) return +*this;
31751       CImg<T> res;
31752       if (is_empty()) return res;
31753       const ulongT vsiz = values.size();
31754       const char _axis = cimg::lowercase(axis);
31755       ulongT j = 0;
31756       unsigned int k = 0;
31757       int i0 = 0;
31758       res.assign(width(),height(),depth(),spectrum());
31759       switch (_axis) {
31760       case 'x' : {
31761         cimg_forX(*this,i) {
31762           if ((*this)(i)!=(T)values[j]) {
31763             if (j) --i;
31764             res.draw_image(k,get_columns(i0,i));
31765             k+=i - i0 + 1; i0 = i + 1; j = 0;
31766           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31767         }
31768         if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
31769         res.resize(k,-100,-100,-100,0);
31770       } break;
31771       case 'y' : {
31772         cimg_forY(*this,i) {
31773           if ((*this)(0,i)!=(T)values[j]) {
31774             if (j) --i;
31775             res.draw_image(0,k,get_rows(i0,i));
31776             k+=i - i0 + 1; i0 = i + 1; j = 0;
31777           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31778         }
31779         if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
31780         res.resize(-100,k,-100,-100,0);
31781       } break;
31782       case 'z' : {
31783         cimg_forZ(*this,i) {
31784           if ((*this)(0,0,i)!=(T)values[j]) {
31785             if (j) --i;
31786             res.draw_image(0,0,k,get_slices(i0,i));
31787             k+=i - i0 + 1; i0 = i + 1; j = 0;
31788           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31789         }
31790         if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
31791         res.resize(-100,-100,k,-100,0);
31792       } break;
31793       case 'c' : {
31794         cimg_forC(*this,i) {
31795           if ((*this)(0,0,0,i)!=(T)values[j]) {
31796             if (j) --i;
31797             res.draw_image(0,0,0,k,get_channels(i0,i));
31798             k+=i - i0 + 1; i0 = i + 1; j = 0;
31799           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31800         }
31801         if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
31802         res.resize(-100,-100,-100,k,0);
31803       } break;
31804       default : {
31805         const ulongT siz = size();
31806         res.unroll('y');
31807         if (vsiz==1) { // Optimized version for a single discard value
31808           const T val = (T)values[0];
31809           cimg_foroff(*this,i) {
31810             const T _val = (T)_data[i];
31811             if (_val!=val) res[k++] = _val;
31812           }
31813         } else { // Generic version
31814           cimg_foroff(*this,i) {
31815             if ((*this)[i]!=(T)values[j]) {
31816               if (j) --i;
31817               std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
31818               k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
31819             } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
31820           }
31821           if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
31822         }
31823         res.resize(1,k,1,1,0);
31824       }
31825       }
31826       return res;
31827     }
31828 
31829     //! Discard neighboring duplicates in the image buffer, along the specified axis.
31830     CImg<T>& discard(const char axis=0) {
31831       return get_discard(axis).move_to(*this);
31832     }
31833 
31834     //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
31835     CImg<T> get_discard(const char axis=0) const {
31836       CImg<T> res;
31837       if (is_empty()) return res;
31838       const char _axis = cimg::lowercase(axis);
31839       T current = *_data?(T)0:(T)1;
31840       int j = 0;
31841       res.assign(width(),height(),depth(),spectrum());
31842       switch (_axis) {
31843       case 'x' : {
31844         cimg_forX(*this,i)
31845           if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
31846         res.resize(j,-100,-100,-100,0);
31847       } break;
31848       case 'y' : {
31849         cimg_forY(*this,i)
31850           if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
31851         res.resize(-100,j,-100,-100,0);
31852       } break;
31853       case 'z' : {
31854         cimg_forZ(*this,i)
31855           if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
31856         res.resize(-100,-100,j,-100,0);
31857       } break;
31858       case 'c' : {
31859         cimg_forC(*this,i)
31860           if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
31861         res.resize(-100,-100,-100,j,0);
31862       } break;
31863       default : {
31864         res.unroll('y');
31865         cimg_foroff(*this,i) {
31866           const T val = (*this)[i];
31867           if (val!=current) res[j++] = current = val;
31868         }
31869         res.resize(-100,j,-100,-100,0);
31870       }
31871       }
31872       return res;
31873     }
31874 
31875     //! Invert endianness of all pixel values.
31876     /**
31877      **/
31878     CImg<T>& invert_endianness() {
31879       cimg::invert_endianness(_data,size());
31880       return *this;
31881     }
31882 
31883     //! Invert endianness of all pixel values \newinstance.
31884     CImg<T> get_invert_endianness() const {
31885       return (+*this).invert_endianness();
31886     }
31887 
31888     //! Fill image with random values in specified range.
31889     /**
31890        \param val_min Minimal authorized random value.
31891        \param val_max Maximal authorized random value.
31892        \note Random variables are uniformly distributed in [val_min,val_max].
31893      **/
31894     CImg<T>& rand(const T& val_min, const T& val_max) {
31895       const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
31896       if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31897           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31898 
31899 #if cimg_use_openmp!=0
31900           rng+=omp_get_thread_num();
31901 #endif
31902           cimg_pragma_openmp(for)
31903             cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng));
31904           cimg::srand(rng);
31905         } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31906           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31907 
31908 #if cimg_use_openmp!=0
31909           rng+=omp_get_thread_num();
31910 #endif
31911           cimg_pragma_openmp(for)
31912             cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng)));
31913           cimg::srand(rng);
31914         }
31915       return *this;
31916     }
31917 
31918     //! Fill image with random values in specified range \newinstance.
31919     CImg<T> get_rand(const T& val_min, const T& val_max) const {
31920       return (+*this).rand(val_min,val_max);
31921     }
31922 
31923     //! Round pixel values.
31924     /**
31925        \param y Rounding precision.
31926        \param rounding_type Rounding type. Can be:
31927        - \c -1: Backward.
31928        - \c 0: Nearest.
31929        - \c 1: Forward.
31930     **/
31931     CImg<T>& round(const double y=1, const int rounding_type=0) {
31932       if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192);
31933       return *this;
31934     }
31935 
31936     //! Round pixel values \newinstance.
31937     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
31938       return (+*this).round(y,rounding_type);
31939     }
31940 
31941     //! Add random noise to pixel values.
31942     /**
31943        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
31944          global value range.
31945        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
31946          \p 3=Poisson or \p 4=Rician).
31947        \return A reference to the modified image instance.
31948        \note
31949        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
31950          the image value itself.
31951        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
31952        \par Example
31953        \code
31954        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
31955        (img,res.normalize(0,255)).display();
31956        \endcode
31957        \image html ref_noise.jpg
31958     **/
31959     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
31960       if (is_empty()) return *this;
31961       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
31962       Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
31963       if (nsigma==0 && noise_type!=3) return *this;
31964       if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
31965       if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.);
31966       switch (noise_type) {
31967       case 0 : { // Gaussian noise
31968         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31969           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31970 
31971 #if cimg_use_openmp!=0
31972           rng+=omp_get_thread_num();
31973 #endif
31974           cimg_pragma_openmp(for)
31975             cimg_rofoff(*this,off) {
31976             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng));
31977             if (val>vmax) val = vmax;
31978             if (val<vmin) val = vmin;
31979             _data[off] = (T)val;
31980           }
31981           cimg::srand(rng);
31982         }
31983       } break;
31984       case 1 : { // Uniform noise
31985         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31986           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31987 
31988 #if cimg_use_openmp!=0
31989           rng+=omp_get_thread_num();
31990 #endif
31991           cimg_pragma_openmp(for)
31992             cimg_rofoff(*this,off) {
31993             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng));
31994             if (val>vmax) val = vmax;
31995             if (val<vmin) val = vmin;
31996             _data[off] = (T)val;
31997           }
31998           cimg::srand(rng);
31999         }
32000       } break;
32001       case 2 : { // Salt & Pepper noise
32002         if (nsigma<0) nsigma = -nsigma;
32003         if (M==m) {
32004           if (cimg::type<T>::is_float()) { --m; ++M; }
32005           else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
32006         }
32007         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
32008           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
32009 
32010 #if cimg_use_openmp!=0
32011           rng+=omp_get_thread_num();
32012 #endif
32013           cimg_pragma_openmp(for)
32014             cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m);
32015           cimg::srand(rng);
32016           }
32017       } break;
32018       case 3 : { // Poisson Noise
32019         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
32020           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
32021 
32022 #if cimg_use_openmp!=0
32023           rng+=omp_get_thread_num();
32024 #endif
32025           cimg_pragma_openmp(for)
32026             cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng);
32027           cimg::srand(rng);
32028         }
32029       } break;
32030       case 4 : { // Rice noise
32031         const Tfloat sqrt2 = (Tfloat)std::sqrt(2.);
32032         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
32033           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
32034 
32035 #if cimg_use_openmp!=0
32036           rng+=omp_get_thread_num();
32037 #endif
32038           cimg_pragma_openmp(for)
32039             cimg_rofoff(*this,off) {
32040             const Tfloat
32041               val0 = (Tfloat)_data[off]/sqrt2,
32042               re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)),
32043               im = (Tfloat)(val0 + nsigma*cimg::grand(&rng));
32044             Tfloat val = cimg::hypot(re,im);
32045             if (val>vmax) val = vmax;
32046             if (val<vmin) val = vmin;
32047             _data[off] = (T)val;
32048           }
32049           cimg::srand(rng);
32050         }
32051       } break;
32052       default :
32053         throw CImgArgumentException(_cimg_instance
32054                                     "noise(): Invalid specified noise type %d "
32055                                     "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
32056                                     cimg_instance,
32057                                     noise_type);
32058       }
32059       return *this;
32060     }
32061 
32062     //! Add random noise to pixel values \newinstance.
32063     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
32064       return (+*this).noise(sigma,noise_type);
32065     }
32066 
32067     //! Linearly normalize pixel values.
32068     /**
32069        \param min_value Minimum desired value of the resulting image.
32070        \param max_value Maximum desired value of the resulting image.
32071        \param constant_case_ratio In case of instance image having a constant value, tell what ratio
32072               of [min_value,max_value] is used to fill the normalized image
32073               (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2).
32074        \par Example
32075        \code
32076        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
32077        (img,res).display();
32078        \endcode
32079        \image html ref_normalize2.jpg
32080     **/
32081     CImg<T>& normalize(const T& min_value, const T& max_value,
32082                        const float constant_case_ratio=0) {
32083       if (is_empty()) return *this;
32084       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
32085       T m, M = max_min(m);
32086       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
32087       if (m==M)
32088         return fill(constant_case_ratio==0?a:
32089                     constant_case_ratio==1?b:
32090                     (T)((1 - constant_case_ratio)*a + constant_case_ratio*b));
32091       if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
32092       return *this;
32093     }
32094 
32095     //! Linearly normalize pixel values \newinstance.
32096     CImg<Tfloat> get_normalize(const T& min_value, const T& max_value,
32097                                const float ratio_if_constant_image=0) const {
32098       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image);
32099     }
32100 
32101     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
32102     /**
32103        \par Example
32104        \code
32105        const CImg<float> img("reference.jpg"), res = img.get_normalize();
32106        (img,res.normalize(0,255)).display();
32107        \endcode
32108        \image html ref_normalize.jpg
32109     **/
32110     CImg<T>& normalize() {
32111       const ulongT whd = (ulongT)_width*_height*_depth;
32112       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32113                                                                  _height*_depth>=16))
32114       cimg_forYZ(*this,y,z) {
32115         T *ptrd = data(0,y,z,0);
32116         cimg_forX(*this,x) {
32117           const T *ptrs = ptrd;
32118           float n = 0;
32119           cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
32120           n = (float)std::sqrt(n);
32121           T *_ptrd = ptrd++;
32122           if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
32123           else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
32124         }
32125       }
32126       return *this;
32127     }
32128 
32129     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
32130     CImg<Tfloat> get_normalize() const {
32131       return CImg<Tfloat>(*this,false).normalize();
32132     }
32133 
32134     //! Compute Lp-norm of each multi-valued pixel of the image instance.
32135     /**
32136        \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
32137        \par Example
32138        \code
32139        const CImg<float> img("reference.jpg"), res = img.get_norm();
32140        (img,res.normalize(0,255)).display();
32141        \endcode
32142        \image html ref_norm.jpg
32143     **/
32144     CImg<T>& norm(const int norm_type=2) {
32145       if (_spectrum==1 && norm_type) return abs();
32146       return get_norm(norm_type).move_to(*this);
32147     }
32148 
32149     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
32150     CImg<Tfloat> get_norm(const int norm_type=2) const {
32151       if (is_empty()) return *this;
32152       if (_spectrum==1 && norm_type) return get_abs();
32153       const ulongT whd = (ulongT)_width*_height*_depth;
32154       CImg<Tfloat> res(_width,_height,_depth);
32155       switch (norm_type) {
32156       case -1 : { // Linf-norm
32157         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32158                                                                    _height*_depth>=16))
32159         cimg_forYZ(*this,y,z) {
32160           const ulongT off = (ulongT)offset(0,y,z);
32161           const T *ptrs = _data + off;
32162           Tfloat *ptrd = res._data + off;
32163           cimg_forX(*this,x) {
32164             Tfloat n = 0;
32165             const T *_ptrs = ptrs++;
32166             cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
32167             *(ptrd++) = n;
32168           }
32169         }
32170       } break;
32171       case 0 : { // L0-norm
32172         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32173                                                                    _height*_depth>=16))
32174         cimg_forYZ(*this,y,z) {
32175           const ulongT off = (ulongT)offset(0,y,z);
32176           const T *ptrs = _data + off;
32177           Tfloat *ptrd = res._data + off;
32178           cimg_forX(*this,x) {
32179             unsigned int n = 0;
32180             const T *_ptrs = ptrs++;
32181             cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
32182             *(ptrd++) = (Tfloat)n;
32183           }
32184         }
32185       } break;
32186       case 1 : { // L1-norm
32187         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32188                                                                    _height*_depth>=16))
32189         cimg_forYZ(*this,y,z) {
32190           const ulongT off = (ulongT)offset(0,y,z);
32191           const T *ptrs = _data + off;
32192           Tfloat *ptrd = res._data + off;
32193           cimg_forX(*this,x) {
32194             Tfloat n = 0;
32195             const T *_ptrs = ptrs++;
32196             cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
32197             *(ptrd++) = n;
32198           }
32199         }
32200       } break;
32201       case 2 : { // L2-norm
32202         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32203                                                                    _height*_depth>=16))
32204         cimg_forYZ(*this,y,z) {
32205           const ulongT off = (ulongT)offset(0,y,z);
32206           const T *ptrs = _data + off;
32207           Tfloat *ptrd = res._data + off;
32208           cimg_forX(*this,x) {
32209             Tfloat n = 0;
32210             const T *_ptrs = ptrs++;
32211             cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
32212             *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
32213           }
32214         }
32215       } break;
32216       default : { // Linf-norm
32217         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
32218                                                                    _height*_depth>=16))
32219         cimg_forYZ(*this,y,z) {
32220           const ulongT off = (ulongT)offset(0,y,z);
32221           const T *ptrs = _data + off;
32222           Tfloat *ptrd = res._data + off;
32223           cimg_forX(*this,x) {
32224             Tfloat n = 0;
32225             const T *_ptrs = ptrs++;
32226             cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
32227             *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
32228           }
32229         }
32230       }
32231       }
32232       return res;
32233     }
32234 
32235     //! Cut pixel values in specified range.
32236     /**
32237        \param min_value Minimum desired value of the resulting image.
32238        \param max_value Maximum desired value of the resulting image.
32239        \par Example
32240        \code
32241        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
32242        (img,res).display();
32243        \endcode
32244        \image html ref_cut.jpg
32245     **/
32246     CImg<T>& cut(const T& min_value, const T& max_value) {
32247       if (is_empty()) return *this;
32248       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
32249       cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768);
32250       return *this;
32251     }
32252 
32253     //! Cut pixel values in specified range \newinstance.
32254     CImg<T> get_cut(const T& min_value, const T& max_value) const {
32255       return (+*this).cut(min_value,max_value);
32256     }
32257 
32258     //! Uniformly quantize pixel values.
32259     /**
32260        \param nb_levels Number of quantization levels.
32261        \param keep_range Tells if resulting values keep the same range as the original ones.
32262        \par Example
32263        \code
32264        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
32265        (img,res).display();
32266        \endcode
32267        \image html ref_quantize.jpg
32268     **/
32269     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
32270       if (!nb_levels)
32271         throw CImgArgumentException(_cimg_instance
32272                                     "quantize(): Invalid quantization request with 0 values.",
32273                                     cimg_instance);
32274 
32275       if (is_empty()) return *this;
32276       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
32277       if (range>0) {
32278         if (keep_range)
32279           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
32280           cimg_rofoff(*this,off) {
32281             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
32282             _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
32283           } else
32284           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
32285           cimg_rofoff(*this,off) {
32286             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
32287             _data[off] = (T)std::min(val,nb_levels - 1);
32288           }
32289       }
32290       return *this;
32291     }
32292 
32293     //! Uniformly quantize pixel values \newinstance.
32294     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
32295       return (+*this).quantize(n,keep_range);
32296     }
32297 
32298     //! Threshold pixel values.
32299     /**
32300        \param value Threshold value
32301        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
32302        \param strict_threshold Tells if threshold value is strict.
32303        \par Example
32304        \code
32305        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
32306        (img,res.normalize(0,255)).display();
32307        \endcode
32308        \image html ref_threshold.jpg
32309     **/
32310     CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
32311       if (is_empty()) return *this;
32312       if (strict_threshold) {
32313         if (soft_threshold)
32314           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
32315           cimg_rofoff(*this,off) {
32316             const T v = _data[off];
32317             _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
32318           }
32319         else
32320           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
32321           cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
32322       } else {
32323         if (soft_threshold)
32324           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
32325           cimg_rofoff(*this,off) {
32326             const T v = _data[off];
32327             _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
32328           }
32329         else
32330           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
32331           cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
32332       }
32333       return *this;
32334     }
32335 
32336     //! Threshold pixel values \newinstance.
32337     CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
32338       return (+*this).threshold(value,soft_threshold,strict_threshold);
32339     }
32340 
32341     //! Compute the histogram of pixel values.
32342     /**
32343        \param nb_levels Number of desired histogram levels.
32344        \param min_value Minimum pixel value considered for the histogram computation.
32345          All pixel values lower than \p min_value will not be counted.
32346        \param max_value Maximum pixel value considered for the histogram computation.
32347          All pixel values higher than \p max_value will not be counted.
32348        \note
32349        - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x
32350          in the image I.
32351        - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional.
32352        \par Example
32353        \code
32354        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
32355        img.display_graph(0,3);
32356        \endcode
32357        \image html ref_histogram.jpg
32358     **/
32359     CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
32360       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
32361     }
32362 
32363     //! Compute the histogram of pixel values \overloading.
32364     CImg<T>& histogram(const unsigned int nb_levels) {
32365       return get_histogram(nb_levels).move_to(*this);
32366     }
32367 
32368     //! Compute the histogram of pixel values \newinstance.
32369     CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
32370       if (!nb_levels || is_empty()) return CImg<ulongT>();
32371       const double
32372         vmin = (double)(min_value<max_value?min_value:max_value),
32373         vmax = (double)(min_value<max_value?max_value:min_value);
32374       CImg<ulongT> res(nb_levels,1,1,1,0);
32375       cimg_rof(*this,ptrs,T) {
32376         const T val = *ptrs;
32377         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
32378       }
32379       return res;
32380     }
32381 
32382     //! Compute the histogram of pixel values \newinstance.
32383     CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
32384       if (!nb_levels || is_empty()) return CImg<ulongT>();
32385       T vmax = 0, vmin = min_max(vmax);
32386       return get_histogram(nb_levels,vmin,vmax);
32387     }
32388 
32389     //! Equalize histogram of pixel values.
32390     /**
32391        \param nb_levels Number of histogram levels used for the equalization.
32392        \param min_value Minimum pixel value considered for the histogram computation.
32393          All pixel values lower than \p min_value will not be counted.
32394        \param max_value Maximum pixel value considered for the histogram computation.
32395          All pixel values higher than \p max_value will not be counted.
32396        \par Example
32397        \code
32398        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
32399        (img,res).display();
32400        \endcode
32401        \image html ref_equalize.jpg
32402     **/
32403     CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
32404       if (!nb_levels || is_empty()) return *this;
32405       const T
32406         vmin = min_value<max_value?min_value:max_value,
32407         vmax = min_value<max_value?max_value:min_value;
32408       CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
32409       ulongT cumul = 0;
32410       cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
32411       if (!cumul) cumul = 1;
32412       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576))
32413       cimg_rofoff(*this,off) {
32414         const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin));
32415         if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul);
32416       }
32417       return *this;
32418     }
32419 
32420     //! Equalize histogram of pixel values \overloading.
32421     CImg<T>& equalize(const unsigned int nb_levels) {
32422       if (!nb_levels || is_empty()) return *this;
32423       T vmax = 0, vmin = min_max(vmax);
32424       return equalize(nb_levels,vmin,vmax);
32425     }
32426 
32427     //! Equalize histogram of pixel values \newinstance.
32428     CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
32429       return (+*this).equalize(nblevels,val_min,val_max);
32430     }
32431 
32432     //! Equalize histogram of pixel values \newinstance.
32433     CImg<T> get_equalize(const unsigned int nblevels) const {
32434       return (+*this).equalize(nblevels);
32435     }
32436 
32437     //! Index multi-valued pixels regarding to a specified colormap.
32438     /**
32439        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
32440        \param dithering Level of dithering (0=disable, 1=standard level).
32441        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
32442        \note
32443        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
32444        \par Example
32445        \code
32446        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
32447        const CImg<float> res = img.get_index(colormap,1,true);
32448        (img,res).display();
32449        \endcode
32450        \image html ref_index.jpg
32451     **/
32452     template<typename t>
32453     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
32454       return get_index(colormap,dithering,map_indexes).move_to(*this);
32455     }
32456 
32457     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
32458     template<typename t>
32459     CImg<typename CImg<t>::Tuint>
32460     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
32461       if (colormap._spectrum!=_spectrum)
32462         throw CImgArgumentException(_cimg_instance
32463                                     "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
32464                                     "have incompatible dimensions.",
32465                                     cimg_instance,
32466                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
32467 
32468       typedef typename CImg<t>::Tuint tuint;
32469       if (is_empty()) return CImg<tuint>();
32470       const ulongT
32471         whd = (ulongT)_width*_height*_depth,
32472         pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
32473       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
32474       if (dithering>0) { // Dithered versions
32475         tuint *ptrd = res._data;
32476         const float ndithering = cimg::cut(dithering,0,1)/16;
32477         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
32478         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
32479         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
32480         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
32481         const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
32482         switch (_spectrum) {
32483         case 1 : { // Optimized for scalars
32484           cimg_forYZ(*this,y,z) {
32485             if (y<height() - 2) {
32486               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
32487               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
32488             }
32489             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
32490             cimg_forX(*this,x) {
32491               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
32492               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32493               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
32494                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
32495                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32496               }
32497               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
32498               *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
32499               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32500             }
32501             cimg::swap(cache_current,cache_next);
32502           }
32503         } break;
32504         case 2 : { // Optimized for 2D vectors
32505           tuint *ptrd1 = ptrd + whd;
32506           cimg_forYZ(*this,y,z) {
32507             if (y<height() - 2) {
32508               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
32509               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
32510               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
32511             }
32512             Tfloat
32513               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
32514               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
32515             cimg_forX(*this,x) {
32516               const Tfloat
32517                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
32518                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
32519               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32520               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32521                 const Tfloat
32522                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
32523                   dist = pval0*pval0 + pval1*pval1;
32524                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32525               }
32526               const t *const ptrmin1 = ptrmin0 + pwhd;
32527               const Tfloat
32528                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
32529                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
32530               *ptrs0+=7*err0; *ptrs1+=7*err1;
32531               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
32532               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
32533               *ptrsn0+=err0; *ptrsn1+=err1;
32534               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
32535               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32536             }
32537             cimg::swap(cache_current,cache_next);
32538           }
32539         } break;
32540         case 3 : { // Optimized for 3D vectors (colors)
32541           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
32542           cimg_forYZ(*this,y,z) {
32543             if (y<height() - 2) {
32544               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
32545               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
32546               cimg_forX(*this,x) {
32547                 *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
32548               }
32549             }
32550             Tfloat
32551               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
32552               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
32553             cimg_forX(*this,x) {
32554               const Tfloat
32555                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
32556                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
32557                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
32558               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32559               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
32560                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32561                 const Tfloat
32562                   pval0 = (Tfloat)*(ptrp0++) - val0,
32563                   pval1 = (Tfloat)*(ptrp1++) - val1,
32564                   pval2 = (Tfloat)*(ptrp2++) - val2,
32565                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
32566                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32567               }
32568               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
32569               const Tfloat
32570                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
32571                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
32572                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
32573 
32574               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
32575               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
32576               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
32577               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
32578 
32579               if (map_indexes) {
32580                 *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
32581               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32582             }
32583             cimg::swap(cache_current,cache_next);
32584           }
32585         } break;
32586         default : // Generic version
32587           cimg_forYZ(*this,y,z) {
32588             if (y<height() - 2) {
32589               Tfloat *ptrc = cache_next;
32590               cimg_forC(*this,c) {
32591                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
32592                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
32593                 ptrc+=cwhd;
32594               }
32595             }
32596             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
32597             cimg_forX(*this,x) {
32598               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
32599               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
32600                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
32601                 cimg_forC(*this,c) {
32602                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
32603                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
32604                 }
32605                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
32606               }
32607               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
32608               cimg_forC(*this,c) {
32609                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
32610                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
32611                 _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
32612               }
32613               if (map_indexes) {
32614                 tuint *_ptrd = ptrd++;
32615                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
32616               }
32617               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
32618             }
32619             cimg::swap(cache_current,cache_next);
32620           }
32621         }
32622       } else { // Non-dithered versions
32623         switch (_spectrum) {
32624         case 1 : { // Optimized for scalars
32625           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32626                                                                      _height*_depth>=16 && pwhd>=16))
32627           cimg_forYZ(*this,y,z) {
32628             tuint *ptrd = res.data(0,y,z);
32629             for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32630               const Tfloat val0 = (Tfloat)*(ptrs0++);
32631               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32632               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
32633                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
32634                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32635               }
32636               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32637             }
32638           }
32639         } break;
32640         case 2 : { // Optimized for 2D vectors
32641           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32642                                                                      _height*_depth>=16 && pwhd>=16))
32643           cimg_forYZ(*this,y,z) {
32644             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
32645             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32646               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
32647               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32648               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32649                 const Tfloat
32650                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
32651                   dist = pval0*pval0 + pval1*pval1;
32652                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32653               }
32654               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
32655               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32656             }
32657           }
32658         } break;
32659         case 3 : { // Optimized for 3D vectors (colors)
32660           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32661                                                                      _height*_depth>=16 && pwhd>=16))
32662           cimg_forYZ(*this,y,z) {
32663             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
32664             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
32665                    *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
32666               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
32667               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
32668               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
32669                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
32670                 const Tfloat
32671                   pval0 = (Tfloat)*(ptrp0++) - val0,
32672                   pval1 = (Tfloat)*(ptrp1++) - val1,
32673                   pval2 = (Tfloat)*(ptrp2++) - val2,
32674                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
32675                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
32676               }
32677               if (map_indexes) {
32678                 *(ptrd++) = (tuint)*ptrmin0;
32679                 *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
32680                 *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
32681               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
32682             }
32683           }
32684         } break;
32685         default : // Generic version
32686           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
32687                                                                      _height*_depth>=16 && pwhd>=16))
32688           cimg_forYZ(*this,y,z) {
32689             tuint *ptrd = res.data(0,y,z);
32690             for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
32691               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
32692               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
32693                 Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
32694                 cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
32695                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
32696               }
32697               if (map_indexes) {
32698                 tuint *_ptrd = ptrd++;
32699                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
32700               }
32701               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
32702             }
32703           }
32704         }
32705       }
32706       return res;
32707     }
32708 
32709     //! Map predefined colormap on the scalar (indexed) image instance.
32710     /**
32711        \param colormap Multi-valued colormap used for mapping the indexes.
32712        \param boundary_conditions Boundary conditions.
32713          Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
32714        \par Example
32715        \code
32716        const CImg<float> img("reference.jpg"),
32717                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
32718                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
32719                          res = img.get_index(colormap1,0).map(colormap2);
32720        (img,res).display();
32721        \endcode
32722        \image html ref_map.jpg
32723     **/
32724     template<typename t>
32725     CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
32726       return get_map(colormap,boundary_conditions).move_to(*this);
32727     }
32728 
32729     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
32730     template<typename t>
32731     CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
32732       const ulongT
32733         whd = (ulongT)_width*_height*_depth, siz = size(),
32734         cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
32735         cwhd2 = 2*cwhd;
32736       CImg<t> res(_width,_height,_depth,_spectrum*colormap._spectrum);
32737       switch (colormap._spectrum) {
32738 
32739       case 1 : { // Optimized for scalars
32740         switch (boundary_conditions) {
32741         case 3 : // Mirror
32742           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32743           for (longT off = 0; off<(longT)siz; ++off) {
32744             const ulongT ind = ((ulongT)_data[off])%cwhd2;
32745             res[off] = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
32746           }
32747           break;
32748         case 2 : // Periodic
32749           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32750           for (longT off = 0; off<(longT)siz; ++off) {
32751             const ulongT ind = (ulongT)_data[off];
32752             res[off] = colormap[ind%cwhd];
32753           }
32754           break;
32755         case 1 : // Neumann
32756           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32757           for (longT off = 0; off<(longT)siz; ++off) {
32758             const longT ind = (longT)_data[off];
32759             res[off] = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
32760           } break;
32761         default : // Dirichlet
32762           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32763           for (longT off = 0; off<(longT)siz; ++off) {
32764             const ulongT ind = (ulongT)_data[off];
32765             res[off] = ind<cwhd?colormap[ind]:(t)0;
32766           }
32767         }
32768       } break;
32769 
32770       case 2 : { // Optimized for 2D vectors
32771         const t *const ptrp0 = colormap._data, *const ptrp1 = ptrp0 + cwhd;
32772         switch (boundary_conditions) {
32773         case 3 : // Mirror
32774           cimg_forC(*this,c) {
32775             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32776             const T *const ptrs = data(0,0,0,c);
32777             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32778             for (longT off = 0; off<(longT)whd; ++off) {
32779               const ulongT
32780                 _ind = ((ulongT)ptrs[off])%cwhd2,
32781                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32782               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32783             }
32784           } break;
32785         case 2 : // Periodic
32786           cimg_forC(*this,c) {
32787             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32788             const T *const ptrs = data(0,0,0,c);
32789             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32790             for (longT off = 0; off<(longT)whd; ++off) {
32791               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32792               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32793             }
32794           } break;
32795         case 1 : // Neumann
32796           cimg_forC(*this,c) {
32797             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32798             const T *const ptrs = data(0,0,0,c);
32799             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32800             for (longT off = 0; off<(longT)whd; ++off) {
32801               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32802               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32803             }
32804           } break;
32805         default : // Dirichlet
32806           cimg_forC(*this,c) {
32807             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32808             const T *const ptrs = data(0,0,0,c);
32809             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32810             for (longT off = 0; off<(longT)whd; ++off) {
32811               const ulongT ind = (ulongT)ptrs[off];
32812               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; }
32813               else ptrd0[off] = ptrd1[off] = (t)0;
32814             }
32815           }
32816         }
32817       } break;
32818 
32819       case 3 : { // Optimized for 3D vectors (colors)
32820         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp0 + 2*cwhd;
32821         switch (boundary_conditions) {
32822         case 3 : // Mirror
32823           cimg_forC(*this,c) {
32824             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32825             const T *const ptrs = data(0,0,0,c);
32826             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32827             for (longT off = 0; off<(longT)whd; ++off) {
32828               const ulongT
32829                 _ind = ((ulongT)ptrs[off])%cwhd2,
32830                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32831               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32832             }
32833           } break;
32834         case 2 : // Periodic
32835           cimg_forC(*this,c) {
32836             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32837             const T *const ptrs = data(0,0,0,c);
32838             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32839             for (longT off = 0; off<(longT)whd; ++off) {
32840               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32841               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32842             }
32843           } break;
32844         case 1 : // Neumann
32845           cimg_forC(*this,c) {
32846             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32847             const T *const ptrs = data(0,0,0,c);
32848             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32849             for (longT off = 0; off<(longT)whd; ++off) {
32850               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32851               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32852             }
32853           } break;
32854         default : // Dirichlet
32855           cimg_forC(*this,c) {
32856             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32857             const T *const ptrs = data(0,0,0,c);
32858             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32859             for (longT off = 0; off<(longT)whd; ++off) {
32860               const ulongT ind = (ulongT)ptrs[off];
32861               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind]; }
32862               else ptrd0[off] = ptrd1[off] = ptrd2[off] = (t)0;
32863             }
32864           }
32865         }
32866       } break;
32867 
32868       default : { // Generic version
32869         switch (boundary_conditions) {
32870         case 3 : // Mirror
32871           cimg_forC(*this,c) {
32872             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32873             const T *const ptrs = data(0,0,0,c);
32874             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32875             for (longT off = 0; off<(longT)whd; ++off) {
32876               const ulongT
32877                 _ind = ((ulongT)ptrs[off])%cwhd,
32878                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32879               t *const _ptrd = ptrd + off;
32880               const t *const ptrp = &colormap[ind];
32881               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32882             }
32883           } break;
32884         case 2 : // Periodic
32885           cimg_forC(*this,c) {
32886             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32887             const T *const ptrs = data(0,0,0,c);
32888             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32889             for (longT off = 0; off<(longT)whd; ++off) {
32890               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32891               t *const _ptrd = ptrd + off;
32892               const t *const ptrp = &colormap[ind];
32893               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32894             }
32895           } break;
32896         case 1 : // Neumann
32897           cimg_forC(*this,c) {
32898             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32899             const T *const ptrs = data(0,0,0,c);
32900             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32901             for (longT off = 0; off<(longT)whd; ++off) {
32902               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32903               t *const _ptrd = ptrd + off;
32904               const t *const ptrp = &colormap[ind];
32905               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32906             }
32907           } break;
32908         default : // Dirichlet
32909           cimg_forC(*this,c) {
32910             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32911             const T *const ptrs = data(0,0,0,c);
32912             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32913             for (longT off = 0; off<(longT)whd; ++off) {
32914               const ulongT ind = (ulongT)ptrs[off];
32915               t *const _ptrd = ptrd + off;
32916               if (ind<cwhd) {
32917                 const t *const ptrp = &colormap[ind];
32918                 cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32919               } else cimg_forC(colormap,k) _ptrd[k*whd] = (t)0;
32920             }
32921           }
32922         }
32923       }
32924       }
32925       return res;
32926     }
32927 
32928     //! Label connected components.
32929     /**
32930        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
32931        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
32932        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32933        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32934        \note The algorithm of connected components computation has been primarily done
32935        by A. Meijster, according to the publication:
32936        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
32937        In: Science of Computer Programming 41 (2001), pp. 173--194'.
32938        The submitted code has then been modified to fit CImg coding style and constraints.
32939     **/
32940     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32941                    const bool is_L2_norm=true) {
32942       if (is_empty()) return *this;
32943       return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this);
32944     }
32945 
32946     //! Label connected components \newinstance.
32947     CImg<ulongT> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32948                            const bool is_L2_norm=true) const {
32949       if (is_empty()) return CImg<ulongT>();
32950 
32951       // Create neighborhood tables.
32952       int dx[13], dy[13], dz[13], nb = 0;
32953       dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
32954       dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
32955       if (is_high_connectivity) {
32956         dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
32957         dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
32958       }
32959       if (_depth>1) { // 3D version
32960         dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
32961         if (is_high_connectivity) {
32962           dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
32963           dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
32964           dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
32965           dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
32966 
32967           dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
32968           dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
32969           dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
32970           dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
32971         }
32972       }
32973       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
32974     }
32975 
32976     //! Label connected components \overloading.
32977     /**
32978        \param connectivity_mask Mask of the neighboring pixels.
32979        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32980        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32981     **/
32982     template<typename t>
32983     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32984                    const bool is_L2_norm=true) {
32985       if (is_empty()) return *this;
32986       return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this);
32987     }
32988 
32989     //! Label connected components \newinstance.
32990     template<typename t>
32991     CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32992                            const bool is_L2_norm=true) const {
32993       if (is_empty()) return CImg<ulongT>();
32994       int nb = 0;
32995       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
32996       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
32997       nb = 0;
32998       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
32999                                                connectivity_mask(x,y,z)) {
33000         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
33001       }
33002       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
33003     }
33004 
33005     CImg<ulongT> _label(const unsigned int nb, const int *const dx,
33006                         const int *const dy, const int *const dz,
33007                         const Tfloat tolerance, const bool is_L2_norm) const {
33008       CImg<ulongT> res(_width,_height,_depth);
33009       const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance;
33010 
33011       // Init label numbers.
33012       ulongT *ptr = res.data();
33013       cimg_foroff(res,p) *(ptr++) = p;
33014 
33015       // For each neighbour-direction, label.
33016       for (unsigned int n = 0; n<nb; ++n) {
33017         const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
33018         if (_dx || _dy || _dz) {
33019           const int
33020             x0 = _dx<0?-_dx:0,
33021             x1 = _dx<0?width():width() - _dx,
33022             y0 = _dy<0?-_dy:0,
33023             y1 = _dy<0?height():height() - _dy,
33024             z0 = _dz<0?-_dz:0,
33025             z1 = _dz<0?depth():depth() - _dz;
33026           const longT
33027             wh = (longT)width()*height(),
33028             whd = (longT)width()*height()*depth(),
33029             offset = _dz*wh + _dy*width() + _dx;
33030           for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
33031             for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
33032               for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
33033                 Tfloat diff;
33034                 switch (_spectrum) {
33035                 case 1 :
33036                   diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd));
33037                   break;
33038                 case 2 :
33039                   if (is_L2_norm)
33040                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33041                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
33042                   else
33043                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33044                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
33045                   break;
33046                 case 3 :
33047                   if (is_L2_norm)
33048                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33049                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
33050                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
33051                   else
33052                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33053                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
33054                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
33055                   break;
33056                 case 4 :
33057                   if (is_L2_norm)
33058                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33059                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
33060                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
33061                       cimg::sqr((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
33062                   else
33063                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
33064                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
33065                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
33066                       cimg::abs((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
33067                   break;
33068                 default :
33069                   diff = 0;
33070                   if (is_L2_norm)
33071                     cimg_forC(*this,c)
33072                       diff+=cimg::sqr((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
33073                   else
33074                     cimg_forC(*this,c)
33075                       diff+=cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
33076                 }
33077 
33078                 if (diff<=_tolerance) {
33079                   const longT q = p + offset;
33080                   ulongT xk, yk;
33081                   for (xk = (ulongT)(p<q?q:p), yk = (ulongT)(p<q?p:q); xk!=yk && res[xk]!=xk; ) {
33082                     xk = res[xk]; if (xk<yk) cimg::swap(xk,yk);
33083                   }
33084                   if (xk!=yk) res[xk] = (ulongT)yk;
33085                   for (ulongT _p = (ulongT)p; _p!=yk; ) {
33086                     const ulongT h = res[_p];
33087                     res[_p] = (ulongT)yk;
33088                     _p = h;
33089                   }
33090                   for (ulongT _q = (ulongT)q; _q!=yk; ) {
33091                     const ulongT h = res[_q];
33092                     res[_q] = (ulongT)yk;
33093                     _q = h;
33094                   }
33095                 }
33096               }
33097             }
33098           }
33099         }
33100       }
33101 
33102       // Resolve equivalences.
33103       ulongT counter = 0;
33104       ptr = res.data();
33105       cimg_foroff(res,p) { *ptr = *ptr==p?counter++:res[*ptr]; ++ptr; }
33106       return res;
33107     }
33108 
33109     // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
33110     CImg<T>& _system_strescape() {
33111 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
33112       move_to(list); \
33113       CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
33114       CImgList<T> list;
33115       const T *ptrs = _data;
33116       cimg_for(*this,p,T) switch ((int)*p) {
33117         cimg_system_strescape('\\',"\\\\");
33118         cimg_system_strescape('\"',"\\\"");
33119         cimg_system_strescape('!',"\"\\!\"");
33120         cimg_system_strescape('`',"\\`");
33121         cimg_system_strescape('$',"\\$");
33122       }
33123       if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
33124       return (list>'x').move_to(*this);
33125     }
33126 
33127     //@}
33128     //---------------------------------
33129     //
33130     //! \name Color Base Management
33131     //@{
33132     //---------------------------------
33133 
33134     //! Return colormap \e "default", containing 256 colors entries in RGB.
33135     /**
33136        \return The following \c 256x1x1x3 colormap is returned:
33137        \image html ref_colormap_default.jpg
33138     **/
33139     static const CImg<Tuchar>& default_LUT256() {
33140       static CImg<Tuchar> colormap;
33141       cimg::mutex(8);
33142       if (!colormap) {
33143         colormap.assign(1,256,1,3);
33144         for (unsigned int index = 0, r = 16; r<256; r+=32)
33145           for (unsigned int g = 16; g<256; g+=32)
33146             for (unsigned int b = 32; b<256; b+=64) {
33147               colormap(0,index,0) = (Tuchar)r;
33148               colormap(0,index,1) = (Tuchar)g;
33149               colormap(0,index++,2) = (Tuchar)b;
33150             }
33151       }
33152       cimg::mutex(8,0);
33153       return colormap;
33154     }
33155 
33156     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
33157     /**
33158        \return The following \c 256x1x1x3 colormap is returned:
33159        \image html ref_colormap_hsv.jpg
33160     **/
33161     static const CImg<Tuchar>& HSV_LUT256() {
33162       static CImg<Tuchar> colormap;
33163       cimg::mutex(8);
33164       if (!colormap) {
33165         CImg<Tint> tmp(1,256,1,3,1);
33166         tmp.get_shared_channel(0).sequence(0,359);
33167         colormap = tmp.HSVtoRGB();
33168       }
33169       cimg::mutex(8,0);
33170       return colormap;
33171     }
33172 
33173     //! Return colormap \e "lines", containing 256 colors entries in RGB.
33174     /**
33175        \return The following \c 256x1x1x3 colormap is returned:
33176        \image html ref_colormap_lines.jpg
33177     **/
33178     static const CImg<Tuchar>& lines_LUT256() {
33179       static const unsigned char pal[] = {
33180         0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255,
33181         53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125,
33182         206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138,
33183         101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125,
33184         16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0,
33185         97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121,
33186         227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85,
33187         210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255,
33188         20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49,
33189         210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121,
33190         166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190,
33191         85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81,
33192         247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0,
33193         170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81,
33194         215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121,
33195         0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125,
33196         0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219,
33197         251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239,
33198         89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57,
33199         202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125,
33200         32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142,
33201         8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65,
33202         194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210,
33203         154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85,
33204         146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125,
33205         0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49,
33206         89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125,
33207         69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69,
33208         40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194,
33209         125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0,
33210         125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93,
33211         16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85,
33212         0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97,
33213         255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49,
33214         162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130,
33215         36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125,
33216         219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125,
33217         202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210,
33218         85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255,
33219         97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255,
33220         85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239,
33221         89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166,
33222         0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0,
33223         174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69,
33224         162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81,
33225         158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97,
33226         57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4,
33227         73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 };
33228       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
33229       return colormap;
33230     }
33231 
33232     //! Return colormap \e "hot", containing 256 colors entries in RGB.
33233     /**
33234        \return The following \c 256x1x1x3 colormap is returned:
33235        \image html ref_colormap_hot.jpg
33236     **/
33237     static const CImg<Tuchar>& hot_LUT256() {
33238       static CImg<Tuchar> colormap;
33239       cimg::mutex(8);
33240       if (!colormap) {
33241         colormap.assign(1,4,1,3,(T)0);
33242         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
33243         colormap.resize(1,256,1,3,3);
33244       }
33245       cimg::mutex(8,0);
33246       return colormap;
33247     }
33248 
33249     //! Return colormap \e "cool", containing 256 colors entries in RGB.
33250     /**
33251        \return The following \c 256x1x1x3 colormap is returned:
33252        \image html ref_colormap_cool.jpg
33253     **/
33254     static const CImg<Tuchar>& cool_LUT256() {
33255       static CImg<Tuchar> colormap;
33256       cimg::mutex(8);
33257       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);
33258       cimg::mutex(8,0);
33259       return colormap;
33260     }
33261 
33262     //! Return colormap \e "jet", containing 256 colors entries in RGB.
33263     /**
33264        \return The following \c 256x1x1x3 colormap is returned:
33265        \image html ref_colormap_jet.jpg
33266     **/
33267     static const CImg<Tuchar>& jet_LUT256() {
33268       static CImg<Tuchar> colormap;
33269       cimg::mutex(8);
33270       if (!colormap) {
33271         colormap.assign(1,4,1,3,(T)0);
33272         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
33273         colormap.resize(1,256,1,3,3);
33274       }
33275       cimg::mutex(8,0);
33276       return colormap;
33277     }
33278 
33279     //! Return colormap \e "flag", containing 256 colors entries in RGB.
33280     /**
33281        \return The following \c 256x1x1x3 colormap is returned:
33282        \image html ref_colormap_flag.jpg
33283     **/
33284     static const CImg<Tuchar>& flag_LUT256() {
33285       static CImg<Tuchar> colormap;
33286       cimg::mutex(8);
33287       if (!colormap) {
33288         colormap.assign(1,4,1,3,(T)0);
33289         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
33290         colormap.resize(1,256,1,3,0,2);
33291       }
33292       cimg::mutex(8,0);
33293       return colormap;
33294     }
33295 
33296     //! Return colormap \e "cube", containing 256 colors entries in RGB.
33297     /**
33298        \return The following \c 256x1x1x3 colormap is returned:
33299        \image html ref_colormap_cube.jpg
33300     **/
33301     static const CImg<Tuchar>& cube_LUT256() {
33302       static CImg<Tuchar> colormap;
33303       cimg::mutex(8);
33304       if (!colormap) {
33305         colormap.assign(1,8,1,3,(T)0);
33306         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
33307           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
33308           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
33309         colormap.resize(1,256,1,3,3);
33310       }
33311       cimg::mutex(8,0);
33312       return colormap;
33313     }
33314 
33315     //! Convert pixel values from sRGB to RGB color spaces.
33316     CImg<T>& sRGBtoRGB() {
33317       if (is_empty()) return *this;
33318       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
33319       cimg_rofoff(*this,off) {
33320         const Tfloat
33321           sval = (Tfloat)_data[off]/255,
33322           val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
33323         _data[off] = (T)cimg::cut(val*255,0,255);
33324       }
33325       return *this;
33326     }
33327 
33328     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
33329     CImg<Tfloat> get_sRGBtoRGB() const {
33330       return CImg<Tfloat>(*this,false).sRGBtoRGB();
33331     }
33332 
33333     //! Convert pixel values from RGB to sRGB color spaces.
33334     CImg<T>& RGBtosRGB() {
33335       if (is_empty()) return *this;
33336       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
33337       cimg_rofoff(*this,off) {
33338         const Tfloat
33339           val = (Tfloat)_data[off]/255,
33340           sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
33341         _data[off] = (T)cimg::cut(sval*255,0,255);
33342       }
33343       return *this;
33344     }
33345 
33346     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
33347     CImg<Tfloat> get_RGBtosRGB() const {
33348       return CImg<Tfloat>(*this,false).RGBtosRGB();
33349     }
33350 
33351     //! Convert pixel values from RGB to HSI color spaces.
33352     CImg<T>& RGBtoHSI() {
33353       if (_spectrum!=3)
33354         throw CImgInstanceException(_cimg_instance
33355                                     "RGBtoHSI(): Instance is not a RGB image.",
33356                                     cimg_instance);
33357 
33358       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33359       const longT whd = (longT)width()*height()*depth();
33360       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33361       for (longT N = 0; N<whd; ++N) {
33362         const Tfloat
33363           R = (Tfloat)p1[N],
33364           G = (Tfloat)p2[N],
33365           B = (Tfloat)p3[N],
33366           m = cimg::min(R,G,B),
33367           M = cimg::max(R,G,B),
33368           C = M - m,
33369           sum = R + G + B,
33370           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33371           S = sum<=0?0:1 - 3*m/sum,
33372           I = sum/(3*255);
33373         p1[N] = (T)H;
33374         p2[N] = (T)S;
33375         p3[N] = (T)I;
33376       }
33377       return *this;
33378     }
33379 
33380     //! Convert pixel values from RGB to HSI color spaces \newinstance.
33381     CImg<Tfloat> get_RGBtoHSI() const {
33382       return CImg<Tfloat>(*this,false).RGBtoHSI();
33383     }
33384 
33385     //! Convert pixel values from HSI to RGB color spaces.
33386     CImg<T>& HSItoRGB() {
33387       if (_spectrum!=3)
33388         throw CImgInstanceException(_cimg_instance
33389                                     "HSItoRGB(): Instance is not a HSI image.",
33390                                     cimg_instance);
33391 
33392       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33393       const longT whd = (longT)width()*height()*depth();
33394       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33395       for (longT N = 0; N<whd; ++N) {
33396         const Tfloat
33397           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33398           S = (Tfloat)p2[N],
33399           I = (Tfloat)p3[N],
33400           Z = 1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1),
33401           C = I*S/(1 + Z),
33402           X = C*Z,
33403           m = I*(1 - S)/3;
33404         Tfloat R, G, B;
33405         switch ((int)H) {
33406         case 0 : R = C; G = X; B = 0; break;
33407         case 1 : R = X; G = C; B = 0; break;
33408         case 2 : R = 0; G = C; B = X; break;
33409         case 3 : R = 0; G = X; B = C; break;
33410         case 4 : R = X; G = 0; B = C; break;
33411         default : R = C; G = 0; B = X;
33412         }
33413         p1[N] = (T)((R + m)*3*255);
33414         p2[N] = (T)((G + m)*3*255);
33415         p3[N] = (T)((B + m)*3*255);
33416       }
33417       return *this;
33418     }
33419 
33420     //! Convert pixel values from HSI to RGB color spaces \newinstance.
33421     CImg<Tfloat> get_HSItoRGB() const {
33422       return CImg< Tuchar>(*this,false).HSItoRGB();
33423     }
33424 
33425     //! Convert pixel values from RGB to HSL color spaces.
33426     CImg<T>& RGBtoHSL() {
33427       if (_spectrum!=3)
33428         throw CImgInstanceException(_cimg_instance
33429                                     "RGBtoHSL(): Instance is not a RGB image.",
33430                                     cimg_instance);
33431 
33432       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33433       const longT whd = (longT)width()*height()*depth();
33434       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33435       for (longT N = 0; N<whd; ++N) {
33436         const Tfloat
33437           R = (Tfloat)p1[N],
33438           G = (Tfloat)p2[N],
33439           B = (Tfloat)p3[N],
33440           m = cimg::min(R,G,B),
33441           M = cimg::max(R,G,B),
33442           C = M - m,
33443           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33444           L = 0.5f*(m + M)/255,
33445           S = L==1 || L==0?0:C/(1 - cimg::abs(2*L - 1))/255;
33446         p1[N] = (T)H;
33447         p2[N] = (T)S;
33448         p3[N] = (T)L;
33449       }
33450       return *this;
33451     }
33452 
33453     //! Convert pixel values from RGB to HSL color spaces \newinstance.
33454     CImg<Tfloat> get_RGBtoHSL() const {
33455       return CImg<Tfloat>(*this,false).RGBtoHSL();
33456     }
33457 
33458     //! Convert pixel values from HSL to RGB color spaces.
33459     CImg<T>& HSLtoRGB() {
33460       if (_spectrum!=3)
33461         throw CImgInstanceException(_cimg_instance
33462                                     "HSLtoRGB(): Instance is not a HSL image.",
33463                                     cimg_instance);
33464 
33465       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33466       const longT whd = (longT)width()*height()*depth();
33467       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33468       for (longT N = 0; N<whd; ++N) {
33469         const Tfloat
33470           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33471           S = (Tfloat)p2[N],
33472           L = (Tfloat)p3[N],
33473           C = (1 - cimg::abs(2*L - 1))*S,
33474           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
33475           m = L - C/2;
33476         Tfloat R, G, B;
33477         switch ((int)H) {
33478         case 0 : R = C; G = X; B = 0; break;
33479         case 1 : R = X; G = C; B = 0; break;
33480         case 2 : R = 0; G = C; B = X; break;
33481         case 3 : R = 0; G = X; B = C; break;
33482         case 4 : R = X; G = 0; B = C; break;
33483         default : R = C; G = 0; B = X;
33484         }
33485         p1[N] = (T)((R + m)*255);
33486         p2[N] = (T)((G + m)*255);
33487         p3[N] = (T)((B + m)*255);
33488       }
33489       return *this;
33490     }
33491 
33492     //! Convert pixel values from HSL to RGB color spaces \newinstance.
33493     CImg<Tuchar> get_HSLtoRGB() const {
33494       return CImg<Tuchar>(*this,false).HSLtoRGB();
33495     }
33496 
33497     //! Convert pixel values from RGB to HSV color spaces.
33498     CImg<T>& RGBtoHSV() {
33499       if (_spectrum!=3)
33500         throw CImgInstanceException(_cimg_instance
33501                                     "RGBtoHSV(): Instance is not a RGB image.",
33502                                     cimg_instance);
33503 
33504       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33505       const longT whd = (longT)width()*height()*depth();
33506       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33507       for (longT N = 0; N<whd; ++N) {
33508         const Tfloat
33509           R = (Tfloat)p1[N],
33510           G = (Tfloat)p2[N],
33511           B = (Tfloat)p3[N],
33512           M = cimg::max(R,G,B),
33513           C = M - cimg::min(R,G,B),
33514           H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
33515           S = M<=0?0:C/M;
33516         p1[N] = (T)H;
33517         p2[N] = (T)S;
33518         p3[N] = (T)(M/255);
33519       }
33520       return *this;
33521     }
33522 
33523     //! Convert pixel values from RGB to HSV color spaces \newinstance.
33524     CImg<Tfloat> get_RGBtoHSV() const {
33525       return CImg<Tfloat>(*this,false).RGBtoHSV();
33526     }
33527 
33528     //! Convert pixel values from HSV to RGB color spaces.
33529     CImg<T>& HSVtoRGB() {
33530       if (_spectrum!=3)
33531         throw CImgInstanceException(_cimg_instance
33532                                     "HSVtoRGB(): Instance is not a HSV image.",
33533                                     cimg_instance);
33534 
33535       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33536       const longT whd = (longT)width()*height()*depth();
33537       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
33538       for (longT N = 0; N<whd; ++N) {
33539         Tfloat
33540           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
33541           S = (Tfloat)p2[N],
33542           V = (Tfloat)p3[N],
33543           C = V*S,
33544           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
33545           m = V - C;
33546         Tfloat R, G, B;
33547         switch ((int)H) {
33548         case 0 : R = C; G = X; B = 0; break;
33549         case 1 : R = X; G = C; B = 0; break;
33550         case 2 : R = 0; G = C; B = X; break;
33551         case 3 : R = 0; G = X; B = C; break;
33552         case 4 : R = X; G = 0; B = C; break;
33553         default : R = C; G = 0; B = X;
33554         }
33555         p1[N] = (T)((R + m)*255);
33556         p2[N] = (T)((G + m)*255);
33557         p3[N] = (T)((B + m)*255);
33558       }
33559       return *this;
33560     }
33561 
33562     //! Convert pixel values from HSV to RGB color spaces \newinstance.
33563     CImg<Tuchar> get_HSVtoRGB() const {
33564       return CImg<Tuchar>(*this,false).HSVtoRGB();
33565     }
33566 
33567     //! Convert pixel values from RGB to YCbCr color spaces.
33568     CImg<T>& RGBtoYCbCr() {
33569       if (_spectrum!=3)
33570         throw CImgInstanceException(_cimg_instance
33571                                     "RGBtoYCbCr(): Instance is not a RGB image.",
33572                                     cimg_instance);
33573 
33574       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33575       const longT whd = (longT)width()*height()*depth();
33576       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
33577       for (longT N = 0; N<whd; ++N) {
33578         const Tfloat
33579           R = (Tfloat)p1[N],
33580           G = (Tfloat)p2[N],
33581           B = (Tfloat)p3[N],
33582           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
33583           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
33584           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
33585         p1[N] = (T)cimg::cut(Y,0,255),
33586         p2[N] = (T)cimg::cut(Cb,0,255),
33587         p3[N] = (T)cimg::cut(Cr,0,255);
33588       }
33589       return *this;
33590     }
33591 
33592     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
33593     CImg<Tuchar> get_RGBtoYCbCr() const {
33594       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
33595     }
33596 
33597     //! Convert pixel values from RGB to YCbCr color spaces.
33598     CImg<T>& YCbCrtoRGB() {
33599       if (_spectrum!=3)
33600         throw CImgInstanceException(_cimg_instance
33601                                     "YCbCrtoRGB(): Instance is not a YCbCr image.",
33602                                     cimg_instance);
33603 
33604       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33605       const longT whd = (longT)width()*height()*depth();
33606       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
33607       for (longT N = 0; N<whd; ++N) {
33608         const Tfloat
33609           Y = (Tfloat)p1[N] - 16,
33610           Cb = (Tfloat)p2[N] - 128,
33611           Cr = (Tfloat)p3[N] - 128,
33612           R = (298*Y + 409*Cr + 128)/256,
33613           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
33614           B = (298*Y + 516*Cb + 128)/256;
33615         p1[N] = (T)cimg::cut(R,0,255),
33616         p2[N] = (T)cimg::cut(G,0,255),
33617         p3[N] = (T)cimg::cut(B,0,255);
33618       }
33619       return *this;
33620     }
33621 
33622     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
33623     CImg<Tuchar> get_YCbCrtoRGB() const {
33624       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
33625     }
33626 
33627     //! Convert pixel values from RGB to YUV color spaces.
33628     CImg<T>& RGBtoYUV() {
33629       if (_spectrum!=3)
33630         throw CImgInstanceException(_cimg_instance
33631                                     "RGBtoYUV(): Instance is not a RGB image.",
33632                                     cimg_instance);
33633 
33634       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33635       const longT whd = (longT)width()*height()*depth();
33636       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
33637       for (longT N = 0; N<whd; ++N) {
33638         const Tfloat
33639           R = (Tfloat)p1[N]/255,
33640           G = (Tfloat)p2[N]/255,
33641           B = (Tfloat)p3[N]/255,
33642           Y = 0.299f*R + 0.587f*G + 0.114f*B;
33643         p1[N] = (T)Y;
33644         p2[N] = (T)(0.492f*(B - Y));
33645         p3[N] = (T)(0.877*(R - Y));
33646       }
33647       return *this;
33648     }
33649 
33650     //! Convert pixel values from RGB to YUV color spaces \newinstance.
33651     CImg<Tfloat> get_RGBtoYUV() const {
33652       return CImg<Tfloat>(*this,false).RGBtoYUV();
33653     }
33654 
33655     //! Convert pixel values from YUV to RGB color spaces.
33656     CImg<T>& YUVtoRGB() {
33657       if (_spectrum!=3)
33658         throw CImgInstanceException(_cimg_instance
33659                                     "YUVtoRGB(): Instance is not a YUV image.",
33660                                     cimg_instance);
33661 
33662       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33663       const longT whd = (longT)width()*height()*depth();
33664       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
33665       for (longT N = 0; N<whd; ++N) {
33666         const Tfloat
33667           Y = (Tfloat)p1[N],
33668           U = (Tfloat)p2[N],
33669           V = (Tfloat)p3[N],
33670           R = (Y + 1.140f*V)*255,
33671           G = (Y - 0.395f*U - 0.581f*V)*255,
33672           B = (Y + 2.032f*U)*255;
33673         p1[N] = (T)cimg::cut(R,0,255),
33674         p2[N] = (T)cimg::cut(G,0,255),
33675         p3[N] = (T)cimg::cut(B,0,255);
33676       }
33677       return *this;
33678     }
33679 
33680     //! Convert pixel values from YUV to RGB color spaces \newinstance.
33681     CImg<Tuchar> get_YUVtoRGB() const {
33682       return CImg< Tuchar>(*this,false).YUVtoRGB();
33683     }
33684 
33685     //! Convert pixel values from RGB to CMY color spaces.
33686     CImg<T>& RGBtoCMY() {
33687       if (_spectrum!=3)
33688         throw CImgInstanceException(_cimg_instance
33689                                     "RGBtoCMY(): Instance is not a RGB image.",
33690                                     cimg_instance);
33691 
33692       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33693       const longT whd = (longT)width()*height()*depth();
33694       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33695       for (longT N = 0; N<whd; ++N) {
33696         const Tfloat
33697           R = (Tfloat)p1[N],
33698           G = (Tfloat)p2[N],
33699           B = (Tfloat)p3[N],
33700           C = 255 - R,
33701           M = 255 - G,
33702           Y = 255 - B;
33703         p1[N] = (T)cimg::cut(C,0,255),
33704         p2[N] = (T)cimg::cut(M,0,255),
33705         p3[N] = (T)cimg::cut(Y,0,255);
33706       }
33707       return *this;
33708     }
33709 
33710     //! Convert pixel values from RGB to CMY color spaces \newinstance.
33711     CImg<Tuchar> get_RGBtoCMY() const {
33712       return CImg<Tfloat>(*this,false).RGBtoCMY();
33713     }
33714 
33715     //! Convert pixel values from CMY to RGB color spaces.
33716     CImg<T>& CMYtoRGB() {
33717       if (_spectrum!=3)
33718         throw CImgInstanceException(_cimg_instance
33719                                     "CMYtoRGB(): Instance is not a CMY image.",
33720                                     cimg_instance);
33721 
33722       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33723       const longT whd = (longT)width()*height()*depth();
33724       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33725       for (longT N = 0; N<whd; ++N) {
33726         const Tfloat
33727           C = (Tfloat)p1[N],
33728           M = (Tfloat)p2[N],
33729           Y = (Tfloat)p3[N],
33730           R = 255 - C,
33731           G = 255 - M,
33732           B = 255 - Y;
33733         p1[N] = (T)cimg::cut(R,0,255),
33734         p2[N] = (T)cimg::cut(G,0,255),
33735         p3[N] = (T)cimg::cut(B,0,255);
33736       }
33737       return *this;
33738     }
33739 
33740     //! Convert pixel values from CMY to RGB color spaces \newinstance.
33741     CImg<Tuchar> get_CMYtoRGB() const {
33742       return CImg<Tuchar>(*this,false).CMYtoRGB();
33743     }
33744 
33745     //! Convert pixel values from CMY to CMYK color spaces.
33746     CImg<T>& CMYtoCMYK() {
33747       return get_CMYtoCMYK().move_to(*this);
33748     }
33749 
33750     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
33751     CImg<Tuchar> get_CMYtoCMYK() const {
33752       if (_spectrum!=3)
33753         throw CImgInstanceException(_cimg_instance
33754                                     "CMYtoCMYK(): Instance is not a CMY image.",
33755                                     cimg_instance);
33756 
33757       CImg<Tfloat> res(_width,_height,_depth,4);
33758       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
33759       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);
33760       const longT whd = (longT)width()*height()*depth();
33761       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33762       for (longT N = 0; N<whd; ++N) {
33763         Tfloat
33764           C = (Tfloat)ps1[N],
33765           M = (Tfloat)ps2[N],
33766           Y = (Tfloat)ps3[N],
33767           K = cimg::min(C,M,Y);
33768         if (K>=255) C = M = Y = 0;
33769         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
33770         pd1[N] = (Tfloat)cimg::cut(C,0,255),
33771         pd2[N] = (Tfloat)cimg::cut(M,0,255),
33772         pd3[N] = (Tfloat)cimg::cut(Y,0,255),
33773         pd4[N] = (Tfloat)cimg::cut(K,0,255);
33774       }
33775       return res;
33776     }
33777 
33778     //! Convert pixel values from CMYK to CMY color spaces.
33779     CImg<T>& CMYKtoCMY() {
33780       return get_CMYKtoCMY().move_to(*this);
33781     }
33782 
33783     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
33784     CImg<Tfloat> get_CMYKtoCMY() const {
33785       if (_spectrum!=4)
33786         throw CImgInstanceException(_cimg_instance
33787                                     "CMYKtoCMY(): Instance is not a CMYK image.",
33788                                     cimg_instance);
33789 
33790       CImg<Tfloat> res(_width,_height,_depth,3);
33791       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);
33792       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
33793       const longT whd = (longT)width()*height()*depth();
33794       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33795       for (longT N = 0; N<whd; ++N) {
33796         const Tfloat
33797           C = (Tfloat)ps1[N],
33798           M = (Tfloat)ps2[N],
33799           Y = (Tfloat)ps3[N],
33800           K = (Tfloat)ps4[N],
33801           K1 = 1 - K/255,
33802           nC = C*K1 + K,
33803           nM = M*K1 + K,
33804           nY = Y*K1 + K;
33805         pd1[N] = (Tfloat)cimg::cut(nC,0,255),
33806         pd2[N] = (Tfloat)cimg::cut(nM,0,255),
33807         pd3[N] = (Tfloat)cimg::cut(nY,0,255);
33808       }
33809       return res;
33810     }
33811 
33812     //! Convert pixel values from RGB to XYZ color spaces.
33813     /**
33814        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33815     **/
33816     CImg<T>& RGBtoXYZ(const bool use_D65=true) {
33817       if (_spectrum!=3)
33818         throw CImgInstanceException(_cimg_instance
33819                                     "RGBtoXYZ(): Instance is not a RGB image.",
33820                                     cimg_instance);
33821 
33822       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33823       const longT whd = (longT)width()*height()*depth();
33824       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33825       for (longT N = 0; N<whd; ++N) {
33826         const Tfloat
33827           R = (Tfloat)p1[N]/255,
33828           G = (Tfloat)p2[N]/255,
33829           B = (Tfloat)p3[N]/255;
33830         if (use_D65) { // D65
33831           p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
33832           p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
33833           p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
33834         } else { // D50
33835           p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
33836           p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
33837           p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
33838         }
33839       }
33840       return *this;
33841     }
33842 
33843     //! Convert pixel values from RGB to XYZ color spaces \newinstance.
33844     CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
33845       return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
33846     }
33847 
33848     //! Convert pixel values from XYZ to RGB color spaces.
33849     /**
33850        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33851     **/
33852     CImg<T>& XYZtoRGB(const bool use_D65=true) {
33853       if (_spectrum!=3)
33854         throw CImgInstanceException(_cimg_instance
33855                                     "XYZtoRGB(): Instance is not a XYZ image.",
33856                                     cimg_instance);
33857 
33858       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33859       const longT whd = (longT)width()*height()*depth();
33860       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33861       for (longT N = 0; N<whd; ++N) {
33862         const Tfloat
33863           X = (Tfloat)p1[N]*255,
33864           Y = (Tfloat)p2[N]*255,
33865           Z = (Tfloat)p3[N]*255;
33866         if (use_D65) {
33867           p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
33868           p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
33869           p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
33870         } else {
33871           p1[N] = (T)cimg::cut(3.134274799724*X  - 1.617275708956*Y - 0.490724283042*Z,0,255);
33872           p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
33873           p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
33874         }
33875       }
33876       return *this;
33877     }
33878 
33879     //! Convert pixel values from XYZ to RGB color spaces \newinstance.
33880     CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
33881       return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
33882     }
33883 
33884     //! Convert pixel values from XYZ to Lab color spaces.
33885     CImg<T>& XYZtoLab(const bool use_D65=true) {
33886 #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
33887 
33888       if (_spectrum!=3)
33889         throw CImgInstanceException(_cimg_instance
33890                                     "XYZtoLab(): Instance is not a XYZ image.",
33891                                     cimg_instance);
33892       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33893       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33894       const longT whd = (longT)width()*height()*depth();
33895       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33896       for (longT N = 0; N<whd; ++N) {
33897         const Tfloat
33898           X = (Tfloat)(p1[N]/white[0]),
33899           Y = (Tfloat)(p2[N]/white[1]),
33900           Z = (Tfloat)(p3[N]/white[2]),
33901           fX = (Tfloat)_cimg_Labf(X),
33902           fY = (Tfloat)_cimg_Labf(Y),
33903           fZ = (Tfloat)_cimg_Labf(Z);
33904         p1[N] = (T)cimg::cut(116*fY - 16,0,100);
33905         p2[N] = (T)(500*(fX - fY));
33906         p3[N] = (T)(200*(fY - fZ));
33907       }
33908       return *this;
33909     }
33910 
33911     //! Convert pixel values from XYZ to Lab color spaces \newinstance.
33912     CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
33913       return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
33914     }
33915 
33916     //! Convert pixel values from Lab to XYZ color spaces.
33917     CImg<T>& LabtoXYZ(const bool use_D65=true) {
33918       if (_spectrum!=3)
33919         throw CImgInstanceException(_cimg_instance
33920                                     "LabtoXYZ(): Instance is not a Lab image.",
33921                                     cimg_instance);
33922       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33923       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33924       const longT whd = (longT)width()*height()*depth();
33925       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33926       for (longT N = 0; N<whd; ++N) {
33927         const Tfloat
33928           L = (Tfloat)p1[N],
33929           a = (Tfloat)p2[N],
33930           b = (Tfloat)p3[N],
33931           cY = (L + 16)/116,
33932           cZ = cY - b/200,
33933           cX = a/500 + cY,
33934           X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
33935           Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
33936           Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
33937         p1[N] = (T)(X*white[0]);
33938         p2[N] = (T)(Y*white[1]);
33939         p3[N] = (T)(Z*white[2]);
33940       }
33941       return *this;
33942     }
33943 
33944     //! Convert pixel values from Lab to XYZ color spaces \newinstance.
33945     CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
33946       return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
33947     }
33948 
33949     //! Convert pixel values from XYZ to xyY color spaces.
33950     CImg<T>& XYZtoxyY() {
33951       if (_spectrum!=3)
33952         throw CImgInstanceException(_cimg_instance
33953                                     "XYZtoxyY(): Instance is not a XYZ image.",
33954                                     cimg_instance);
33955 
33956       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33957       const longT whd = (longT)width()*height()*depth();
33958       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33959       for (longT N = 0; N<whd; ++N) {
33960         const Tfloat
33961           X = (Tfloat)p1[N],
33962           Y = (Tfloat)p2[N],
33963           Z = (Tfloat)p3[N],
33964           sum = X + Y + Z,
33965           nsum = sum>0?sum:1;
33966         p1[N] = (T)(X/nsum);
33967         p2[N] = (T)(Y/nsum);
33968         p3[N] = (T)Y;
33969       }
33970       return *this;
33971     }
33972 
33973     //! Convert pixel values from XYZ to xyY color spaces \newinstance.
33974     CImg<Tfloat> get_XYZtoxyY() const {
33975       return CImg<Tfloat>(*this,false).XYZtoxyY();
33976     }
33977 
33978     //! Convert pixel values from xyY pixels to XYZ color spaces.
33979     CImg<T>& xyYtoXYZ() {
33980       if (_spectrum!=3)
33981         throw CImgInstanceException(_cimg_instance
33982                                     "xyYtoXYZ(): Instance is not a xyY image.",
33983                                     cimg_instance);
33984 
33985       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33986       const longT whd = (longT)width()*height()*depth();
33987       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33988       for (longT N = 0; N<whd; ++N) {
33989         const Tfloat
33990          px = (Tfloat)p1[N],
33991          py = (Tfloat)p2[N],
33992          Y = (Tfloat)p3[N],
33993          ny = py>0?py:1;
33994         p1[N] = (T)(px*Y/ny);
33995         p2[N] = (T)Y;
33996         p3[N] = (T)((1 - px - py)*Y/ny);
33997       }
33998       return *this;
33999     }
34000 
34001     //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
34002     CImg<Tfloat> get_xyYtoXYZ() const {
34003       return CImg<Tfloat>(*this,false).xyYtoXYZ();
34004     }
34005 
34006     //! Convert pixel values from RGB to Lab color spaces.
34007     CImg<T>& RGBtoLab(const bool use_D65=true) {
34008       return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
34009     }
34010 
34011     //! Convert pixel values from RGB to Lab color spaces \newinstance.
34012     CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
34013       return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
34014     }
34015 
34016     //! Convert pixel values from Lab to RGB color spaces.
34017     CImg<T>& LabtoRGB(const bool use_D65=true) {
34018       return LabtoXYZ().XYZtoRGB(use_D65);
34019     }
34020 
34021     //! Convert pixel values from Lab to RGB color spaces \newinstance.
34022     CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
34023       return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
34024     }
34025 
34026     //! Convert pixel values from RGB to xyY color spaces.
34027     CImg<T>& RGBtoxyY(const bool use_D65=true) {
34028       return RGBtoXYZ(use_D65).XYZtoxyY();
34029     }
34030 
34031     //! Convert pixel values from RGB to xyY color spaces \newinstance.
34032     CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
34033       return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
34034     }
34035 
34036     //! Convert pixel values from xyY to RGB color spaces.
34037     CImg<T>& xyYtoRGB(const bool use_D65=true) {
34038       return xyYtoXYZ().XYZtoRGB(use_D65);
34039     }
34040 
34041     //! Convert pixel values from xyY to RGB color spaces \newinstance.
34042     CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
34043       return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
34044     }
34045 
34046     //! Convert pixel values from RGB to CMYK color spaces.
34047     CImg<T>& RGBtoCMYK() {
34048       return RGBtoCMY().CMYtoCMYK();
34049     }
34050 
34051     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
34052     CImg<Tfloat> get_RGBtoCMYK() const {
34053       return CImg<Tfloat>(*this,false).RGBtoCMYK();
34054     }
34055 
34056     //! Convert pixel values from CMYK to RGB color spaces.
34057     CImg<T>& CMYKtoRGB() {
34058       return CMYKtoCMY().CMYtoRGB();
34059     }
34060 
34061     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
34062     CImg<Tuchar> get_CMYKtoRGB() const {
34063       return CImg<Tuchar>(*this,false).CMYKtoRGB();
34064     }
34065 
34066     //@}
34067     //------------------------------------------
34068     //
34069     //! \name Geometric / Spatial Manipulation
34070     //@{
34071     //------------------------------------------
34072 
34073     static float _cimg_lanczos(const float x) {
34074       if (x<=-2 || x>=2) return 0;
34075       const float a = (float)cimg::PI*x, b = 0.5f*a;
34076       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
34077     }
34078 
34079     //! Resize image to new dimensions.
34080     /**
34081        \param size_x Number of columns (new size along the X-axis).
34082        \param size_y Number of rows (new size along the Y-axis).
34083        \param size_z Number of slices (new size along the Z-axis).
34084        \param size_c Number of vector-channels (new size along the C-axis).
34085        \param interpolation_type Method of interpolation:
34086        - -1 = no interpolation: raw memory resizing.
34087        - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
34088        - 1 = nearest-neighbor interpolation.
34089        - 2 = moving average interpolation.
34090        - 3 = linear interpolation.
34091        - 4 = grid interpolation.
34092        - 5 = cubic interpolation.
34093        - 6 = lanczos interpolation.
34094        \param boundary_conditions Type of boundary conditions used if necessary.
34095        \param centering_x Set centering type (only if \p interpolation_type=0).
34096        \param centering_y Set centering type (only if \p interpolation_type=0).
34097        \param centering_z Set centering type (only if \p interpolation_type=0).
34098        \param centering_c Set centering type (only if \p interpolation_type=0).
34099        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
34100     **/
34101     CImg<T>& resize(const int size_x, const int size_y=-100,
34102                     const int size_z=-100, const int size_c=-100,
34103                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
34104                     const float centering_x = 0, const float centering_y = 0,
34105                     const float centering_z = 0, const float centering_c = 0) {
34106       if (!size_x || !size_y || !size_z || !size_c) return assign();
34107       const unsigned int
34108         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
34109         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
34110         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
34111         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
34112         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
34113       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
34114       if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
34115       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
34116         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
34117         return *this;
34118       }
34119       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
34120                         centering_x,centering_y,centering_z,centering_c).move_to(*this);
34121     }
34122 
34123     //! Resize image to new dimensions \newinstance.
34124     CImg<T> get_resize(const int size_x, const int size_y = -100,
34125                        const int size_z = -100, const int size_c = -100,
34126                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
34127                        const float centering_x = 0, const float centering_y = 0,
34128                        const float centering_z = 0, const float centering_c = 0) const {
34129       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
34130           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
34131         throw CImgArgumentException(_cimg_instance
34132                                     "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
34133                                     cimg_instance,
34134                                     centering_x,centering_y,centering_z,centering_c);
34135 
34136       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
34137       const unsigned int
34138         sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
34139         sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
34140         sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
34141         sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
34142       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
34143       if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
34144       CImg<T> res;
34145       switch (interpolation_type) {
34146 
34147         // Raw resizing.
34148         //
34149       case -1 :
34150         std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
34151         break;
34152 
34153         // No interpolation.
34154         //
34155       case 0 : {
34156         const int
34157           xc = (int)(centering_x*((int)sx - width())),
34158           yc = (int)(centering_y*((int)sy - height())),
34159           zc = (int)(centering_z*((int)sz - depth())),
34160           cc = (int)(centering_c*((int)sc - spectrum()));
34161 
34162         switch (boundary_conditions) {
34163         case 3 : { // Mirror
34164           res.assign(sx,sy,sz,sc);
34165           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
34166           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
34167           cimg_forXYZC(res,x,y,z,c) {
34168             const int
34169               mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
34170               mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
34171             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
34172                                    my<height()?my:h2 - my - 1,
34173                                    mz<depth()?mz:d2 - mz - 1,
34174                                    mc<spectrum()?mc:s2 - mc - 1);
34175           }
34176         } break;
34177         case 2 : { // Periodic
34178           res.assign(sx,sy,sz,sc);
34179           const int
34180             x0 = ((int)xc%width()) - width(),
34181             y0 = ((int)yc%height()) - height(),
34182             z0 = ((int)zc%depth()) - depth(),
34183             c0 = ((int)cc%spectrum()) - spectrum(),
34184             dx = width(), dy = height(), dz = depth(), dc = spectrum();
34185           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
34186           for (int c = c0; c<(int)sc; c+=dc)
34187             for (int z = z0; z<(int)sz; z+=dz)
34188               for (int y = y0; y<(int)sy; y+=dy)
34189                 for (int x = x0; x<(int)sx; x+=dx)
34190                   res.draw_image(x,y,z,c,*this);
34191         } break;
34192         case 1 : { // Neumann
34193           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
34194           CImg<T> sprite;
34195           if (xc>0) {  // X-backward
34196             res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
34197             for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
34198           }
34199           if (xc + width()<(int)sx) { // X-forward
34200             res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
34201                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
34202             for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
34203           }
34204           if (yc>0) {  // Y-backward
34205             res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
34206             for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
34207           }
34208           if (yc + height()<(int)sy) { // Y-forward
34209             res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
34210                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
34211             for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
34212           }
34213           if (zc>0) {  // Z-backward
34214             res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
34215             for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
34216           }
34217           if (zc + depth()<(int)sz) { // Z-forward
34218             res.get_crop(0,0,zc  +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
34219             for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
34220           }
34221           if (cc>0) {  // C-backward
34222             res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
34223             for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
34224           }
34225           if (cc + spectrum()<(int)sc) { // C-forward
34226             res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
34227             for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
34228           }
34229         } break;
34230         default : // Dirichlet
34231           res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
34232         }
34233         break;
34234       } break;
34235 
34236         // Nearest neighbor interpolation.
34237         //
34238       case 1 : {
34239         res.assign(sx,sy,sz,sc);
34240         CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
34241         const ulongT
34242           wh = (ulongT)_width*_height,
34243           whd = (ulongT)_width*_height*_depth,
34244           sxy = (ulongT)sx*sy,
34245           sxyz = (ulongT)sx*sy*sz,
34246           one = (ulongT)1;
34247         if (sx==_width) off_x.fill(1);
34248         else {
34249           ulongT *poff_x = off_x._data, curr = 0;
34250           cimg_forX(res,x) {
34251             const ulongT old = curr;
34252             curr = (x + one)*_width/sx;
34253             *(poff_x++) = curr - old;
34254           }
34255         }
34256         if (sy==_height) off_y.fill(_width);
34257         else {
34258           ulongT *poff_y = off_y._data, curr = 0;
34259           cimg_forY(res,y) {
34260             const ulongT old = curr;
34261             curr = (y + one)*_height/sy;
34262             *(poff_y++) = _width*(curr - old);
34263           }
34264           *poff_y = 0;
34265         }
34266         if (sz==_depth) off_z.fill(wh);
34267         else {
34268           ulongT *poff_z = off_z._data, curr = 0;
34269           cimg_forZ(res,z) {
34270             const ulongT old = curr;
34271             curr = (z + one)*_depth/sz;
34272             *(poff_z++) = wh*(curr - old);
34273           }
34274           *poff_z = 0;
34275         }
34276         if (sc==_spectrum) off_c.fill(whd);
34277         else {
34278           ulongT *poff_c = off_c._data, curr = 0;
34279           cimg_forC(res,c) {
34280             const ulongT old = curr;
34281             curr = (c + one)*_spectrum/sc;
34282             *(poff_c++) = whd*(curr - old);
34283           }
34284           *poff_c = 0;
34285         }
34286 
34287         T *ptrd = res._data;
34288         const T* ptrc = _data;
34289         const ulongT *poff_c = off_c._data;
34290         for (unsigned int c = 0; c<sc; ) {
34291           const T *ptrz = ptrc;
34292           const ulongT *poff_z = off_z._data;
34293           for (unsigned int z = 0; z<sz; ) {
34294             const T *ptry = ptrz;
34295             const ulongT *poff_y = off_y._data;
34296             for (unsigned int y = 0; y<sy; ) {
34297               const T *ptrx = ptry;
34298               const ulongT *poff_x = off_x._data;
34299               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
34300               ++y;
34301               ulongT dy = *(poff_y++);
34302               for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
34303               ptry+=dy;
34304             }
34305             ++z;
34306             ulongT dz = *(poff_z++);
34307             for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
34308             ptrz+=dz;
34309           }
34310           ++c;
34311           ulongT dc = *(poff_c++);
34312           for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
34313           ptrc+=dc;
34314         }
34315       } break;
34316 
34317         // Moving average.
34318         //
34319       case 2 : {
34320         bool instance_first = true;
34321         if (sx!=_width) {
34322           if (sx>_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res);
34323           else {
34324             CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
34325             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34326                                cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256))
34327             cimg_forYZC(tmp,y,z,v) {
34328               for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
34329                 const unsigned int d = std::min(b,c);
34330                 a-=d; b-=d; c-=d;
34331                 tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
34332                 if (!b) { tmp(t++,y,z,v)/=_width; b = _width; }
34333                 if (!c) { ++s; c = sx; }
34334               }
34335             }
34336             tmp.move_to(res);
34337           }
34338           instance_first = false;
34339         }
34340 
34341         if (sy!=_height) {
34342           if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res);
34343           else {
34344             CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
34345             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34346                                cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256))
34347             cimg_forXZC(tmp,x,z,v) {
34348               for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
34349                 const unsigned int d = std::min(b,c);
34350                 a-=d; b-=d; c-=d;
34351                 if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
34352                 else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
34353                 if (!b) { tmp(x,t++,z,v)/=_height; b = _height; }
34354                 if (!c) { ++s; c = sy; }
34355               }
34356             }
34357             tmp.move_to(res);
34358           }
34359           instance_first = false;
34360         }
34361 
34362         if (sz!=_depth) {
34363           if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res);
34364           else {
34365             CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
34366             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34367                                cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256))
34368             cimg_forXYC(tmp,x,y,v) {
34369               for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
34370                 const unsigned int d = std::min(b,c);
34371                 a-=d; b-=d; c-=d;
34372                 if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
34373                 else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
34374                 if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; }
34375                 if (!c) { ++s; c = sz; }
34376               }
34377             }
34378             tmp.move_to(res);
34379           }
34380           instance_first = false;
34381         }
34382 
34383         if (sc!=_spectrum) {
34384           if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res);
34385           else {
34386             CImg<Tfloat> tmp(sx,sy,sz,sc,0);
34387             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34388                                cimg_openmp_if(sc>=256 && _width*_height*_depth>=256))
34389             cimg_forXYZ(tmp,x,y,z) {
34390               for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
34391                 const unsigned int d = std::min(b,c);
34392                 a-=d; b-=d; c-=d;
34393                 if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
34394                 else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
34395                 if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; }
34396                 if (!c) { ++s; c = sc; }
34397               }
34398             }
34399             tmp.move_to(res);
34400           }
34401           instance_first = false;
34402         }
34403 
34404       } break;
34405 
34406         // Linear interpolation.
34407         //
34408       case 3 : {
34409         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34410         CImg<doubleT> foff(off._width);
34411         CImg<T> resx, resy, resz, resc;
34412         double curr, old;
34413 
34414         if (sx!=_width) {
34415           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34416           else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34417           else {
34418             const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34419               (double)_width/sx;
34420             resx.assign(sx,_height,_depth,_spectrum);
34421             curr = old = 0;
34422             {
34423               unsigned int *poff = off._data;
34424               double *pfoff = foff._data;
34425               cimg_forX(resx,x) {
34426                 *(pfoff++) = curr - (unsigned int)curr;
34427                 old = curr;
34428                 curr = std::min(width() - 1.,curr + fx);
34429                 *(poff++) = (unsigned int)curr - (unsigned int)old;
34430               }
34431             }
34432             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34433                                cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34434               cimg_forYZC(resx,y,z,c) {
34435               const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
34436               T *ptrd = resx.data(0,y,z,c);
34437               const unsigned int *poff = off._data;
34438               const double *pfoff = foff._data;
34439               cimg_forX(resx,x) {
34440                 const double alpha = *(pfoff++);
34441                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
34442                 *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
34443                 ptrs+=*(poff++);
34444               }
34445             }
34446           }
34447         } else resx.assign(*this,true);
34448 
34449         if (sy!=_height) {
34450           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34451           else {
34452             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34453             else {
34454               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34455                 (double)_height/sy;
34456               resy.assign(sx,sy,_depth,_spectrum);
34457               curr = old = 0;
34458               {
34459                 unsigned int *poff = off._data;
34460                 double *pfoff = foff._data;
34461                 cimg_forY(resy,y) {
34462                   *(pfoff++) = curr - (unsigned int)curr;
34463                   old = curr;
34464                   curr = std::min(height() - 1.,curr + fy);
34465                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34466                 }
34467               }
34468               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34469                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34470               cimg_forXZC(resy,x,z,c) {
34471                 const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
34472                 T *ptrd = resy.data(x,0,z,c);
34473                 const unsigned int *poff = off._data;
34474                 const double *pfoff = foff._data;
34475                 cimg_forY(resy,y) {
34476                   const double alpha = *(pfoff++);
34477                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
34478                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34479                   ptrd+=sx;
34480                   ptrs+=*(poff++);
34481                 }
34482               }
34483             }
34484           }
34485           resx.assign();
34486         } else resy.assign(resx,true);
34487 
34488         if (sz!=_depth) {
34489           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34490           else {
34491             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34492             else {
34493               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34494                 (double)_depth/sz;
34495               const unsigned int sxy = sx*sy;
34496               resz.assign(sx,sy,sz,_spectrum);
34497               curr = old = 0;
34498               {
34499                 unsigned int *poff = off._data;
34500                 double *pfoff = foff._data;
34501                 cimg_forZ(resz,z) {
34502                   *(pfoff++) = curr - (unsigned int)curr;
34503                   old = curr;
34504                   curr = std::min(depth() - 1.,curr + fz);
34505                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34506                 }
34507               }
34508               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34509                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34510               cimg_forXYC(resz,x,y,c) {
34511                 const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
34512                 T *ptrd = resz.data(x,y,0,c);
34513                 const unsigned int *poff = off._data;
34514                 const double *pfoff = foff._data;
34515                 cimg_forZ(resz,z) {
34516                   const double alpha = *(pfoff++);
34517                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
34518                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34519                   ptrd+=sxy;
34520                   ptrs+=*(poff++);
34521                 }
34522               }
34523             }
34524           }
34525           resy.assign();
34526         } else resz.assign(resy,true);
34527 
34528         if (sc!=_spectrum) {
34529           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34530           else {
34531             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34532             else {
34533               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34534                 (double)_spectrum/sc;
34535               const unsigned int sxyz = sx*sy*sz;
34536               resc.assign(sx,sy,sz,sc);
34537               curr = old = 0;
34538               {
34539                 unsigned int *poff = off._data;
34540                 double *pfoff = foff._data;
34541                 cimg_forC(resc,c) {
34542                   *(pfoff++) = curr - (unsigned int)curr;
34543                   old = curr;
34544                   curr = std::min(spectrum() - 1.,curr + fc);
34545                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34546                 }
34547               }
34548               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34549                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
34550               cimg_forXYZ(resc,x,y,z) {
34551                 const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
34552                 T *ptrd = resc.data(x,y,z,0);
34553                 const unsigned int *poff = off._data;
34554                 const double *pfoff = foff._data;
34555                 cimg_forC(resc,c) {
34556                   const double alpha = *(pfoff++);
34557                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
34558                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
34559                   ptrd+=sxyz;
34560                   ptrs+=*(poff++);
34561                 }
34562               }
34563             }
34564           }
34565           resz.assign();
34566         } else resc.assign(resz,true);
34567         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34568       } break;
34569 
34570         // Grid interpolation.
34571         //
34572       case 4 : {
34573         CImg<T> resx, resy, resz, resc;
34574         if (sx!=_width) {
34575           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34576           else {
34577             resx.assign(sx,_height,_depth,_spectrum,(T)0);
34578             const int dx = (int)(2*sx), dy = 2*width();
34579             int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
34580             cimg_forX(resx,x) if ((err-=dy)<=0) {
34581               cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
34582               ++xs;
34583               err+=dx;
34584             }
34585           }
34586         } else resx.assign(*this,true);
34587 
34588         if (sy!=_height) {
34589           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34590           else {
34591             resy.assign(sx,sy,_depth,_spectrum,(T)0);
34592             const int dx = (int)(2*sy), dy = 2*height();
34593             int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
34594             cimg_forY(resy,y) if ((err-=dy)<=0) {
34595               cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
34596               ++ys;
34597               err+=dx;
34598             }
34599           }
34600           resx.assign();
34601         } else resy.assign(resx,true);
34602 
34603         if (sz!=_depth) {
34604           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34605           else {
34606             resz.assign(sx,sy,sz,_spectrum,(T)0);
34607             const int dx = (int)(2*sz), dy = 2*depth();
34608             int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
34609             cimg_forZ(resz,z) if ((err-=dy)<=0) {
34610               cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
34611               ++zs;
34612               err+=dx;
34613             }
34614           }
34615           resy.assign();
34616         } else resz.assign(resy,true);
34617 
34618         if (sc!=_spectrum) {
34619           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34620           else {
34621             resc.assign(sx,sy,sz,sc,(T)0);
34622             const int dx = (int)(2*sc), dy = 2*spectrum();
34623             int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
34624             cimg_forC(resc,c) if ((err-=dy)<=0) {
34625               cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
34626               ++cs;
34627               err+=dx;
34628             }
34629           }
34630           resz.assign();
34631         } else resc.assign(resz,true);
34632 
34633         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34634       } break;
34635 
34636         // Cubic interpolation.
34637         //
34638       case 5 : {
34639         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
34640         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34641         CImg<doubleT> foff(off._width);
34642         CImg<T> resx, resy, resz, resc;
34643         double curr, old;
34644 
34645         if (sx!=_width) {
34646           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34647           else {
34648             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34649             else {
34650               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34651                 (double)_width/sx;
34652               resx.assign(sx,_height,_depth,_spectrum);
34653               curr = old = 0;
34654               {
34655                 unsigned int *poff = off._data;
34656                 double *pfoff = foff._data;
34657                 cimg_forX(resx,x) {
34658                   *(pfoff++) = curr - (unsigned int)curr;
34659                   old = curr;
34660                   curr = std::min(width() - 1.,curr + fx);
34661                   *(poff++) = (unsigned int)curr - (unsigned int)old;
34662                 }
34663               }
34664               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34665                                  cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34666               cimg_forYZC(resx,y,z,c) {
34667                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
34668                 T *ptrd = resx.data(0,y,z,c);
34669                 const unsigned int *poff = off._data;
34670                 const double *pfoff = foff._data;
34671                 cimg_forX(resx,x) {
34672                   const double
34673                     t = *(pfoff++),
34674                     val1 = (double)*ptrs,
34675                     val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
34676                     val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
34677                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
34678                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34679                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34680                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
34681                   ptrs+=*(poff++);
34682                 }
34683               }
34684             }
34685           }
34686         } else resx.assign(*this,true);
34687 
34688         if (sy!=_height) {
34689           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34690           else {
34691             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34692             else {
34693               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34694                 (double)_height/sy;
34695               resy.assign(sx,sy,_depth,_spectrum);
34696               curr = old = 0;
34697               {
34698                 unsigned int *poff = off._data;
34699                 double *pfoff = foff._data;
34700                 cimg_forY(resy,y) {
34701                   *(pfoff++) = curr - (unsigned int)curr;
34702                   old = curr;
34703                   curr = std::min(height() - 1.,curr + fy);
34704                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34705                 }
34706               }
34707               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34708                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34709               cimg_forXZC(resy,x,z,c) {
34710                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
34711                 T *ptrd = resy.data(x,0,z,c);
34712                 const unsigned int *poff = off._data;
34713                 const double *pfoff = foff._data;
34714                 cimg_forY(resy,y) {
34715                   const double
34716                     t = *(pfoff++),
34717                     val1 = (double)*ptrs,
34718                     val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
34719                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
34720                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
34721                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34722                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34723                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34724                   ptrd+=sx;
34725                   ptrs+=*(poff++);
34726                 }
34727               }
34728             }
34729           }
34730           resx.assign();
34731         } else resy.assign(resx,true);
34732 
34733         if (sz!=_depth) {
34734           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34735           else {
34736             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34737             else {
34738               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34739                 (double)_depth/sz;
34740               const unsigned int sxy = sx*sy;
34741               resz.assign(sx,sy,sz,_spectrum);
34742               curr = old = 0;
34743               {
34744                 unsigned int *poff = off._data;
34745                 double *pfoff = foff._data;
34746                 cimg_forZ(resz,z) {
34747                   *(pfoff++) = curr - (unsigned int)curr;
34748                   old = curr;
34749                   curr = std::min(depth() - 1.,curr + fz);
34750                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34751                 }
34752               }
34753               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34754                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34755               cimg_forXYC(resz,x,y,c) {
34756                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
34757                 T *ptrd = resz.data(x,y,0,c);
34758                 const unsigned int *poff = off._data;
34759                 const double *pfoff = foff._data;
34760                 cimg_forZ(resz,z) {
34761                   const double
34762                     t = *(pfoff++),
34763                     val1 = (double)*ptrs,
34764                     val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
34765                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
34766                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
34767                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34768                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34769                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34770                   ptrd+=sxy;
34771                   ptrs+=*(poff++);
34772                 }
34773               }
34774             }
34775           }
34776           resy.assign();
34777         } else resz.assign(resy,true);
34778 
34779         if (sc!=_spectrum) {
34780           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34781           else {
34782             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34783             else {
34784               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34785                 (double)_spectrum/sc;
34786               const unsigned int sxyz = sx*sy*sz;
34787               resc.assign(sx,sy,sz,sc);
34788               curr = old = 0;
34789               {
34790                 unsigned int *poff = off._data;
34791                 double *pfoff = foff._data;
34792                 cimg_forC(resc,c) {
34793                   *(pfoff++) = curr - (unsigned int)curr;
34794                   old = curr;
34795                   curr = std::min(spectrum() - 1.,curr + fc);
34796                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34797                 }
34798               }
34799               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34800                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
34801               cimg_forXYZ(resc,x,y,z) {
34802                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
34803                 T *ptrd = resc.data(x,y,z,0);
34804                 const unsigned int *poff = off._data;
34805                 const double *pfoff = foff._data;
34806                 cimg_forC(resc,c) {
34807                   const double
34808                     t = *(pfoff++),
34809                     val1 = (double)*ptrs,
34810                     val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
34811                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
34812                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
34813                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34814                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34815                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34816                   ptrd+=sxyz;
34817                   ptrs+=*(poff++);
34818                 }
34819               }
34820             }
34821           }
34822           resz.assign();
34823         } else resc.assign(resz,true);
34824 
34825         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34826       } break;
34827 
34828         // Lanczos interpolation.
34829         //
34830       case 6 : {
34831         const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
34832         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34833         CImg<doubleT> foff(off._width);
34834         CImg<T> resx, resy, resz, resc;
34835         double curr, old;
34836 
34837         if (sx!=_width) {
34838           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34839           else {
34840             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34841             else {
34842               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34843                 (double)_width/sx;
34844               resx.assign(sx,_height,_depth,_spectrum);
34845               curr = old = 0;
34846               {
34847                 unsigned int *poff = off._data;
34848                 double *pfoff = foff._data;
34849                 cimg_forX(resx,x) {
34850                   *(pfoff++) = curr - (unsigned int)curr;
34851                   old = curr;
34852                   curr = std::min(width() - 1.,curr + fx);
34853                   *(poff++) = (unsigned int)curr - (unsigned int)old;
34854                 }
34855               }
34856               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34857                                  cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
34858               cimg_forYZC(resx,y,z,c) {
34859                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
34860                   *const ptrsmax = ptrs0 + (_width - 2);
34861                 T *ptrd = resx.data(0,y,z,c);
34862                 const unsigned int *poff = off._data;
34863                 const double *pfoff = foff._data;
34864                 cimg_forX(resx,x) {
34865                   const double
34866                     t = *(pfoff++),
34867                     w0 = _cimg_lanczos(t + 2),
34868                     w1 = _cimg_lanczos(t + 1),
34869                     w2 = _cimg_lanczos(t),
34870                     w3 = _cimg_lanczos(t - 1),
34871                     w4 = _cimg_lanczos(t - 2),
34872                     val2 = (double)*ptrs,
34873                     val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
34874                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
34875                     val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
34876                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
34877                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34878                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
34879                   ptrs+=*(poff++);
34880                 }
34881               }
34882             }
34883           }
34884         } else resx.assign(*this,true);
34885 
34886         if (sy!=_height) {
34887           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34888           else {
34889             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34890             else {
34891               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34892                 (double)_height/sy;
34893               resy.assign(sx,sy,_depth,_spectrum);
34894               curr = old = 0;
34895               {
34896                 unsigned int *poff = off._data;
34897                 double *pfoff = foff._data;
34898                 cimg_forY(resy,y) {
34899                   *(pfoff++) = curr - (unsigned int)curr;
34900                   old = curr;
34901                   curr = std::min(height() - 1.,curr + fy);
34902                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34903                 }
34904               }
34905               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34906                                  cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
34907               cimg_forXZC(resy,x,z,c) {
34908                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
34909                   *const ptrsmax = ptrs0 + (_height - 2)*sx;
34910                 T *ptrd = resy.data(x,0,z,c);
34911                 const unsigned int *poff = off._data;
34912                 const double *pfoff = foff._data;
34913                 cimg_forY(resy,y) {
34914                   const double
34915                     t = *(pfoff++),
34916                     w0 = _cimg_lanczos(t + 2),
34917                     w1 = _cimg_lanczos(t + 1),
34918                     w2 = _cimg_lanczos(t),
34919                     w3 = _cimg_lanczos(t - 1),
34920                     w4 = _cimg_lanczos(t - 2),
34921                     val2 = (double)*ptrs,
34922                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
34923                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
34924                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
34925                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
34926                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34927                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34928                   ptrd+=sx;
34929                   ptrs+=*(poff++);
34930                 }
34931               }
34932             }
34933           }
34934           resx.assign();
34935         } else resy.assign(resx,true);
34936 
34937         if (sz!=_depth) {
34938           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34939           else {
34940             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34941             else {
34942               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34943                 (double)_depth/sz;
34944               const unsigned int sxy = sx*sy;
34945               resz.assign(sx,sy,sz,_spectrum);
34946               curr = old = 0;
34947               {
34948                 unsigned int *poff = off._data;
34949                 double *pfoff = foff._data;
34950                 cimg_forZ(resz,z) {
34951                   *(pfoff++) = curr - (unsigned int)curr;
34952                   old = curr;
34953                   curr = std::min(depth() - 1.,curr + fz);
34954                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34955                 }
34956               }
34957               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
34958                                  cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
34959               cimg_forXYC(resz,x,y,c) {
34960                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
34961                   *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
34962                 T *ptrd = resz.data(x,y,0,c);
34963                 const unsigned int *poff = off._data;
34964                 const double *pfoff = foff._data;
34965                 cimg_forZ(resz,z) {
34966                   const double
34967                     t = *(pfoff++),
34968                     w0 = _cimg_lanczos(t + 2),
34969                     w1 = _cimg_lanczos(t + 1),
34970                     w2 = _cimg_lanczos(t),
34971                     w3 = _cimg_lanczos(t - 1),
34972                     w4 = _cimg_lanczos(t - 2),
34973                     val2 = (double)*ptrs,
34974                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
34975                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
34976                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
34977                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
34978                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34979                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34980                   ptrd+=sxy;
34981                   ptrs+=*(poff++);
34982                 }
34983               }
34984             }
34985           }
34986           resy.assign();
34987         } else resz.assign(resy,true);
34988 
34989         if (sc!=_spectrum) {
34990           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34991           else {
34992             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34993             else {
34994               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34995                 (double)_spectrum/sc;
34996               const unsigned int sxyz = sx*sy*sz;
34997               resc.assign(sx,sy,sz,sc);
34998               curr = old = 0;
34999               {
35000                 unsigned int *poff = off._data;
35001                 double *pfoff = foff._data;
35002                 cimg_forC(resc,c) {
35003                   *(pfoff++) = curr - (unsigned int)curr;
35004                   old = curr;
35005                   curr = std::min(spectrum() - 1.,curr + fc);
35006                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
35007                 }
35008               }
35009               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
35010                                  cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
35011               cimg_forXYZ(resc,x,y,z) {
35012                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
35013                   *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
35014                 T *ptrd = resc.data(x,y,z,0);
35015                 const unsigned int *poff = off._data;
35016                 const double *pfoff = foff._data;
35017                 cimg_forC(resc,c) {
35018                   const double
35019                     t = *(pfoff++),
35020                     w0 = _cimg_lanczos(t + 2),
35021                     w1 = _cimg_lanczos(t + 1),
35022                     w2 = _cimg_lanczos(t),
35023                     w3 = _cimg_lanczos(t - 1),
35024                     w4 = _cimg_lanczos(t - 2),
35025                     val2 = (double)*ptrs,
35026                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
35027                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
35028                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
35029                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
35030                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
35031                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
35032                   ptrd+=sxyz;
35033                   ptrs+=*(poff++);
35034                 }
35035               }
35036             }
35037           }
35038           resz.assign();
35039         } else resc.assign(resz,true);
35040 
35041         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
35042       } break;
35043 
35044         // Unknown interpolation.
35045         //
35046       default :
35047         throw CImgArgumentException(_cimg_instance
35048                                     "resize(): Invalid specified interpolation %d "
35049                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
35050                                     "5=cubic | 6=lanczos }).",
35051                                     cimg_instance,
35052                                     interpolation_type);
35053       }
35054       return res;
35055     }
35056 
35057     //! Resize image to dimensions of another image.
35058     /**
35059        \param src Reference image used for dimensions.
35060        \param interpolation_type Interpolation method.
35061        \param boundary_conditions Boundary conditions.
35062          Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
35063        \param centering_x Set centering type (only if \p interpolation_type=0).
35064        \param centering_y Set centering type (only if \p interpolation_type=0).
35065        \param centering_z Set centering type (only if \p interpolation_type=0).
35066        \param centering_c Set centering type (only if \p interpolation_type=0).
35067      **/
35068     template<typename t>
35069     CImg<T>& resize(const CImg<t>& src,
35070                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
35071                     const float centering_x = 0, const float centering_y = 0,
35072                     const float centering_z = 0, const float centering_c = 0) {
35073       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
35074                     centering_x,centering_y,centering_z,centering_c);
35075     }
35076 
35077     //! Resize image to dimensions of another image \newinstance.
35078     template<typename t>
35079     CImg<T> get_resize(const CImg<t>& src,
35080                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
35081                        const float centering_x = 0, const float centering_y = 0,
35082                        const float centering_z = 0, const float centering_c = 0) const {
35083       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
35084                         centering_x,centering_y,centering_z,centering_c);
35085     }
35086 
35087     //! Resize image to dimensions of a display window.
35088     /**
35089        \param disp Reference display window used for dimensions.
35090        \param interpolation_type Interpolation method.
35091        \param boundary_conditions Boundary conditions.
35092          Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
35093        \param centering_x Set centering type (only if \p interpolation_type=0).
35094        \param centering_y Set centering type (only if \p interpolation_type=0).
35095        \param centering_z Set centering type (only if \p interpolation_type=0).
35096        \param centering_c Set centering type (only if \p interpolation_type=0).
35097      **/
35098     CImg<T>& resize(const CImgDisplay& disp,
35099                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
35100                     const float centering_x = 0, const float centering_y = 0,
35101                     const float centering_z = 0, const float centering_c = 0) {
35102       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
35103                     centering_x,centering_y,centering_z,centering_c);
35104     }
35105 
35106     //! Resize image to dimensions of a display window \newinstance.
35107     CImg<T> get_resize(const CImgDisplay& disp,
35108                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
35109                        const float centering_x = 0, const float centering_y = 0,
35110                        const float centering_z = 0, const float centering_c = 0) const {
35111       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
35112                         centering_x,centering_y,centering_z,centering_c);
35113     }
35114 
35115     //! Resize image to half-size along XY axes, using an optimized filter.
35116     CImg<T>& resize_halfXY() {
35117       return get_resize_halfXY().move_to(*this);
35118     }
35119 
35120     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
35121     CImg<T> get_resize_halfXY() const {
35122       if (is_empty()) return *this;
35123       static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
35124                                         0.1231940459f,  0.1935127547f, 0.1231940459f,
35125                                         0.07842776544f, 0.1231940459f, 0.07842776544f };
35126       CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
35127       T *ptrd = res._data;
35128       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
35129         if (x%2 && y%2) *(ptrd++) = (T)
35130                           (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
35131                            I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
35132                            I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
35133       return res;
35134     }
35135 
35136     //! Resize image to double-size, using the Scale2X algorithm.
35137     /**
35138        \note Use anisotropic upscaling algorithm
35139        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
35140     **/
35141     CImg<T>& resize_doubleXY() {
35142       return get_resize_doubleXY().move_to(*this);
35143     }
35144 
35145     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
35146     CImg<T> get_resize_doubleXY() const {
35147 #define _cimg_gs2x_for3(bound,i) \
35148  for (int i = 0, _p1##i = 0, \
35149       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
35150       _n1##i<(int)(bound) || i==--_n1##i; \
35151       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
35152 
35153 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
35154   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
35155    _p1##x = 0, \
35156    _n1##x = (int)( \
35157    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
35158    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
35159    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
35160    1>=(img)._width?(img).width() - 1:1); \
35161    (_n1##x<(img).width() && ( \
35162    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
35163    (I[5] = (T)(img)(_n1##x,y,z,c)), \
35164    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
35165    x==--_n1##x; \
35166    I[1] = I[2], \
35167    I[3] = I[4], I[4] = I[5], \
35168    I[7] = I[8], \
35169    _p1##x = x++, ++_n1##x)
35170 
35171       if (is_empty()) return *this;
35172       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
35173       CImg_3x3(I,T);
35174       cimg_forZC(*this,z,c) {
35175         T
35176           *ptrd1 = res.data(0,0,z,c),
35177           *ptrd2 = ptrd1 + res._width;
35178         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
35179           if (Icp!=Icn && Ipc!=Inc) {
35180             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
35181             *(ptrd1++) = Icp==Inc?Inc:Icc;
35182             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
35183             *(ptrd2++) = Icn==Inc?Inc:Icc;
35184           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
35185         }
35186       }
35187       return res;
35188     }
35189 
35190     //! Resize image to triple-size, using the Scale3X algorithm.
35191     /**
35192        \note Use anisotropic upscaling algorithm
35193        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
35194     **/
35195     CImg<T>& resize_tripleXY() {
35196       return get_resize_tripleXY().move_to(*this);
35197     }
35198 
35199     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
35200     CImg<T> get_resize_tripleXY() const {
35201 #define _cimg_gs3x_for3(bound,i) \
35202  for (int i = 0, _p1##i = 0, \
35203       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
35204       _n1##i<(int)(bound) || i==--_n1##i; \
35205       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
35206 
35207 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
35208   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
35209    _p1##x = 0, \
35210    _n1##x = (int)( \
35211    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
35212    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
35213    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
35214    1>=(img)._width?(img).width() - 1:1); \
35215    (_n1##x<(img).width() && ( \
35216    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
35217    (I[5] = (T)(img)(_n1##x,y,z,c)), \
35218    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
35219    x==--_n1##x; \
35220    I[0] = I[1], I[1] = I[2], \
35221    I[3] = I[4], I[4] = I[5], \
35222    I[6] = I[7], I[7] = I[8], \
35223    _p1##x = x++, ++_n1##x)
35224 
35225       if (is_empty()) return *this;
35226       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
35227       CImg_3x3(I,T);
35228       cimg_forZC(*this,z,c) {
35229         T
35230           *ptrd1 = res.data(0,0,z,c),
35231           *ptrd2 = ptrd1 + res._width,
35232           *ptrd3 = ptrd2 + res._width;
35233         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
35234           if (Icp != Icn && Ipc != Inc) {
35235             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
35236             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
35237             *(ptrd1++) = Icp==Inc?Inc:Icc;
35238             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
35239             *(ptrd2++) = Icc;
35240             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
35241             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
35242             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
35243             *(ptrd3++) = Icn==Inc?Inc:Icc;
35244           } else {
35245             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
35246             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
35247             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
35248           }
35249         }
35250       }
35251       return res;
35252     }
35253 
35254     //! Mirror image content along specified axis.
35255     /**
35256        \param axis Mirror axis
35257     **/
35258     CImg<T>& mirror(const char axis) {
35259       if (is_empty()) return *this;
35260       T *pf, *pb, *buf = 0;
35261       switch (cimg::lowercase(axis)) {
35262       case 'x' : {
35263         pf = _data; pb = data(_width - 1);
35264         const unsigned int width2 = _width/2;
35265         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
35266           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
35267           pf+=_width - width2;
35268           pb+=_width + width2;
35269         }
35270       } break;
35271       case 'y' : {
35272         buf = new T[_width];
35273         pf = _data; pb = data(0,_height - 1);
35274         const unsigned int height2 = _height/2;
35275         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
35276           for (unsigned int y = 0; y<height2; ++y) {
35277             std::memcpy(buf,pf,_width*sizeof(T));
35278             std::memcpy(pf,pb,_width*sizeof(T));
35279             std::memcpy(pb,buf,_width*sizeof(T));
35280             pf+=_width;
35281             pb-=_width;
35282           }
35283           pf+=(ulongT)_width*(_height - height2);
35284           pb+=(ulongT)_width*(_height + height2);
35285         }
35286       } break;
35287       case 'z' : {
35288         buf = new T[(ulongT)_width*_height];
35289         pf = _data; pb = data(0,0,_depth - 1);
35290         const unsigned int depth2 = _depth/2;
35291         cimg_forC(*this,c) {
35292           for (unsigned int z = 0; z<depth2; ++z) {
35293             std::memcpy(buf,pf,_width*_height*sizeof(T));
35294             std::memcpy(pf,pb,_width*_height*sizeof(T));
35295             std::memcpy(pb,buf,_width*_height*sizeof(T));
35296             pf+=(ulongT)_width*_height;
35297             pb-=(ulongT)_width*_height;
35298           }
35299           pf+=(ulongT)_width*_height*(_depth - depth2);
35300           pb+=(ulongT)_width*_height*(_depth + depth2);
35301         }
35302       } break;
35303       case 'c' : {
35304         buf = new T[(ulongT)_width*_height*_depth];
35305         pf = _data; pb = data(0,0,0,_spectrum - 1);
35306         const unsigned int _spectrum2 = _spectrum/2;
35307         for (unsigned int v = 0; v<_spectrum2; ++v) {
35308           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
35309           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
35310           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
35311           pf+=(ulongT)_width*_height*_depth;
35312           pb-=(ulongT)_width*_height*_depth;
35313         }
35314       } break;
35315       default :
35316         throw CImgArgumentException(_cimg_instance
35317                                     "mirror(): Invalid specified axis '%c'.",
35318                                     cimg_instance,
35319                                     axis);
35320       }
35321       delete[] buf;
35322       return *this;
35323     }
35324 
35325     //! Mirror image content along specified axis \newinstance.
35326     CImg<T> get_mirror(const char axis) const {
35327       return (+*this).mirror(axis);
35328     }
35329 
35330     //! Mirror image content along specified axes.
35331     /**
35332        \param axes Mirror axes, as a C-string.
35333        \note \c axes may contains multiple characters, e.g. \c "xyz"
35334     **/
35335     CImg<T>& mirror(const char *const axes) {
35336       for (const char *s = axes; *s; ++s) mirror(*s);
35337       return *this;
35338     }
35339 
35340     //! Mirror image content along specified axes \newinstance.
35341     CImg<T> get_mirror(const char *const axes) const {
35342       return (+*this).mirror(axes);
35343     }
35344 
35345     //! Shift image content.
35346     /**
35347        \param delta_x Amount of displacement along the X-axis.
35348        \param delta_y Amount of displacement along the Y-axis.
35349        \param delta_z Amount of displacement along the Z-axis.
35350        \param delta_c Amount of displacement along the C-axis.
35351        \param boundary_conditions Boundary conditions.
35352          Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
35353     **/
35354     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
35355                    const unsigned int boundary_conditions=0) {
35356       if (is_empty()) return *this;
35357       if (boundary_conditions==3)
35358         return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
35359                         width() - delta_x - 1,
35360                         height() - delta_y - 1,
35361                         depth() - delta_z - 1,
35362                         spectrum() - delta_c - 1,3).move_to(*this);
35363       if (delta_x) // Shift along X-axis
35364         switch (boundary_conditions) {
35365         case 2 : { // Periodic
35366           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
35367           if (!ndelta_x) return *this;
35368           CImg<T> buf(cimg::abs(ndelta_x));
35369           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
35370               std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
35371               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
35372               std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
35373             } else cimg_forYZC(*this,y,z,c) {
35374               std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
35375               std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
35376               std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
35377             }
35378         } break;
35379         case 1 : // Neumann
35380           if (delta_x<0) {
35381             const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
35382             if (!ndelta_x) return *this;
35383             cimg_forYZC(*this,y,z,c) {
35384               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
35385               T *ptrd = data(_width - 1,y,z,c);
35386               const T val = *ptrd;
35387               for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
35388             }
35389           } else {
35390             const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
35391             if (!ndelta_x) return *this;
35392             cimg_forYZC(*this,y,z,c) {
35393               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
35394               T *ptrd = data(0,y,z,c);
35395               const T val = *ptrd;
35396               for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
35397             }
35398           }
35399           break;
35400         default : // Dirichlet
35401           if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
35402           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
35403               std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
35404               std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
35405             } else cimg_forYZC(*this,y,z,c) {
35406               std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
35407               std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
35408             }
35409         }
35410 
35411       if (delta_y) // Shift along Y-axis
35412         switch (boundary_conditions) {
35413         case 2 : { // Periodic
35414           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
35415           if (!ndelta_y) return *this;
35416           CImg<T> buf(width(),cimg::abs(ndelta_y));
35417           if (ndelta_y>0) cimg_forZC(*this,z,c) {
35418               std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
35419               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
35420               std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
35421             } else cimg_forZC(*this,z,c) {
35422               std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
35423               std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
35424               std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
35425             }
35426         } break;
35427         case 1 : // Neumann
35428           if (delta_y<0) {
35429             const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
35430             if (!ndelta_y) return *this;
35431             cimg_forZC(*this,z,c) {
35432               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
35433               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
35434               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
35435             }
35436           } else {
35437             const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
35438             if (!ndelta_y) return *this;
35439             cimg_forZC(*this,z,c) {
35440               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
35441               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
35442               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
35443             }
35444           }
35445           break;
35446         default : // Dirichlet
35447           if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
35448           if (delta_y<0) cimg_forZC(*this,z,c) {
35449               std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
35450               std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
35451             } else cimg_forZC(*this,z,c) {
35452               std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
35453               std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
35454             }
35455         }
35456 
35457       if (delta_z) // Shift along Z-axis
35458         switch (boundary_conditions) {
35459         case 2 : { // Periodic
35460           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
35461           if (!ndelta_z) return *this;
35462           CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
35463           if (ndelta_z>0) cimg_forC(*this,c) {
35464               std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
35465               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35466               std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
35467             } else cimg_forC(*this,c) {
35468               std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
35469               std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
35470               std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
35471             }
35472         } break;
35473         case 1 : // Neumann
35474           if (delta_z<0) {
35475             const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
35476             if (!ndelta_z) return *this;
35477             cimg_forC(*this,c) {
35478               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35479               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
35480               for (int l = 0; l<ndelta_z - 1; ++l) {
35481                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
35482               }
35483             }
35484           } else {
35485             const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
35486             if (!ndelta_z) return *this;
35487             cimg_forC(*this,c) {
35488               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
35489               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
35490               for (int l = 0; l<ndelta_z - 1; ++l) {
35491                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
35492               }
35493             }
35494           }
35495           break;
35496         default : // Dirichlet
35497           if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
35498           if (delta_z<0) cimg_forC(*this,c) {
35499               std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
35500               std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
35501             } else cimg_forC(*this,c) {
35502               std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
35503               std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
35504             }
35505         }
35506 
35507       if (delta_c) // Shift along C-axis
35508         switch (boundary_conditions) {
35509         case 2 : { // Periodic
35510           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
35511           if (!ndelta_c) return *this;
35512           CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
35513           if (ndelta_c>0) {
35514             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
35515             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35516             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
35517           } else {
35518             std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
35519             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
35520             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
35521           }
35522         } break;
35523         case 1 : // Neumann
35524           if (delta_c<0) {
35525             const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
35526             if (!ndelta_c) return *this;
35527             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35528             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
35529             for (int l = 0; l<ndelta_c - 1; ++l) {
35530               std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
35531             }
35532           } else {
35533             const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
35534             if (!ndelta_c) return *this;
35535             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
35536             T *ptrd = data(0,0,0,1);
35537             for (int l = 0; l<ndelta_c - 1; ++l) {
35538               std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
35539             }
35540           }
35541           break;
35542         default : // Dirichlet
35543           if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
35544           if (delta_c<0) {
35545             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
35546             std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
35547           } else {
35548             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
35549             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
35550           }
35551         }
35552       return *this;
35553     }
35554 
35555     //! Shift image content \newinstance.
35556     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
35557                       const unsigned int boundary_conditions=0) const {
35558       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
35559     }
35560 
35561     //! Permute axes order.
35562     /**
35563        \param axes_order Axes permutations, as a C-string of 4 characters.
35564        This function permutes image content regarding the specified axes permutation.
35565     **/
35566     CImg<T>& permute_axes(const char *const axes_order) {
35567       return get_permute_axes(axes_order).move_to(*this);
35568     }
35569 
35570     //! Permute axes order \newinstance.
35571     CImg<T> get_permute_axes(const char *const axes_order) const {
35572       const T foo = (T)0;
35573       return _permute_axes(axes_order,foo);
35574     }
35575 
35576     template<typename t>
35577     CImg<t> _permute_axes(const char *const axes_order, const t&) const {
35578       if (is_empty() || !axes_order) return CImg<t>(*this,false);
35579       CImg<t> res;
35580       const T* ptrs = _data;
35581       unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
35582       for (unsigned int l = 0; axes_order[l]; ++l) {
35583         int c = cimg::lowercase(axes_order[l]);
35584         if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
35585         else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; }
35586       }
35587       if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
35588         const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
35589         ulongT wh, whd;
35590         switch (code) {
35591         case 0x0123 : // xyzc
35592           return +*this;
35593         case 0x0132 : // xycz
35594           res.assign(_width,_height,_spectrum,_depth);
35595           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35596           cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
35597           break;
35598         case 0x0213 : // xzyc
35599           res.assign(_width,_depth,_height,_spectrum);
35600           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35601           cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
35602           break;
35603         case 0x0231 : // xzcy
35604           res.assign(_width,_depth,_spectrum,_height);
35605           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35606           cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
35607           break;
35608         case 0x0312 : // xcyz
35609           res.assign(_width,_spectrum,_height,_depth);
35610           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35611           cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
35612           break;
35613         case 0x0321 : // xczy
35614           res.assign(_width,_spectrum,_depth,_height);
35615           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35616           cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
35617           break;
35618         case 0x1023 : // yxzc
35619           res.assign(_height,_width,_depth,_spectrum);
35620           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35621           cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
35622           break;
35623         case 0x1032 : // yxcz
35624           res.assign(_height,_width,_spectrum,_depth);
35625           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35626           cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
35627           break;
35628         case 0x1203 : // yzxc
35629           res.assign(_height,_depth,_width,_spectrum);
35630           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35631           cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
35632           break;
35633         case 0x1230 : // yzcx
35634           res.assign(_height,_depth,_spectrum,_width);
35635           switch (_width) {
35636           case 1 : {
35637             t *ptr_r = res.data(0,0,0,0);
35638             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35639               *(ptr_r++) = (t)*(ptrs++);
35640             }
35641           } break;
35642           case 2 : {
35643             t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
35644             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35645               *(ptr_r++) = (t)ptrs[0];
35646               *(ptr_g++) = (t)ptrs[1];
35647               ptrs+=2;
35648             }
35649           } break;
35650           case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
35651             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);
35652             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35653               *(ptr_r++) = (t)ptrs[0];
35654               *(ptr_g++) = (t)ptrs[1];
35655               *(ptr_b++) = (t)ptrs[2];
35656               ptrs+=3;
35657             }
35658           } break;
35659           case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
35660             t
35661               *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
35662               *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
35663             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
35664               *(ptr_r++) = (t)ptrs[0];
35665               *(ptr_g++) = (t)ptrs[1];
35666               *(ptr_b++) = (t)ptrs[2];
35667               *(ptr_a++) = (t)ptrs[3];
35668               ptrs+=4;
35669             }
35670           } break;
35671           default : {
35672             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35673             cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
35674             return res;
35675           }
35676           }
35677           break;
35678         case 0x1302 : // ycxz
35679           res.assign(_height,_spectrum,_width,_depth);
35680           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35681           cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
35682           break;
35683         case 0x1320 : // yczx
35684           res.assign(_height,_spectrum,_depth,_width);
35685           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35686           cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
35687           break;
35688         case 0x2013 : // zxyc
35689           res.assign(_depth,_width,_height,_spectrum);
35690           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35691           cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
35692           break;
35693         case 0x2031 : // zxcy
35694           res.assign(_depth,_width,_spectrum,_height);
35695           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35696           cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
35697           break;
35698         case 0x2103 : // zyxc
35699           res.assign(_depth,_height,_width,_spectrum);
35700           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35701           cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
35702           break;
35703         case 0x2130 : // zycx
35704           res.assign(_depth,_height,_spectrum,_width);
35705           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35706           cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
35707           break;
35708         case 0x2301 : // zcxy
35709           res.assign(_depth,_spectrum,_width,_height);
35710           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35711           cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
35712           break;
35713         case 0x2310 : // zcyx
35714           res.assign(_depth,_spectrum,_height,_width);
35715           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35716           cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
35717           break;
35718         case 0x3012 : // cxyz
35719           res.assign(_spectrum,_width,_height,_depth);
35720           switch (_spectrum) {
35721           case 1 : {
35722             const T *ptr_r = data(0,0,0,0);
35723             t *ptrd = res._data;
35724             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
35725           } break;
35726           case 2 : {
35727             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
35728             t *ptrd = res._data;
35729             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35730               ptrd[0] = (t)*(ptr_r++);
35731               ptrd[1] = (t)*(ptr_g++);
35732               ptrd+=2;
35733             }
35734           } break;
35735           case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
35736             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35737             t *ptrd = res._data;
35738             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35739               ptrd[0] = (t)*(ptr_r++);
35740               ptrd[1] = (t)*(ptr_g++);
35741               ptrd[2] = (t)*(ptr_b++);
35742               ptrd+=3;
35743             }
35744           } break;
35745           case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
35746             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);
35747             t *ptrd = res._data;
35748             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
35749               ptrd[0] = (t)*(ptr_r++);
35750               ptrd[1] = (t)*(ptr_g++);
35751               ptrd[2] = (t)*(ptr_b++);
35752               ptrd[3] = (t)*(ptr_a++);
35753               ptrd+=4;
35754             }
35755           } break;
35756           default : {
35757             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35758             cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
35759           }
35760           }
35761           break;
35762         case 0x3021 : // cxzy
35763           res.assign(_spectrum,_width,_depth,_height);
35764           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35765           cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
35766           break;
35767         case 0x3102 : // cyxz
35768           res.assign(_spectrum,_height,_width,_depth);
35769           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35770           cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
35771           break;
35772         case 0x3120 : // cyzx
35773           res.assign(_spectrum,_height,_depth,_width);
35774           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35775           cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
35776           break;
35777         case 0x3201 : // czxy
35778           res.assign(_spectrum,_depth,_width,_height);
35779           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35780           cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
35781           break;
35782         case 0x3210 : // czyx
35783           res.assign(_spectrum,_depth,_height,_width);
35784           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35785           cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
35786           break;
35787         }
35788       }
35789       if (!res)
35790         throw CImgArgumentException(_cimg_instance
35791                                     "permute_axes(): Invalid specified axes order '%s'.",
35792                                     cimg_instance,
35793                                     axes_order);
35794       return res;
35795     }
35796 
35797     //! Unroll pixel values along specified axis.
35798     /**
35799        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
35800     **/
35801     CImg<T>& unroll(const char axis) {
35802       const unsigned int siz = (unsigned int)size();
35803       if (siz) switch (cimg::lowercase(axis)) {
35804       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
35805       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
35806       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
35807       case 'c' : _spectrum = siz; _width = _height = _depth = 1; break;
35808       }
35809       return *this;
35810     }
35811 
35812     //! Unroll pixel values along specified axis \newinstance.
35813     CImg<T> get_unroll(const char axis) const {
35814       return (+*this).unroll(axis);
35815     }
35816 
35817     //! Rotate image with arbitrary angle.
35818     /**
35819        \param angle Rotation angle, in degrees.
35820        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35821        \param boundary_conditions Boundary conditions.
35822          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35823        \note The size of the image is modified.
35824     **/
35825     CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
35826                     const unsigned int boundary_conditions=0) {
35827       const float nangle = cimg::mod(angle,360.f);
35828       if (nangle==0.f) return *this;
35829       return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
35830     }
35831 
35832     //! Rotate image with arbitrary angle \newinstance.
35833     CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
35834                        const unsigned int boundary_conditions=0) const {
35835       if (is_empty()) return *this;
35836       CImg<T> res;
35837       const float nangle = cimg::mod(angle,360.f);
35838       if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles
35839         const int wm1 = width() - 1, hm1 = height() - 1;
35840         const int iangle = (int)nangle/90;
35841         switch (iangle) {
35842         case 1 : { // 90 deg
35843           res.assign(_height,_width,_depth,_spectrum);
35844           T *ptrd = res._data;
35845           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
35846         } break;
35847         case 2 : { // 180 deg
35848           res.assign(_width,_height,_depth,_spectrum);
35849           T *ptrd = res._data;
35850           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
35851         } break;
35852         case 3 : { // 270 deg
35853           res.assign(_height,_width,_depth,_spectrum);
35854           T *ptrd = res._data;
35855           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
35856         } break;
35857         default : // 0 deg
35858           return *this;
35859         }
35860       } else { // Generic angle
35861         const float
35862           rad = (float)(nangle*cimg::PI/180.),
35863           ca = (float)std::cos(rad), sa = (float)std::sin(rad),
35864           ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
35865           vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
35866           w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
35867         res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
35868         const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
35869         _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
35870       }
35871       return res;
35872     }
35873 
35874     //! Rotate image with arbitrary angle, around a center point.
35875     /**
35876        \param angle Rotation angle, in degrees.
35877        \param cx X-coordinate of the rotation center.
35878        \param cy Y-coordinate of the rotation center.
35879        \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
35880        \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35881     **/
35882     CImg<T>& rotate(const float angle, const float cx, const float cy,
35883                     const unsigned int interpolation, const unsigned int boundary_conditions=0) {
35884       return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
35885     }
35886 
35887     //! Rotate image with arbitrary angle, around a center point \newinstance.
35888     CImg<T> get_rotate(const float angle, const float cx, const float cy,
35889                        const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
35890       if (is_empty()) return *this;
35891       CImg<T> res(_width,_height,_depth,_spectrum);
35892       _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
35893       return res;
35894     }
35895 
35896     // [internal] Perform 2D rotation with arbitrary angle.
35897     void _rotate(CImg<T>& res, const float angle,
35898                  const unsigned int interpolation, const unsigned int boundary_conditions,
35899                  const float w2, const float h2,
35900                  const float rw2, const float rh2) const {
35901       const float
35902         rad = (float)(angle*cimg::PI/180.),
35903         ca = (float)std::cos(rad), sa = (float)std::sin(rad);
35904 
35905       switch (boundary_conditions) {
35906       case 3 : { // Mirror
35907 
35908         switch (interpolation) {
35909         case 2 : { // Cubic interpolation
35910           const float ww = 2.f*width(), hh = 2.f*height();
35911           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35912             cimg_forXYZC(res,x,y,z,c) {
35913             const float xc = x - rw2, yc = y - rh2,
35914               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35915               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35916             res(x,y,z,c) = _cubic_atXY_c(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35917           }
35918         } break;
35919         case 1 : { // Linear interpolation
35920           const float ww = 2.f*width(), hh = 2.f*height();
35921           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35922             cimg_forXYZC(res,x,y,z,c) {
35923             const float xc = x - rw2, yc = y - rh2,
35924               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35925               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35926             res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35927           }
35928         } break;
35929         default : { // Nearest-neighbor interpolation
35930           const int ww = 2*width(), hh = 2*height();
35931           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35932             cimg_forXYZC(res,x,y,z,c) {
35933             const float xc = x - rw2, yc = y - rh2,
35934               mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
35935               my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
35936             res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35937           }
35938         }
35939         }
35940       } break;
35941 
35942       case 2 : // Periodic
35943         switch (interpolation) {
35944         case 2 : { // Cubic interpolation
35945           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35946             cimg_forXYZC(res,x,y,z,c) {
35947             const float xc = x - rw2, yc = y - rh2;
35948             res(x,y,z,c) = _cubic_atXY_pc(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35949           }
35950         } break;
35951         case 1 : { // Linear interpolation
35952           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35953             cimg_forXYZC(res,x,y,z,c) {
35954             const float xc = x - rw2, yc = y - rh2;
35955             res(x,y,z,c) = (T)_linear_atXY_p(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35956           }
35957         } break;
35958         default : { // Nearest-neighbor interpolation
35959           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35960             cimg_forXYZC(res,x,y,z,c) {
35961             const float xc = x - rw2, yc = y - rh2;
35962             res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
35963                                    cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
35964           }
35965         }
35966         } break;
35967 
35968       case 1 : // Neumann
35969         switch (interpolation) {
35970         case 2 : { // Cubic interpolation
35971           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35972           cimg_forXYZC(res,x,y,z,c) {
35973             const float xc = x - rw2, yc = y - rh2;
35974             res(x,y,z,c) = _cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35975           }
35976         } break;
35977         case 1 : { // Linear interpolation
35978           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35979           cimg_forXYZC(res,x,y,z,c) {
35980             const float xc = x - rw2, yc = y - rh2;
35981             res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35982           }
35983         } break;
35984         default : { // Nearest-neighbor interpolation
35985           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35986           cimg_forXYZC(res,x,y,z,c) {
35987             const float xc = x - rw2, yc = y - rh2;
35988             res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
35989                                  (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
35990           }
35991         }
35992         } break;
35993 
35994       default : // Dirichlet
35995         switch (interpolation) {
35996         case 2 : { // Cubic interpolation
35997           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35998           cimg_forXYZC(res,x,y,z,c) {
35999             const float xc = x - rw2, yc = y - rh2;
36000             res(x,y,z,c) = cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
36001           }
36002         } break;
36003         case 1 : { // Linear interpolation
36004           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
36005           cimg_forXYZC(res,x,y,z,c) {
36006             const float xc = x - rw2, yc = y - rh2;
36007             res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
36008           }
36009         } break;
36010         default : { // Nearest-neighbor interpolation
36011           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
36012           cimg_forXYZC(res,x,y,z,c) {
36013             const float xc = x - rw2, yc = y - rh2;
36014             res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
36015                                 (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
36016           }
36017         }
36018         }
36019       }
36020     }
36021 
36022     //! Rotate volumetric image with arbitrary angle and axis.
36023     /**
36024        \param u X-coordinate of the 3D rotation axis.
36025        \param v Y-coordinate of the 3D rotation axis.
36026        \param w Z-coordinate of the 3D rotation axis.
36027        \param angle Rotation angle, in degrees.
36028        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
36029        \param boundary_conditions Boundary conditions.
36030          Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
36031        \note Most of the time, size of the image is modified.
36032     **/
36033     CImg<T> rotate(const float u, const float v, const float w, const float angle,
36034                    const unsigned int interpolation, const unsigned int boundary_conditions) {
36035       const float nangle = cimg::mod(angle,360.f);
36036       if (nangle==0.f) return *this;
36037       return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
36038     }
36039 
36040     //! Rotate volumetric image with arbitrary angle and axis \newinstance.
36041     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
36042                        const unsigned int interpolation, const unsigned int boundary_conditions) const {
36043       if (is_empty()) return *this;
36044       CImg<T> res;
36045       const float
36046         w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
36047         w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
36048       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
36049       const CImg<Tfloat>
36050         X = R*CImg<Tfloat>(8,3,1,1,
36051                            0.f,w1,w1,0.f,0.f,w1,w1,0.f,
36052                            0.f,0.f,h1,h1,0.f,0.f,h1,h1,
36053                            0.f,0.f,0.f,0.f,d1,d1,d1,d1);
36054       float
36055         xm, xM = X.get_shared_row(0).max_min(xm),
36056         ym, yM = X.get_shared_row(1).max_min(ym),
36057         zm, zM = X.get_shared_row(2).max_min(zm);
36058       const int
36059         dx = (int)cimg::round(xM - xm),
36060         dy = (int)cimg::round(yM - ym),
36061         dz = (int)cimg::round(zM - zm);
36062       R.transpose();
36063       res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
36064       const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
36065       _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
36066       return res;
36067     }
36068 
36069     //! Rotate volumetric image with arbitrary angle and axis, around a center point.
36070     /**
36071        \param u X-coordinate of the 3D rotation axis.
36072        \param v Y-coordinate of the 3D rotation axis.
36073        \param w Z-coordinate of the 3D rotation axis.
36074        \param angle Rotation angle, in degrees.
36075        \param cx X-coordinate of the rotation center.
36076        \param cy Y-coordinate of the rotation center.
36077        \param cz Z-coordinate of the rotation center.
36078        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
36079        \param boundary_conditions Boundary conditions.
36080          Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic }</tt>.
36081        \note Most of the time, size of the image is modified.
36082     **/
36083     CImg<T> rotate(const float u, const float v, const float w, const float angle,
36084                    const float cx, const float cy, const float cz,
36085                    const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
36086       const float nangle = cimg::mod(angle,360.f);
36087       if (nangle==0.f) return *this;
36088       return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
36089     }
36090 
36091     //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
36092     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
36093                        const float cx, const float cy, const float cz,
36094                        const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
36095       if (is_empty()) return *this;
36096       CImg<T> res(_width,_height,_depth,_spectrum);
36097       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
36098       _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
36099       return res;
36100     }
36101 
36102     // [internal] Perform 3D rotation with arbitrary axis and angle.
36103     void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
36104                  const unsigned int interpolation, const unsigned int boundary_conditions,
36105                  const float w2, const float h2, const float d2,
36106                  const float rw2, const float rh2, const float rd2) const {
36107       switch (boundary_conditions) {
36108       case 3 : // Mirror
36109         switch (interpolation) {
36110         case 2 : { // Cubic interpolation
36111           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
36112           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36113           cimg_forXYZ(res,x,y,z) {
36114             const float
36115               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36116               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
36117               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
36118               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
36119             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X<width()?X:ww - X - 1,
36120                                                            Y<height()?Y:hh - Y - 1,
36121                                                            Z<depth()?Z:dd - Z - z,c);
36122           }
36123         } break;
36124         case 1 : { // Linear interpolation
36125           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
36126           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36127           cimg_forXYZ(res,x,y,z) {
36128             const float
36129               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36130               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
36131               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
36132               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
36133             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
36134                                                              Y<height()?Y:hh - Y - 1,
36135                                                              Z<depth()?Z:dd - Z - 1,c);
36136           }
36137         } break;
36138         default : { // Nearest-neighbor interpolation
36139           const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
36140           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36141           cimg_forXYZ(res,x,y,z) {
36142             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
36143             const int
36144               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
36145               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
36146               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
36147             cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
36148                                                     Y<height()?Y:hh - Y - 1,
36149                                                     Z<depth()?Z:dd - Z -  1,c);
36150           }
36151         }
36152         } break;
36153 
36154       case 2 : // Periodic
36155         switch (interpolation) {
36156         case 2 : { // Cubic interpolation
36157           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36158           cimg_forXYZ(res,x,y,z) {
36159             const float
36160               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36161               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36162               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36163               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36164             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_pc(X,Y,Z,c);
36165           }
36166         } break;
36167         case 1 : { // Linear interpolation
36168           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36169           cimg_forXYZ(res,x,y,z) {
36170             const float
36171               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36172               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36173               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36174               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36175             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ_p(X,Y,Z,c);
36176           }
36177         } break;
36178         default : { // Nearest-neighbor interpolation
36179           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36180           cimg_forXYZ(res,x,y,z) {
36181             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
36182             const int
36183               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
36184               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
36185               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
36186             cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
36187           }
36188         }
36189         } break;
36190 
36191       case 1 : // Neumann
36192         switch (interpolation) {
36193         case 2 : { // Cubic interpolation
36194           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36195           cimg_forXYZ(res,x,y,z) {
36196             const float
36197               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36198               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36199               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36200               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36201             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X,Y,Z,c);
36202           }
36203         } break;
36204         case 1 : { // Linear interpolation
36205           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36206           cimg_forXYZ(res,x,y,z) {
36207             const float
36208               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36209               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36210               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36211               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36212             cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
36213           }
36214         } break;
36215         default : { // Nearest-neighbor interpolation
36216           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36217           cimg_forXYZ(res,x,y,z) {
36218             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
36219             const int
36220               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
36221               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
36222               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
36223             cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
36224           }
36225         }
36226         } break;
36227 
36228       default : // Dirichlet
36229         switch (interpolation) {
36230         case 2 : { // Cubic interpolation
36231           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36232           cimg_forXYZ(res,x,y,z) {
36233             const float
36234               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36235               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36236               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36237               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36238             cimg_forC(res,c) res(x,y,z,c) = cubic_atXYZ_c(X,Y,Z,c,(T)0);
36239           }
36240         } break;
36241         case 1 : { // Linear interpolation
36242           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36243           cimg_forXYZ(res,x,y,z) {
36244             const float
36245               xc = x - rw2, yc = y - rh2, zc = z - rd2,
36246               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
36247               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
36248               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
36249             cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
36250           }
36251         } break;
36252         default : { // Nearest-neighbor interpolation
36253           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
36254           cimg_forXYZ(res,x,y,z) {
36255             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
36256             const int
36257               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
36258               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
36259               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
36260             cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
36261           }
36262         }
36263         } break;
36264       }
36265     }
36266 
36267     //! Warp image content by a warping field.
36268     /**
36269        \param warp Warping field.
36270        \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
36271        \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
36272        \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
36273     **/
36274     template<typename t>
36275     CImg<T>& warp(const CImg<t>& p_warp, const unsigned int mode=0,
36276                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
36277       return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this);
36278     }
36279 
36280     //! Warp image content by a warping field \newinstance
36281     template<typename t>
36282     CImg<T> get_warp(const CImg<t>& p_warp, const unsigned int mode=0,
36283                      const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
36284       if (is_empty() || !p_warp) return *this;
36285       if (mode && !is_sameXYZ(p_warp))
36286         throw CImgArgumentException(_cimg_instance
36287                                     "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
36288                                     "have different XYZ dimensions.",
36289                                     cimg_instance,
36290                                     p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data);
36291 
36292       CImg<T> res(p_warp._width,p_warp._height,p_warp._depth,_spectrum);
36293 
36294       if (p_warp._spectrum==1) { // 1D warping
36295         if (mode>=3) { // Forward-relative warp
36296           res.fill((T)0);
36297           if (interpolation>=1) // Linear interpolation
36298             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36299             cimg_forYZC(res,y,z,c) {
36300               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
36301               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
36302             }
36303           else // Nearest-neighbor interpolation
36304             cimg_forYZC(res,y,z,c) {
36305               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
36306               cimg_forX(res,x) {
36307                 const int X = x + (int)cimg::round(*(ptrs0++));
36308                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
36309               }
36310             }
36311         } else if (mode==2) { // Forward-absolute warp
36312           res.fill((T)0);
36313           if (interpolation>=1) // Linear interpolation
36314             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36315             cimg_forYZC(res,y,z,c) {
36316               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
36317               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
36318             }
36319           else // Nearest-neighbor interpolation
36320             cimg_forYZC(res,y,z,c) {
36321               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
36322               cimg_forX(res,x) {
36323                 const int X = (int)cimg::round(*(ptrs0++));
36324                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
36325               }
36326             }
36327         } else if (mode==1) { // Backward-relative warp
36328           if (interpolation==2) // Cubic interpolation
36329             switch (boundary_conditions) {
36330             case 3 : { // Mirror
36331               const float w2 = 2.f*width();
36332               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36333               cimg_forYZC(res,y,z,c) {
36334                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36335                 cimg_forX(res,x) {
36336                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
36337                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,y,z,c);
36338                 }
36339               }
36340             } break;
36341             case 2 : // Periodic
36342               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36343               cimg_forYZC(res,y,z,c) {
36344                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36345                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc(x - (float)*(ptrs0++),y,z,c);
36346               }
36347               break;
36348             case 1 : // Neumann
36349               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36350               cimg_forYZC(res,y,z,c) {
36351                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36352                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c(x - (float)*(ptrs0++),y,z,c);
36353               }
36354               break;
36355             default : // Dirichlet
36356               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36357               cimg_forYZC(res,y,z,c) {
36358                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36359                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c(x - (float)*(ptrs0++),y,z,c,(T)0);
36360               }
36361             }
36362           else if (interpolation==1) // Linear interpolation
36363             switch (boundary_conditions) {
36364             case 3 : { // Mirror
36365               const float w2 = 2.f*width();
36366               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36367               cimg_forYZC(res,y,z,c) {
36368                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36369                 cimg_forX(res,x) {
36370                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
36371                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
36372                 }
36373               }
36374             } break;
36375             case 2 : // Periodic
36376               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36377               cimg_forYZC(res,y,z,c) {
36378                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36379                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p(x - (float)*(ptrs0++),y,z,c);
36380               }
36381               break;
36382             case 1 : // Neumann
36383               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36384               cimg_forYZC(res,y,z,c) {
36385                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36386                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
36387               }
36388               break;
36389             default : // Dirichlet
36390               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36391               cimg_forYZC(res,y,z,c) {
36392                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36393                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
36394               }
36395             }
36396           else // Nearest-neighbor interpolation
36397             switch (boundary_conditions) {
36398             case 3 : { // Mirror
36399               const int w2 = 2*width();
36400               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36401               cimg_forYZC(res,y,z,c) {
36402                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36403                 cimg_forX(res,x) {
36404                   const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
36405                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
36406                 }
36407               }
36408             } break;
36409             case 2 : // Periodic
36410               cimg_forYZC(res,y,z,c) {
36411                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36412                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),y,z,c);
36413               }
36414               break;
36415             case 1 : // Neumann
36416               cimg_forYZC(res,y,z,c) {
36417                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36418                 cimg_forX(res,x) *(ptrd++) = _atX(x - (int)cimg::round(*(ptrs0++)),y,z,c);
36419               }
36420               break;
36421             default : // Dirichlet
36422               cimg_forYZC(res,y,z,c) {
36423                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36424                 cimg_forX(res,x) *(ptrd++) = atX(x - (int)cimg::round(*(ptrs0++)),y,z,c,(T)0);
36425               }
36426             }
36427         }
36428         else { // Backward-absolute warp
36429           if (interpolation==2) // Cubic interpolation
36430             switch (boundary_conditions) {
36431             case 3 : { // Mirror
36432               const float w2 = 2.f*width();
36433               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36434                 cimg_forYZC(res,y,z,c) {
36435                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36436                 cimg_forX(res,x) {
36437                   const float mx = cimg::mod((float)*(ptrs0++),w2);
36438                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,0,0,c);
36439                 }
36440               }
36441             } break;
36442             case 2 : // Periodic
36443               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36444               cimg_forYZC(res,y,z,c) {
36445                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36446                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc((float)*(ptrs0++),0,0,c);
36447               }
36448               break;
36449             case 1 : // Neumann
36450               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36451               cimg_forYZC(res,y,z,c) {
36452                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36453                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c((float)*(ptrs0++),0,0,c);
36454               }
36455               break;
36456             default : // Dirichlet
36457               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36458               cimg_forYZC(res,y,z,c) {
36459                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36460                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c((float)*(ptrs0++),0,0,c,(T)0);
36461               }
36462             }
36463           else if (interpolation==1) // Linear interpolation
36464             switch (boundary_conditions) {
36465             case 3 : { // Mirror
36466               const float w2 = 2.f*width();
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); T *ptrd = res.data(0,y,z,c);
36470                 cimg_forX(res,x) {
36471                   const float mx = cimg::mod((float)*(ptrs0++),w2);
36472                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
36473                 }
36474               }
36475             } break;
36476             case 2 : // Periodic
36477               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36478               cimg_forYZC(res,y,z,c) {
36479                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36480                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p((float)*(ptrs0++),0,0,c);
36481               }
36482               break;
36483             case 1 : // Neumann
36484               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36485               cimg_forYZC(res,y,z,c) {
36486                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36487                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
36488               }
36489               break;
36490             default : // Dirichlet
36491               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36492               cimg_forYZC(res,y,z,c) {
36493                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36494                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
36495               }
36496             }
36497           else // Nearest-neighbor interpolation
36498             switch (boundary_conditions) {
36499             case 3 : { // Mirror
36500               const int w2 = 2*width();
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); T *ptrd = res.data(0,y,z,c);
36504                 cimg_forX(res,x) {
36505                   const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
36506                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
36507                 }
36508               }
36509             } break;
36510             case 2 : // Periodic
36511               cimg_forYZC(res,y,z,c) {
36512                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36513                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),0,0,c);
36514               }
36515               break;
36516             case 1 : // Neumann
36517               cimg_forYZC(res,y,z,c) {
36518                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36519                 cimg_forX(res,x) *(ptrd++) = _atX((int)cimg::round(*(ptrs0++)),0,0,c);
36520               }
36521               break;
36522             default : // Dirichlet
36523               cimg_forYZC(res,y,z,c) {
36524                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
36525                 cimg_forX(res,x) *(ptrd++) = atX((int)cimg::round(*(ptrs0++)),0,0,c,(T)0);
36526               }
36527             }
36528         }
36529 
36530       } else if (p_warp._spectrum==2) { // 2D warping
36531         if (mode>=3) { // Forward-relative warp
36532           res.fill((T)0);
36533           if (interpolation>=1) // Linear interpolation
36534             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36535             cimg_forYZC(res,y,z,c) {
36536               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);
36537               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
36538             }
36539           else // Nearest-neighbor interpolation
36540             cimg_forYZC(res,y,z,c) {
36541               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);
36542               cimg_forX(res,x) {
36543                 const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
36544                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
36545               }
36546             }
36547         } else if (mode==2) { // Forward-absolute warp
36548           res.fill((T)0);
36549           if (interpolation>=1) // Linear interpolation
36550             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36551             cimg_forYZC(res,y,z,c) {
36552               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);
36553               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
36554             }
36555           else // Nearest-neighbor interpolation
36556             cimg_forYZC(res,y,z,c) {
36557               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);
36558               cimg_forX(res,x) {
36559                 const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
36560                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
36561               }
36562             }
36563         } else if (mode==1) { // Backward-relative warp
36564           if (interpolation==2) // Cubic interpolation
36565             switch (boundary_conditions) {
36566             case 3 : { // Mirror
36567               const float w2 = 2.f*width(), h2 = 2.f*height();
36568               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36569               cimg_forYZC(res,y,z,c) {
36570                 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);
36571                 cimg_forX(res,x) {
36572                   const float
36573                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36574                     my = cimg::mod(y - (float)*(ptrs1++),h2);
36575                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36576                 }
36577               }
36578             } break;
36579             case 2 : // Periodic
36580               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36581               cimg_forYZC(res,y,z,c) {
36582                 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);
36583                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36584               }
36585               break;
36586             case 1 : // Neumann
36587               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36588               cimg_forYZC(res,y,z,c) {
36589                 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);
36590                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36591               }
36592               break;
36593             default : // Dirichlet
36594               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36595               cimg_forYZC(res,y,z,c) {
36596                 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);
36597                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
36598               }
36599             }
36600           else if (interpolation==1) // Linear interpolation
36601             switch (boundary_conditions) {
36602             case 3 : { // Mirror
36603               const float w2 = 2.f*width(), h2 = 2.f*height();
36604               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36605               cimg_forYZC(res,y,z,c) {
36606                 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);
36607                 cimg_forX(res,x) {
36608                   const float
36609                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36610                     my = cimg::mod(y - (float)*(ptrs1++),h2);
36611                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36612                 }
36613               }
36614             } break;
36615             case 2 : // Periodic
36616               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36617               cimg_forYZC(res,y,z,c) {
36618                 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);
36619                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36620               }
36621               break;
36622             case 1 : // Neumann
36623               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36624               cimg_forYZC(res,y,z,c) {
36625                 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);
36626                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
36627               }
36628               break;
36629             default : // Dirichlet
36630               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
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); T *ptrd = res.data(0,y,z,c);
36633                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
36634               }
36635             }
36636           else // Nearest-neighbor interpolation
36637             switch (boundary_conditions) {
36638             case 3 : { // Mirror
36639               const int w2 = 2*width(), h2 = 2*height();
36640               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36641               cimg_forYZC(res,y,z,c) {
36642                 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);
36643                 cimg_forX(res,x) {
36644                   const int
36645                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
36646                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
36647                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
36648                 }
36649               }
36650             } break;
36651             case 2 : // Periodic
36652               cimg_forYZC(res,y,z,c) {
36653                 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);
36654                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
36655                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),z,c);
36656               }
36657               break;
36658             case 1 : // Neumann
36659               cimg_forYZC(res,y,z,c) {
36660                 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);
36661                 cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)cimg::round(*(ptrs0++)),
36662                                                    y - (int)cimg::round(*(ptrs1++)),z,c);
36663               }
36664               break;
36665             default : // Dirichlet
36666               cimg_forYZC(res,y,z,c) {
36667                 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);
36668                 cimg_forX(res,x) *(ptrd++) = atXY(x - (int)cimg::round(*(ptrs0++)),
36669                                                   y - (int)cimg::round(*(ptrs1++)),z,c,(T)0);
36670               }
36671             }
36672         } else { // Backward-absolute warp
36673           if (interpolation==2) // Cubic interpolation
36674             switch (boundary_conditions) {
36675             case 3 : { // Mirror
36676               const float w2 = 2.f*width(), h2 = 2.f*height();
36677               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36678               cimg_forYZC(res,y,z,c) {
36679                 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);
36680                 cimg_forX(res,x) {
36681                   const float
36682                     mx = cimg::mod((float)*(ptrs0++),w2),
36683                     my = cimg::mod((float)*(ptrs1++),h2);
36684                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36685                 }
36686               }
36687             } break;
36688             case 2 : // Periodic
36689               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36690               cimg_forYZC(res,y,z,c) {
36691                 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);
36692                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36693               }
36694               break;
36695             case 1 : // Neumann
36696               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36697               cimg_forYZC(res,y,z,c) {
36698                 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);
36699                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36700               }
36701               break;
36702             default : // Dirichlet
36703               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36704               cimg_forYZC(res,y,z,c) {
36705                 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);
36706                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
36707               }
36708             }
36709           else if (interpolation==1) // Linear interpolation
36710             switch (boundary_conditions) {
36711             case 3 : { // Mirror
36712               const float w2 = 2.f*width(), h2 = 2.f*height();
36713               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36714               cimg_forYZC(res,y,z,c) {
36715                 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);
36716                 cimg_forX(res,x) {
36717                   const float
36718                     mx = cimg::mod((float)*(ptrs0++),w2),
36719                     my = cimg::mod((float)*(ptrs1++),h2);
36720                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36721                 }
36722               }
36723             } break;
36724             case 2 : // Periodic
36725               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36726               cimg_forYZC(res,y,z,c) {
36727                 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);
36728                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36729               }
36730               break;
36731             case 1 : // Neumann
36732               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36733               cimg_forYZC(res,y,z,c) {
36734                 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);
36735                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
36736               }
36737               break;
36738             default : // Dirichlet
36739               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36740               cimg_forYZC(res,y,z,c) {
36741                 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);
36742                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
36743               }
36744             }
36745           else // Nearest-neighbor interpolation
36746             switch (boundary_conditions) {
36747             case 3 : { // Mirror
36748               const int w2 = 2*width(), h2 = 2*height();
36749               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36750               cimg_forYZC(res,y,z,c) {
36751                 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);
36752                 cimg_forX(res,x) {
36753                   const int
36754                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
36755                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
36756                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
36757                 }
36758               }
36759             } break;
36760             case 2 : // Periodic
36761               cimg_forYZC(res,y,z,c) {
36762                 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);
36763                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
36764                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),0,c);
36765               }
36766               break;
36767             case 1 : // Neumann
36768               cimg_forYZC(res,y,z,c) {
36769                 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);
36770                 cimg_forX(res,x) *(ptrd++) = _atXY((int)cimg::round(*(ptrs0++)),
36771                                                    (int)cimg::round(*(ptrs1++)),0,c);
36772               }
36773               break;
36774             default : // Dirichlet
36775               cimg_forYZC(res,y,z,c) {
36776                 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);
36777                 cimg_forX(res,x) *(ptrd++) = atXY((int)cimg::round(*(ptrs0++)),
36778                                                   (int)cimg::round(*(ptrs1++)),0,c,(T)0);
36779               }
36780             }
36781         }
36782 
36783       } else { // 3D warping
36784         if (mode>=3) { // Forward-relative warp
36785           res.fill((T)0);
36786           if (interpolation>=1) // Linear interpolation
36787             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36788             cimg_forYZC(res,y,z,c) {
36789               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);
36790               const T *ptrs = data(0,y,z,c);
36791               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
36792                                                     z + (float)*(ptrs2++),c);
36793             }
36794           else // Nearest-neighbor interpolation
36795             cimg_forYZC(res,y,z,c) {
36796               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);
36797               const T *ptrs = data(0,y,z,c);
36798               cimg_forX(res,x) {
36799                 const int
36800                   X = x + (int)cimg::round(*(ptrs0++)),
36801                   Y = y + (int)cimg::round(*(ptrs1++)),
36802                   Z = z + (int)cimg::round(*(ptrs2++));
36803                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36804               }
36805             }
36806         } else if (mode==2) { // Forward-absolute warp
36807           res.fill((T)0);
36808           if (interpolation>=1) // Linear interpolation
36809             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36810             cimg_forYZC(res,y,z,c) {
36811               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);
36812               const T *ptrs = data(0,y,z,c);
36813               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36814             }
36815           else // Nearest-neighbor interpolation
36816             cimg_forYZC(res,y,z,c) {
36817               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);
36818               const T *ptrs = data(0,y,z,c);
36819               cimg_forX(res,x) {
36820                 const int
36821                   X = (int)cimg::round(*(ptrs0++)),
36822                   Y = (int)cimg::round(*(ptrs1++)),
36823                   Z = (int)cimg::round(*(ptrs2++));
36824                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36825               }
36826             }
36827         } else if (mode==1) { // Backward-relative warp
36828           if (interpolation==2) // Cubic interpolation
36829             switch (boundary_conditions) {
36830             case 3 : { // Mirror
36831               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36832               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36833               cimg_forYZC(res,y,z,c) {
36834                 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);
36835                 T *ptrd = res.data(0,y,z,c);
36836                 cimg_forX(res,x) {
36837                   const float
36838                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36839                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36840                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36841                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36842                                              my<height()?my:h2 - my - 1,
36843                                              mz<depth()?mz:d2 - mz - 1,c);
36844                 }
36845               }
36846             } break;
36847             case 2 : // Periodic
36848               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36849               cimg_forYZC(res,y,z,c) {
36850                 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);
36851                 T *ptrd = res.data(0,y,z,c);
36852                 cimg_forX(res,x)
36853                   *(ptrd++) = _cubic_atXYZ_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36854               }
36855               break;
36856             case 1 : // Neumann
36857               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36858               cimg_forYZC(res,y,z,c) {
36859                 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);
36860                 T *ptrd = res.data(0,y,z,c);
36861                 cimg_forX(res,x)
36862                   *(ptrd++) = _cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36863               }
36864               break;
36865             default : // Dirichlet
36866               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36867               cimg_forYZC(res,y,z,c) {
36868                 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);
36869                 T *ptrd = res.data(0,y,z,c);
36870                 cimg_forX(res,x)
36871                   *(ptrd++) = cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36872               }
36873             }
36874           else if (interpolation==1) // Linear interpolation
36875             switch (boundary_conditions) {
36876             case 3 : { // Mirror
36877               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36878               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36879               cimg_forYZC(res,y,z,c) {
36880                 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);
36881                 T *ptrd = res.data(0,y,z,c);
36882                 cimg_forX(res,x) {
36883                   const float
36884                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36885                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36886                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36887                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
36888                                                my<height()?my:h2 - my - 1,
36889                                                mz<depth()?mz:d2 - mz - 1,c);
36890                 }
36891               }
36892             } break;
36893             case 2 : // Periodic
36894               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36895               cimg_forYZC(res,y,z,c) {
36896                 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);
36897                 T *ptrd = res.data(0,y,z,c);
36898                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),
36899                                                                 z - (float)*(ptrs2++),c);
36900               }
36901               break;
36902             case 1 : // Neumann
36903               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36904               cimg_forYZC(res,y,z,c) {
36905                 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);
36906                 T *ptrd = res.data(0,y,z,c);
36907                 cimg_forX(res,x)
36908                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36909               }
36910               break;
36911             default : // Dirichlet
36912               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36913               cimg_forYZC(res,y,z,c) {
36914                 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);
36915                 T *ptrd = res.data(0,y,z,c);
36916                 cimg_forX(res,x)
36917                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36918               }
36919             }
36920           else // Nearest neighbor interpolation
36921             switch (boundary_conditions) {
36922             case 3 : { // Mirror
36923               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
36924               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36925               cimg_forYZC(res,y,z,c) {
36926                 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);
36927                 T *ptrd = res.data(0,y,z,c);
36928                 cimg_forX(res,x) {
36929                   const int
36930                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
36931                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
36932                     mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
36933                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
36934                                       my<height()?my:h2 - my - 1,
36935                                       mz<depth()?mz:d2 - mz - 1,c);
36936                 }
36937               }
36938             } break;
36939             case 2 : // Periodic
36940               cimg_forYZC(res,y,z,c) {
36941                 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);
36942                 T *ptrd = res.data(0,y,z,c);
36943                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
36944                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),
36945                                                      cimg::mod(z - (int)cimg::round(*(ptrs2++)),depth()),c);
36946               }
36947               break;
36948             case 1 : // Neumann
36949               cimg_forYZC(res,y,z,c) {
36950                 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);
36951                 T *ptrd = res.data(0,y,z,c);
36952                 cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)cimg::round(*(ptrs0++)),
36953                                                     y - (int)cimg::round(*(ptrs1++)),
36954                                                     z - (int)cimg::round(*(ptrs2++)),c);
36955               }
36956               break;
36957             default : // Dirichlet
36958               cimg_forYZC(res,y,z,c) {
36959                 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);
36960                 T *ptrd = res.data(0,y,z,c);
36961                 cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)cimg::round(*(ptrs0++)),
36962                                                    y - (int)cimg::round(*(ptrs1++)),
36963                                                    z - (int)cimg::round(*(ptrs2++)),c,(T)0);
36964               }
36965             }
36966         } else { // Backward-absolute warp
36967           if (interpolation==2) // Cubic interpolation
36968             switch (boundary_conditions) {
36969             case 3 : { // Mirror
36970               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36971               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36972               cimg_forYZC(res,y,z,c) {
36973                 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);
36974                 T *ptrd = res.data(0,y,z,c);
36975                 cimg_forX(res,x) {
36976                   const float
36977                     mx = cimg::mod((float)*(ptrs0++),w2),
36978                     my = cimg::mod((float)*(ptrs1++),h2),
36979                     mz = cimg::mod((float)*(ptrs2++),d2);
36980                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36981                                              my<height()?my:h2 - my - 1,
36982                                              mz<depth()?mz:d2 - mz - 1,c);
36983                 }
36984               }
36985             } break;
36986             case 2 : // Periodic
36987               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36988               cimg_forYZC(res,y,z,c) {
36989                 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);
36990                 T *ptrd = res.data(0,y,z,c);
36991                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_pc((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36992               }
36993               break;
36994             case 1 : // Neumann
36995               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36996               cimg_forYZC(res,y,z,c) {
36997                 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);
36998                 T *ptrd = res.data(0,y,z,c);
36999                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
37000               }
37001               break;
37002             default : // Dirichlet
37003               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
37004               cimg_forYZC(res,y,z,c) {
37005                 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);
37006                 T *ptrd = res.data(0,y,z,c);
37007                 cimg_forX(res,x) *(ptrd++) = cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
37008                                                            c,(T)0);
37009               }
37010             }
37011           else if (interpolation==1) // Linear interpolation
37012             switch (boundary_conditions) {
37013             case 3 : { // Mirror
37014               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
37015               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
37016               cimg_forYZC(res,y,z,c) {
37017                 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);
37018                 T *ptrd = res.data(0,y,z,c);
37019                 cimg_forX(res,x) {
37020                   const float
37021                     mx = cimg::mod((float)*(ptrs0++),w2),
37022                     my = cimg::mod((float)*(ptrs1++),h2),
37023                     mz = cimg::mod((float)*(ptrs2++),d2);
37024                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
37025                                                my<height()?my:h2 - my - 1,
37026                                                mz<depth()?mz:d2 - mz - 1,c);
37027                 }
37028               }
37029             } break;
37030             case 2 :// Periodic
37031               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
37032               cimg_forYZC(res,y,z,c) {
37033                 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);
37034                 T *ptrd = res.data(0,y,z,c);
37035                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p((float)*(ptrs0++),(float)*(ptrs1++),
37036                                                                 (float)*(ptrs2++),c);
37037               }
37038               break;
37039             case 1 : // Neumann
37040               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
37041               cimg_forYZC(res,y,z,c) {
37042                 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);
37043                 T *ptrd = res.data(0,y,z,c);
37044                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
37045               }
37046               break;
37047             default : // Dirichlet
37048               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
37049               cimg_forYZC(res,y,z,c) {
37050                 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);
37051                 T *ptrd = res.data(0,y,z,c);
37052                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
37053                                                              c,(T)0);
37054               }
37055             }
37056           else // Nearest-neighbor interpolation
37057             switch (boundary_conditions) {
37058             case 3 : { // Mirror
37059               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
37060               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
37061               cimg_forYZC(res,y,z,c) {
37062                 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);
37063                 T *ptrd = res.data(0,y,z,c);
37064                 cimg_forX(res,x) {
37065                   const int
37066                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
37067                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
37068                     mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
37069                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
37070                                       my<height()?my:h2 - my - 1,
37071                                       mz<depth()?mz:d2 - mz - 1,c);
37072                 }
37073               }
37074             } break;
37075             case 2 : // Periodic
37076               cimg_forYZC(res,y,z,c) {
37077                 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);
37078                 T *ptrd = res.data(0,y,z,c);
37079                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
37080                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),
37081                                                      cimg::mod((int)cimg::round(*(ptrs2++)),depth()),c);
37082               }
37083               break;
37084             case 1 : // Neumann
37085               cimg_forYZC(res,y,z,c) {
37086                 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);
37087                 T *ptrd = res.data(0,y,z,c);
37088                 cimg_forX(res,x) *(ptrd++) = _atXYZ((int)cimg::round(*(ptrs0++)),
37089                                                     (int)cimg::round(*(ptrs1++)),
37090                                                     (int)cimg::round(*(ptrs2++)),c);
37091               }
37092               break;
37093             default : // Dirichlet
37094               cimg_forYZC(res,y,z,c) {
37095                 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);
37096                 T *ptrd = res.data(0,y,z,c);
37097                 cimg_forX(res,x) *(ptrd++) = atXYZ((int)cimg::round(*(ptrs0++)),
37098                                                    (int)cimg::round(*(ptrs1++)),
37099                                                    (int)cimg::round(*(ptrs2++)),c,(T)0);
37100               }
37101             }
37102         }
37103       }
37104       return res;
37105     }
37106 
37107     //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views.
37108     /**
37109        \param x0 X-coordinate of the projection point.
37110        \param y0 Y-coordinate of the projection point.
37111        \param z0 Z-coordinate of the projection point.
37112     **/
37113     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
37114       if (is_empty() || _depth<2) return +*this;
37115       const unsigned int
37116         _x0 = (x0>=_width)?_width - 1:x0,
37117         _y0 = (y0>=_height)?_height - 1:y0,
37118         _z0 = (z0>=_depth)?_depth - 1:z0;
37119       const CImg<T>
37120         img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
37121         img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
37122         resize(_depth,_height,1,-100,-1),
37123         img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
37124       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
37125         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
37126         draw_image(0,img_xy._height,img_xz);
37127     }
37128 
37129     //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace.
37130     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
37131       if (_depth<2) return *this;
37132       return get_projections2d(x0,y0,z0).move_to(*this);
37133     }
37134 
37135     //! Crop image region.
37136     /**
37137        \param x0 = X-coordinate of the upper-left crop rectangle corner.
37138        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
37139        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
37140        \param c0 = C-coordinate of the upper-left crop rectangle corner.
37141        \param x1 = X-coordinate of the lower-right crop rectangle corner.
37142        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
37143        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
37144        \param c1 = C-coordinate of the lower-right crop rectangle corner.
37145        \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
37146     **/
37147     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
37148                   const int x1, const int y1, const int z1, const int c1,
37149                   const unsigned int boundary_conditions=0) {
37150       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
37151     }
37152 
37153     //! Crop image region \newinstance.
37154     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
37155                      const int x1, const int y1, const int z1, const int c1,
37156                      const unsigned int boundary_conditions=0) const {
37157       if (is_empty())
37158         throw CImgInstanceException(_cimg_instance
37159                                     "crop(): Empty instance.",
37160                                     cimg_instance);
37161       const int
37162         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
37163         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
37164         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
37165         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
37166       const unsigned int
37167         _boundary_conditions = nx0>=0 && nx1<width() &&
37168                                ny0>=0 && ny1<height() &&
37169                                nz0>=0 && nz1<depth() &&
37170                                nc0>=0 && nc1<spectrum()?0:boundary_conditions;
37171       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
37172       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
37173         switch (_boundary_conditions) {
37174         case 3 : { // Mirror
37175           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
37176           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
37177                                                                      _height*_depth*_spectrum>=4))
37178           cimg_forXYZC(res,x,y,z,c) {
37179             const int
37180               mx = cimg::mod(nx0 + x,w2),
37181               my = cimg::mod(ny0 + y,h2),
37182               mz = cimg::mod(nz0 + z,d2),
37183               mc = cimg::mod(nc0 + c,s2);
37184             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
37185                                    my<height()?my:h2 - my - 1,
37186                                    mz<depth()?mz:d2 - mz - 1,
37187                                    mc<spectrum()?mc:s2 - mc - 1);
37188           }
37189         } break;
37190         case 2 : { // Periodic
37191           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
37192                                                                      _height*_depth*_spectrum>=4))
37193           cimg_forXYZC(res,x,y,z,c) {
37194             res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
37195                                    cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
37196           }
37197         } break;
37198         case 1 : // Neumann
37199           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
37200                                                                      _height*_depth*_spectrum>=4))
37201           cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
37202           break;
37203         default : // Dirichlet
37204           res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
37205         }
37206       else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
37207       return res;
37208     }
37209 
37210     //! Crop image region \overloading.
37211     CImg<T>& crop(const int x0, const int y0, const int z0,
37212                   const int x1, const int y1, const int z1,
37213                   const unsigned int boundary_conditions=0) {
37214       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
37215     }
37216 
37217     //! Crop image region \newinstance.
37218     CImg<T> get_crop(const int x0, const int y0, const int z0,
37219                      const int x1, const int y1, const int z1,
37220                      const unsigned int boundary_conditions=0) const {
37221       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
37222     }
37223 
37224     //! Crop image region \overloading.
37225     CImg<T>& crop(const int x0, const int y0,
37226                   const int x1, const int y1,
37227                   const unsigned int boundary_conditions=0) {
37228       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
37229     }
37230 
37231     //! Crop image region \newinstance.
37232     CImg<T> get_crop(const int x0, const int y0,
37233                      const int x1, const int y1,
37234                      const unsigned int boundary_conditions=0) const {
37235       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
37236     }
37237 
37238     //! Crop image region \overloading.
37239     CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
37240       return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
37241     }
37242 
37243     //! Crop image region \newinstance.
37244     CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
37245       return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
37246     }
37247 
37248     //! Autocrop image region, regarding the specified background value.
37249     CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
37250       if (is_empty()) return *this;
37251       for (const char *s = axes; *s; ++s) {
37252         const char axis = cimg::lowercase(*s);
37253         const CImg<intT> coords = _autocrop(value,axis);
37254         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
37255         else switch (axis) {
37256         case 'x' : {
37257           const int x0 = coords[0], x1 = coords[1];
37258           if (x0>=0 && x1>=0) crop(x0,x1);
37259         } break;
37260         case 'y' : {
37261           const int y0 = coords[0], y1 = coords[1];
37262           if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
37263         } break;
37264         case 'z' : {
37265           const int z0 = coords[0], z1 = coords[1];
37266           if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
37267         } break;
37268         default : {
37269           const int c0 = coords[0], c1 = coords[1];
37270           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
37271         }
37272         }
37273       }
37274       return *this;
37275     }
37276 
37277     //! Autocrop image region, regarding the specified background value \newinstance.
37278     CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
37279       return (+*this).autocrop(value,axes);
37280     }
37281 
37282     //! Autocrop image region, regarding the specified background color.
37283     /**
37284        \param color Color used for the crop. If \c 0, color is guessed.
37285        \param axes Axes used for the crop.
37286     **/
37287     CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
37288       if (is_empty()) return *this;
37289       if (!color) { // Guess color
37290         const CImg<T> col1 = get_vector_at(0,0,0);
37291         const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
37292         autocrop(col1,axes);
37293         if (_width==w && _height==h && _depth==d && _spectrum==s) {
37294           const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
37295           autocrop(col2,axes);
37296         }
37297         return *this;
37298       }
37299       for (const char *s = axes; *s; ++s) {
37300         const char axis = cimg::lowercase(*s);
37301         switch (axis) {
37302         case 'x' : {
37303           int x0 = width(), x1 = -1;
37304           cimg_forC(*this,c) {
37305             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
37306             const int nx0 = coords[0], nx1 = coords[1];
37307             if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
37308           }
37309           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
37310         } break;
37311         case 'y' : {
37312           int y0 = height(), y1 = -1;
37313           cimg_forC(*this,c) {
37314             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
37315             const int ny0 = coords[0], ny1 = coords[1];
37316             if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
37317           }
37318           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
37319         } break;
37320         default : {
37321           int z0 = depth(), z1 = -1;
37322           cimg_forC(*this,c) {
37323             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
37324             const int nz0 = coords[0], nz1 = coords[1];
37325             if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
37326           }
37327           if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
37328         }
37329         }
37330       }
37331       return *this;
37332     }
37333 
37334     //! Autocrop image region, regarding the specified background color \newinstance.
37335     CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
37336       return (+*this).autocrop(color,axes);
37337     }
37338 
37339     CImg<intT> _autocrop(const T& value, const char axis) const {
37340       CImg<intT> res;
37341       switch (cimg::lowercase(axis)) {
37342       case 'x' : {
37343         int x0 = -1, x1 = -1;
37344         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
37345           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
37346         if (x0>=0) {
37347           for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
37348             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
37349         }
37350         res = CImg<intT>::vector(x0,x1);
37351       } break;
37352       case 'y' : {
37353         int y0 = -1, y1 = -1;
37354         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
37355           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
37356         if (y0>=0) {
37357           for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
37358             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
37359         }
37360         res = CImg<intT>::vector(y0,y1);
37361       } break;
37362       case 'z' : {
37363         int z0 = -1, z1 = -1;
37364         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
37365           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
37366         if (z0>=0) {
37367           for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
37368             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
37369         }
37370         res = CImg<intT>::vector(z0,z1);
37371       } break;
37372       default : {
37373         int c0 = -1, c1 = -1;
37374         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
37375           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
37376         if (c0>=0) {
37377           for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
37378             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
37379         }
37380         res = CImg<intT>::vector(c0,c1);
37381       }
37382       }
37383       return res;
37384     }
37385 
37386     //! Return specified image column.
37387     /**
37388        \param x0 Image column.
37389     **/
37390     CImg<T> get_column(const int x0) const {
37391       return get_columns(x0,x0);
37392     }
37393 
37394     //! Return specified image column \inplace.
37395     CImg<T>& column(const int x0) {
37396       return columns(x0,x0);
37397     }
37398 
37399     //! Return specified range of image columns.
37400     /**
37401        \param x0 Starting image column.
37402        \param x1 Ending image column.
37403     **/
37404     CImg<T>& columns(const int x0, const int x1) {
37405       return get_columns(x0,x1).move_to(*this);
37406     }
37407 
37408     //! Return specified range of image columns \inplace.
37409     CImg<T> get_columns(const int x0, const int x1) const {
37410       return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
37411     }
37412 
37413     //! Return specified image row.
37414     CImg<T> get_row(const int y0) const {
37415       return get_rows(y0,y0);
37416     }
37417 
37418     //! Return specified image row \inplace.
37419     /**
37420        \param y0 Image row.
37421     **/
37422     CImg<T>& row(const int y0) {
37423       return rows(y0,y0);
37424     }
37425 
37426     //! Return specified range of image rows.
37427     /**
37428        \param y0 Starting image row.
37429        \param y1 Ending image row.
37430     **/
37431     CImg<T> get_rows(const int y0, const int y1) const {
37432       return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
37433     }
37434 
37435     //! Return specified range of image rows \inplace.
37436     CImg<T>& rows(const int y0, const int y1) {
37437       return get_rows(y0,y1).move_to(*this);
37438     }
37439 
37440     //! Return specified image slice.
37441     /**
37442        \param z0 Image slice.
37443     **/
37444     CImg<T> get_slice(const int z0) const {
37445       return get_slices(z0,z0);
37446     }
37447 
37448     //! Return specified image slice \inplace.
37449     CImg<T>& slice(const int z0) {
37450       return slices(z0,z0);
37451     }
37452 
37453     //! Return specified range of image slices.
37454     /**
37455        \param z0 Starting image slice.
37456        \param z1 Ending image slice.
37457     **/
37458     CImg<T> get_slices(const int z0, const int z1) const {
37459       return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
37460     }
37461 
37462     //! Return specified range of image slices \inplace.
37463     CImg<T>& slices(const int z0, const int z1) {
37464       return get_slices(z0,z1).move_to(*this);
37465     }
37466 
37467     //! Return specified image channel.
37468     /**
37469        \param c0 Image channel.
37470     **/
37471     CImg<T> get_channel(const int c0) const {
37472       return get_channels(c0,c0);
37473     }
37474 
37475     //! Return specified image channel \inplace.
37476     CImg<T>& channel(const int c0) {
37477       return channels(c0,c0);
37478     }
37479 
37480     //! Return specified range of image channels.
37481     /**
37482        \param c0 Starting image channel.
37483        \param c1 Ending image channel.
37484     **/
37485     CImg<T> get_channels(const int c0, const int c1) const {
37486       return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
37487     }
37488 
37489     //! Return specified range of image channels \inplace.
37490     CImg<T>& channels(const int c0, const int c1) {
37491       return get_channels(c0,c1).move_to(*this);
37492     }
37493 
37494     //! Return stream line of a 2D or 3D vector field.
37495     CImg<floatT> get_streamline(const float x, const float y, const float z,
37496                                 const float L=256, const float dl=0.1f,
37497                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
37498                                 const bool is_oriented_only=false) const {
37499       if (_spectrum!=2 && _spectrum!=3)
37500         throw CImgInstanceException(_cimg_instance
37501                                     "streamline(): Instance is not a 2D or 3D vector field.",
37502                                     cimg_instance);
37503       if (_spectrum==2) {
37504         if (is_oriented_only) {
37505           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
37506           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
37507                             0,0,0,_width - 1.f,_height - 1.f,0.f);
37508         } else {
37509           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
37510           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
37511                             0,0,0,_width - 1.f,_height - 1.f,0.f);
37512         }
37513       }
37514       if (is_oriented_only) {
37515         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
37516         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
37517                           0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
37518       }
37519       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
37520       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
37521                         0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
37522     }
37523 
37524     //! Return stream line of a 3D vector field.
37525     /**
37526        \param func Vector field function.
37527        \param x X-coordinate of the starting point of the streamline.
37528        \param y Y-coordinate of the starting point of the streamline.
37529        \param z Z-coordinate of the starting point of the streamline.
37530        \param L Streamline length.
37531        \param dl Streamline length increment.
37532        \param interpolation_type Type of interpolation.
37533          Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
37534        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
37535        \param is_oriented_only Tells if the direction of the vectors must be ignored.
37536        \param x0 X-coordinate of the first bounding-box vertex.
37537        \param y0 Y-coordinate of the first bounding-box vertex.
37538        \param z0 Z-coordinate of the first bounding-box vertex.
37539        \param x1 X-coordinate of the second bounding-box vertex.
37540        \param y1 Y-coordinate of the second bounding-box vertex.
37541        \param z1 Z-coordinate of the second bounding-box vertex.
37542     **/
37543     template<typename tfunc>
37544     static CImg<floatT> streamline(const tfunc& func,
37545                                    const float x, const float y, const float z,
37546                                    const float L=256, const float dl=0.1f,
37547                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
37548                                    const bool is_oriented_only=false,
37549                                    const float x0=0, const float y0=0, const float z0=0,
37550                                    const float x1=0, const float y1=0, const float z1=0) {
37551       if (dl<=0)
37552         throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
37553                                     "(should be >0).",
37554                                     pixel_type(),
37555                                     dl);
37556 
37557       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
37558       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
37559       const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
37560       CImg<floatT> coordinates(size_L,3);
37561       const float dl2 = dl/2;
37562       float
37563         *ptr_x = coordinates.data(0,0),
37564         *ptr_y = coordinates.data(0,1),
37565         *ptr_z = coordinates.data(0,2),
37566         pu = (float)(dl*func(x,y,z,0)),
37567         pv = (float)(dl*func(x,y,z,1)),
37568         pw = (float)(dl*func(x,y,z,2)),
37569         X = x, Y = y, Z = z;
37570 
37571       switch (interpolation_type) {
37572       case 0 : { // Nearest integer interpolation
37573         cimg_forX(coordinates,l) {
37574           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37575           const int
37576             xi = (int)(X>0?X + 0.5f:X - 0.5f),
37577             yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
37578             zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
37579           float
37580             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
37581             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
37582             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
37583           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37584           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37585           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37586         }
37587       } break;
37588       case 1 : { // First-order interpolation
37589         cimg_forX(coordinates,l) {
37590           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37591           float
37592             u = (float)(dl*func(X,Y,Z,0)),
37593             v = (float)(dl*func(X,Y,Z,1)),
37594             w = (float)(dl*func(X,Y,Z,2));
37595           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37596           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37597           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37598         }
37599       } break;
37600       case 2 : { // Second order interpolation
37601         cimg_forX(coordinates,l) {
37602           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37603           float
37604             u0 = (float)(dl2*func(X,Y,Z,0)),
37605             v0 = (float)(dl2*func(X,Y,Z,1)),
37606             w0 = (float)(dl2*func(X,Y,Z,2));
37607           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
37608           float
37609             u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
37610             v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
37611             w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
37612           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
37613           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37614           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37615         }
37616       } break;
37617       default : { // Fourth order interpolation
37618         cimg_forX(coordinates,k) {
37619           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
37620           float
37621             u0 = (float)(dl2*func(X,Y,Z,0)),
37622             v0 = (float)(dl2*func(X,Y,Z,1)),
37623             w0 = (float)(dl2*func(X,Y,Z,2));
37624           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
37625           float
37626             u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
37627             v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
37628             w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
37629           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
37630           float
37631             u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
37632             v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
37633             w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
37634           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
37635           float
37636             u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
37637             v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
37638             w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
37639           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
37640           const float
37641             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
37642             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
37643             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
37644           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
37645           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
37646         }
37647       }
37648       }
37649       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
37650       return coordinates;
37651     }
37652 
37653     //! Return stream line of a 3D vector field \overloading.
37654     static CImg<floatT> streamline(const char *const expression,
37655                                    const float x, const float y, const float z,
37656                                    const float L=256, const float dl=0.1f,
37657                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
37658                                    const bool is_oriented_only=false,
37659                                    const float x0=0, const float y0=0, const float z0=0,
37660                                    const float x1=0, const float y1=0, const float z1=0) {
37661       _functor4d_streamline_expr func(expression);
37662       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
37663     }
37664 
37665     struct _functor4d_streamline2d_directed {
37666       const CImg<T>& ref;
37667       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
37668       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37669         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
37670       }
37671     };
37672 
37673     struct _functor4d_streamline3d_directed {
37674       const CImg<T>& ref;
37675       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
37676       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37677         return (float)ref._linear_atXYZ(x,y,z,c);
37678       }
37679     };
37680 
37681     struct _functor4d_streamline2d_oriented {
37682       const CImg<T>& ref;
37683       CImg<floatT> *pI;
37684       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
37685       ~_functor4d_streamline2d_oriented() { delete pI; }
37686       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37687 #define _cimg_vecalign2d(i,j) \
37688         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); }
37689         int
37690           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
37691           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
37692           zi = (int)z;
37693         const float
37694           dx = x - xi,
37695           dy = y - yi;
37696         if (c==0) {
37697           CImg<floatT>& I = *pI;
37698           if (xi<0) xi = 0;
37699           if (nxi<0) nxi = 0;
37700           if (xi>=ref.width()) xi = ref.width() - 1;
37701           if (nxi>=ref.width()) nxi = ref.width() - 1;
37702           if (yi<0) yi = 0;
37703           if (nyi<0) nyi = 0;
37704           if (yi>=ref.height()) yi = ref.height() - 1;
37705           if (nyi>=ref.height()) nyi = ref.height() - 1;
37706           I(0,0,0) = (float)ref(xi,yi,zi,0);   I(0,0,1) = (float)ref(xi,yi,zi,1);
37707           I(1,0,0) = (float)ref(nxi,yi,zi,0);  I(1,0,1) = (float)ref(nxi,yi,zi,1);
37708           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
37709           I(0,1,0) = (float)ref(xi,nyi,zi,0);  I(0,1,1) = (float)ref(xi,nyi,zi,1);
37710           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
37711         }
37712         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
37713       }
37714     };
37715 
37716     struct _functor4d_streamline3d_oriented {
37717       const CImg<T>& ref;
37718       CImg<floatT> *pI;
37719       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
37720       ~_functor4d_streamline3d_oriented() { delete pI; }
37721       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37722 #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) { \
37723   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); }
37724         int
37725           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
37726           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
37727           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
37728         const float
37729           dx = x - xi,
37730           dy = y - yi,
37731           dz = z - zi;
37732         if (c==0) {
37733           CImg<floatT>& I = *pI;
37734           if (xi<0) xi = 0;
37735           if (nxi<0) nxi = 0;
37736           if (xi>=ref.width()) xi = ref.width() - 1;
37737           if (nxi>=ref.width()) nxi = ref.width() - 1;
37738           if (yi<0) yi = 0;
37739           if (nyi<0) nyi = 0;
37740           if (yi>=ref.height()) yi = ref.height() - 1;
37741           if (nyi>=ref.height()) nyi = ref.height() - 1;
37742           if (zi<0) zi = 0;
37743           if (nzi<0) nzi = 0;
37744           if (zi>=ref.depth()) zi = ref.depth() - 1;
37745           if (nzi>=ref.depth()) nzi = ref.depth() - 1;
37746           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
37747           I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
37748           I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
37749           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
37750           I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
37751           I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
37752           I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
37753           I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
37754           I(1,0,1,1) = (float)ref(nxi,yi,nzi,1);  I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
37755           I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
37756           I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
37757           I(0,1,1,1) = (float)ref(xi,nyi,nzi,1);  I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
37758           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
37759           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
37760         }
37761         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
37762       }
37763     };
37764 
37765     struct _functor4d_streamline_expr {
37766       _cimg_math_parser *mp;
37767       ~_functor4d_streamline_expr() { mp->end(); delete mp; }
37768       _functor4d_streamline_expr(const char *const expr):mp(0) {
37769         mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
37770       }
37771       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37772         return (float)(*mp)(x,y,z,c);
37773       }
37774     };
37775 
37776     //! Return a shared-memory image referencing a range of pixels of the image instance.
37777     /**
37778        \param x0 X-coordinate of the starting pixel.
37779        \param x1 X-coordinate of the ending pixel.
37780        \param y0 Y-coordinate.
37781        \param z0 Z-coordinate.
37782        \param c0 C-coordinate.
37783      **/
37784     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37785                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
37786       const ulongT
37787         beg = (ulongT)offset(x0,y0,z0,c0),
37788         end = (ulongT)offset(x1,y0,z0,c0);
37789       if (beg>end || beg>=size() || end>=size())
37790         throw CImgArgumentException(_cimg_instance
37791                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37792                                     cimg_instance,
37793                                     x0,x1,y0,z0,c0);
37794       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37795     }
37796 
37797     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
37798     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37799                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
37800       const ulongT
37801         beg = (ulongT)offset(x0,y0,z0,c0),
37802         end = (ulongT)offset(x1,y0,z0,c0);
37803       if (beg>end || beg>=size() || end>=size())
37804         throw CImgArgumentException(_cimg_instance
37805                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37806                                     cimg_instance,
37807                                     x0,x1,y0,z0,c0);
37808       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37809     }
37810 
37811     //! Return a shared-memory image referencing a range of rows of the image instance.
37812     /**
37813        \param y0 Y-coordinate of the starting row.
37814        \param y1 Y-coordinate of the ending row.
37815        \param z0 Z-coordinate.
37816        \param c0 C-coordinate.
37817     **/
37818     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37819                              const unsigned int z0=0, const unsigned int c0=0) {
37820       const ulongT
37821         beg = (ulongT)offset(0,y0,z0,c0),
37822         end = (ulongT)offset(0,y1,z0,c0);
37823       if (beg>end || beg>=size() || end>=size())
37824         throw CImgArgumentException(_cimg_instance
37825                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37826                                     "(0->%u,%u->%u,%u,%u).",
37827                                     cimg_instance,
37828                                     _width - 1,y0,y1,z0,c0);
37829       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37830     }
37831 
37832     //! Return a shared-memory image referencing a range of rows of the image instance \const.
37833     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37834                                    const unsigned int z0=0, const unsigned int c0=0) const {
37835       const ulongT
37836         beg = (ulongT)offset(0,y0,z0,c0),
37837         end = (ulongT)offset(0,y1,z0,c0);
37838       if (beg>end || beg>=size() || end>=size())
37839         throw CImgArgumentException(_cimg_instance
37840                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37841                                     "(0->%u,%u->%u,%u,%u).",
37842                                     cimg_instance,
37843                                     _width - 1,y0,y1,z0,c0);
37844       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37845     }
37846 
37847     //! Return a shared-memory image referencing one row of the image instance.
37848     /**
37849        \param y0 Y-coordinate.
37850        \param z0 Z-coordinate.
37851        \param c0 C-coordinate.
37852     **/
37853     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
37854       return get_shared_rows(y0,y0,z0,c0);
37855     }
37856 
37857     //! Return a shared-memory image referencing one row of the image instance \const.
37858     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
37859       return get_shared_rows(y0,y0,z0,c0);
37860     }
37861 
37862     //! Return a shared memory image referencing a range of slices of the image instance.
37863     /**
37864        \param z0 Z-coordinate of the starting slice.
37865        \param z1 Z-coordinate of the ending slice.
37866        \param c0 C-coordinate.
37867     **/
37868     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
37869       const ulongT
37870         beg = (ulongT)offset(0,0,z0,c0),
37871         end = (ulongT)offset(0,0,z1,c0);
37872       if (beg>end || beg>=size() || end>=size())
37873         throw CImgArgumentException(_cimg_instance
37874                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37875                                     "(0->%u,0->%u,%u->%u,%u).",
37876                                     cimg_instance,
37877                                     _width - 1,_height - 1,z0,z1,c0);
37878       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37879     }
37880 
37881     //! Return a shared memory image referencing a range of slices of the image instance \const.
37882     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
37883       const ulongT
37884         beg = (ulongT)offset(0,0,z0,c0),
37885         end = (ulongT)offset(0,0,z1,c0);
37886       if (beg>end || beg>=size() || end>=size())
37887         throw CImgArgumentException(_cimg_instance
37888                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37889                                     "(0->%u,0->%u,%u->%u,%u).",
37890                                     cimg_instance,
37891                                     _width - 1,_height - 1,z0,z1,c0);
37892       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37893     }
37894 
37895     //! Return a shared-memory image referencing one slice of the image instance.
37896     /**
37897        \param z0 Z-coordinate.
37898        \param c0 C-coordinate.
37899     **/
37900     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
37901       return get_shared_slices(z0,z0,c0);
37902     }
37903 
37904     //! Return a shared-memory image referencing one slice of the image instance \const.
37905     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
37906       return get_shared_slices(z0,z0,c0);
37907     }
37908 
37909     //! Return a shared-memory image referencing a range of channels of the image instance.
37910     /**
37911        \param c0 C-coordinate of the starting channel.
37912        \param c1 C-coordinate of the ending channel.
37913     **/
37914     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
37915       const ulongT
37916         beg = (ulongT)offset(0,0,0,c0),
37917         end = (ulongT)offset(0,0,0,c1);
37918       if (beg>end || beg>=size() || end>=size())
37919         throw CImgArgumentException(_cimg_instance
37920                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37921                                     "(0->%u,0->%u,0->%u,%u->%u).",
37922                                     cimg_instance,
37923                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37924       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37925     }
37926 
37927     //! Return a shared-memory image referencing a range of channels of the image instance \const.
37928     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
37929       const ulongT
37930         beg = (ulongT)offset(0,0,0,c0),
37931         end = (ulongT)offset(0,0,0,c1);
37932       if (beg>end || beg>=size() || end>=size())
37933         throw CImgArgumentException(_cimg_instance
37934                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37935                                     "(0->%u,0->%u,0->%u,%u->%u).",
37936                                     cimg_instance,
37937                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37938       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37939     }
37940 
37941     //! Return a shared-memory image referencing one channel of the image instance.
37942     /**
37943        \param c0 C-coordinate.
37944     **/
37945     CImg<T> get_shared_channel(const unsigned int c0) {
37946       return get_shared_channels(c0,c0);
37947     }
37948 
37949     //! Return a shared-memory image referencing one channel of the image instance \const.
37950     const CImg<T> get_shared_channel(const unsigned int c0) const {
37951       return get_shared_channels(c0,c0);
37952     }
37953 
37954     //! Return a shared-memory version of the image instance.
37955     CImg<T> get_shared() {
37956       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37957     }
37958 
37959     //! Return a shared-memory version of the image instance \const.
37960     const CImg<T> get_shared() const {
37961       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37962     }
37963 
37964     //! Split image into a list along specified axis.
37965     /**
37966        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37967        \param nb Number of split parts.
37968        \note
37969        - If \c nb==0, instance image is split into blocs of egal values along the specified axis.
37970        - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide.
37971        - If \c nb>0, instance image is split into \c nb blocs.
37972     **/
37973     CImgList<T> get_split(const char axis, const int nb=-1) const {
37974       CImgList<T> res;
37975       if (is_empty()) return res;
37976       const char _axis = cimg::lowercase(axis);
37977 
37978       if (nb<0) { // Split by bloc size
37979         const unsigned int dp = (unsigned int)(nb?-nb:1);
37980         switch (_axis) {
37981         case 'x': {
37982           if (_width>dp) {
37983             res.assign(_width/dp + (_width%dp?1:0),1,1);
37984             const unsigned int pe = _width - dp;
37985             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37986                                                            _height*_depth*_spectrum>=128))
37987             for (int p = 0; p<(int)pe; p+=dp)
37988               get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
37989             get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37990           } else res.assign(*this);
37991         } break;
37992         case 'y': {
37993           if (_height>dp) {
37994             res.assign(_height/dp + (_height%dp?1:0),1,1);
37995             const unsigned int pe = _height - dp;
37996             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37997                                                            _width*_depth*_spectrum>=128))
37998             for (int p = 0; p<(int)pe; p+=dp)
37999               get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
38000             get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
38001           } else res.assign(*this);
38002         } break;
38003         case 'z': {
38004           if (_depth>dp) {
38005             res.assign(_depth/dp + (_depth%dp?1:0),1,1);
38006             const unsigned int pe = _depth - dp;
38007             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
38008                                                            _width*_height*_spectrum>=128))
38009             for (int p = 0; p<(int)pe; p+=dp)
38010               get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
38011             get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
38012           } else res.assign(*this);
38013         } break;
38014         case 'c' : {
38015           if (_spectrum>dp) {
38016             res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
38017             const unsigned int pe = _spectrum - dp;
38018             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
38019                                                            _width*_height*_depth>=128))
38020             for (int p = 0; p<(int)pe; p+=dp)
38021               get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
38022             get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
38023           } else res.assign(*this);
38024         }
38025         }
38026       } else if (nb>0) { // Split by number of (non-homogeneous) blocs
38027         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
38028         if ((unsigned int)nb>siz)
38029           throw CImgArgumentException(_cimg_instance
38030                                       "get_split(): Instance cannot be split along %c-axis into %u blocs.",
38031                                       cimg_instance,
38032                                       axis,nb);
38033         if (nb==1) res.assign(*this);
38034         else {
38035           int err = (int)siz;
38036           unsigned int _p = 0;
38037           switch (_axis) {
38038           case 'x' : {
38039             cimg_forX(*this,p) if ((err-=nb)<=0) {
38040               get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
38041               err+=(int)siz;
38042               _p = p + 1U;
38043             }
38044           } break;
38045           case 'y' : {
38046             cimg_forY(*this,p) if ((err-=nb)<=0) {
38047               get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
38048               err+=(int)siz;
38049               _p = p + 1U;
38050             }
38051           } break;
38052           case 'z' : {
38053             cimg_forZ(*this,p) if ((err-=nb)<=0) {
38054               get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
38055               err+=(int)siz;
38056               _p = p + 1U;
38057             }
38058           } break;
38059           case 'c' : {
38060             cimg_forC(*this,p) if ((err-=nb)<=0) {
38061               get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
38062               err+=(int)siz;
38063               _p = p + 1U;
38064             }
38065           }
38066           }
38067         }
38068       } else { // Split by egal values according to specified axis
38069         T current = *_data;
38070         switch (_axis) {
38071         case 'x' : {
38072           int i0 = 0;
38073           cimg_forX(*this,i)
38074             if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
38075           get_columns(i0,width() - 1).move_to(res);
38076         } break;
38077         case 'y' : {
38078           int i0 = 0;
38079           cimg_forY(*this,i)
38080             if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
38081           get_rows(i0,height() - 1).move_to(res);
38082         } break;
38083         case 'z' : {
38084           int i0 = 0;
38085           cimg_forZ(*this,i)
38086             if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
38087           get_slices(i0,depth() - 1).move_to(res);
38088         } break;
38089         case 'c' : {
38090           int i0 = 0;
38091           cimg_forC(*this,i)
38092             if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
38093           get_channels(i0,spectrum() - 1).move_to(res);
38094         } break;
38095         default : {
38096           longT i0 = 0;
38097           cimg_foroff(*this,i)
38098             if ((*this)[i]!=current) {
38099               CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
38100               i0 = (longT)i; current = (*this)[i];
38101             }
38102           CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
38103         }
38104         }
38105       }
38106       return res;
38107     }
38108 
38109     //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
38110     /**
38111        \param values Splitting value sequence.
38112        \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
38113        \param keep_values Tells if the splitting sequence must be kept in the split blocs.
38114      **/
38115     template<typename t>
38116     CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
38117       typedef _cimg_Tt Tt;
38118 
38119       CImgList<T> res;
38120       if (is_empty()) return res;
38121       const ulongT vsiz = values.size();
38122       const char _axis = cimg::lowercase(axis);
38123       if (!vsiz) return CImgList<T>(*this);
38124       if (vsiz==1) { // Split according to a single value
38125         const T value = (T)*values;
38126         switch (_axis) {
38127         case 'x' : {
38128           unsigned int i0 = 0, i = 0;
38129           do {
38130             while (i<_width && (*this)(i)==value) ++i;
38131             if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
38132             while (i<_width && (*this)(i)!=value) ++i;
38133             if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
38134           } while (i<_width);
38135         } break;
38136         case 'y' : {
38137           unsigned int i0 = 0, i = 0;
38138           do {
38139             while (i<_height && (*this)(0,i)==value) ++i;
38140             if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
38141             while (i<_height && (*this)(0,i)!=value) ++i;
38142             if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
38143           } while (i<_height);
38144         } break;
38145         case 'z' : {
38146           unsigned int i0 = 0, i = 0;
38147           do {
38148             while (i<_depth && (*this)(0,0,i)==value) ++i;
38149             if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
38150             while (i<_depth && (*this)(0,0,i)!=value) ++i;
38151             if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
38152           } while (i<_depth);
38153         } break;
38154         case 'c' : {
38155           unsigned int i0 = 0, i = 0;
38156           do {
38157             while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
38158             if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
38159             while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
38160             if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
38161           } while (i<_spectrum);
38162         } break;
38163         default : {
38164           const ulongT siz = size();
38165           ulongT i0 = 0, i = 0;
38166           do {
38167             while (i<siz && (*this)[i]==value) ++i;
38168             if (i>i0) {
38169               if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
38170               i0 = i;
38171             }
38172             while (i<siz && (*this)[i]!=value) ++i;
38173             if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
38174           } while (i<siz);
38175         }
38176         }
38177       } else { // Split according to multiple values
38178         ulongT j = 0;
38179         switch (_axis) {
38180         case 'x' : {
38181           unsigned int i0 = 0, i1 = 0, i = 0;
38182           do {
38183             if ((Tt)(*this)(i)==(Tt)*values) {
38184               i1 = i; j = 0;
38185               while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
38186               i-=j;
38187               if (i>i1) {
38188                 if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
38189                 if (keep_values) get_columns(i1,i - 1).move_to(res);
38190                 i0 = i;
38191               } else ++i;
38192             } else ++i;
38193           } while (i<_width);
38194           if (i0<_width) get_columns(i0,width() - 1).move_to(res);
38195         } break;
38196         case 'y' : {
38197           unsigned int i0 = 0, i1 = 0, i = 0;
38198           do {
38199             if ((Tt)(*this)(0,i)==(Tt)*values) {
38200               i1 = i; j = 0;
38201               while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
38202               i-=j;
38203               if (i>i1) {
38204                 if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
38205                 if (keep_values) get_rows(i1,i - 1).move_to(res);
38206                 i0 = i;
38207               } else ++i;
38208             } else ++i;
38209           } while (i<_height);
38210           if (i0<_height) get_rows(i0,height() - 1).move_to(res);
38211         } break;
38212         case 'z' : {
38213           unsigned int i0 = 0, i1 = 0, i = 0;
38214           do {
38215             if ((Tt)(*this)(0,0,i)==(Tt)*values) {
38216               i1 = i; j = 0;
38217               while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
38218               i-=j;
38219               if (i>i1) {
38220                 if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
38221                 if (keep_values) get_slices(i1,i - 1).move_to(res);
38222                 i0 = i;
38223               } else ++i;
38224             } else ++i;
38225           } while (i<_depth);
38226           if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
38227         } break;
38228         case 'c' : {
38229           unsigned int i0 = 0, i1 = 0, i = 0;
38230           do {
38231             if ((Tt)(*this)(0,0,0,i)==(Tt)*values) {
38232               i1 = i; j = 0;
38233               while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
38234               i-=j;
38235               if (i>i1) {
38236                 if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
38237                 if (keep_values) get_channels(i1,i - 1).move_to(res);
38238                 i0 = i;
38239               } else ++i;
38240             } else ++i;
38241           } while (i<_spectrum);
38242           if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
38243         } break;
38244         default : {
38245           const ulongT siz = size();
38246           ulongT i0 = 0, i1 = 0, i = 0;
38247           do {
38248             if ((Tt)(*this)[i]==(Tt)*values) {
38249               i1 = i; j = 0;
38250               while (i<siz && (Tt)(*this)[i]==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
38251               i-=j;
38252               if (i>i1) {
38253                 if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
38254                 if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
38255                 i0 = i;
38256               } else ++i;
38257             } else ++i;
38258           } while (i<siz);
38259           if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
38260         } break;
38261         }
38262       }
38263       return res;
38264     }
38265 
38266     //! Append two images along specified axis.
38267     /**
38268        \param img Image to append with instance image.
38269        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
38270        \param align Append alignment in \c [0,1].
38271     **/
38272     template<typename t>
38273     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
38274       if (is_empty()) return assign(img,false);
38275       if (!img) return *this;
38276       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
38277     }
38278 
38279     //! Append two images along specified axis \specialization.
38280     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
38281       if (is_empty()) return assign(img,false);
38282       if (!img) return *this;
38283       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
38284     }
38285 
38286     //! Append two images along specified axis \const.
38287     template<typename t>
38288     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
38289       if (is_empty()) return +img;
38290       if (!img) return +*this;
38291       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
38292     }
38293 
38294     //! Append two images along specified axis \specialization.
38295     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
38296       if (is_empty()) return +img;
38297       if (!img) return +*this;
38298       return CImgList<T>(*this,img,true).get_append(axis,align);
38299     }
38300 
38301     //@}
38302     //---------------------------------------
38303     //
38304     //! \name Filtering / Transforms
38305     //@{
38306     //---------------------------------------
38307 
38308     //! Correlate image by a kernel.
38309     /**
38310        \param kernel = the correlation kernel.
38311        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
38312        \param is_normalized = enable local normalization.
38313        \param channel_mode Channel processing mode.
38314                            Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
38315        \param xcenter X-coordinate of the kernel center (~0U>>1 means 'centered').
38316        \param ycenter Y-coordinate of the kernel center (~0U>>1 means 'centered').
38317        \param zcenter Z-coordinate of the kernel center (~0U>>1 means 'centered').
38318        \param xstart Starting X-coordinate of the instance image.
38319        \param ystart Starting Y-coordinate of the instance image.
38320        \param zstart Starting Z-coordinate of the instance image.
38321        \param xend Ending X-coordinate of the instance image.
38322        \param yend Ending Y-coordinate of the instance image.
38323        \param zend Ending Z-coordinate of the instance image.
38324        \param xstride Stride along the X-axis.
38325        \param ystride Stride along the Y-axis.
38326        \param zstride Stride along the Z-axis.
38327        \param xdilation Dilation along the X-axis.
38328        \param ydilation Dilation along the Y-axis.
38329        \param zdilation Dilation along the Z-axis.
38330        \param interpolation_type Can be { false=nearest | true=linear }.
38331        \note
38332        - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
38333        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j -
38334                     c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k).
38335     **/
38336     template<typename t>
38337     CImg<T>& correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38338                        const bool is_normalized=false, const unsigned int channel_mode=1,
38339                        const int xcenter=(int)(~0U>>1),
38340                        const int ycenter=(int)(~0U>>1),
38341                        const int zcenter=(int)(~0U>>1),
38342                        const int xstart=0,
38343                        const int ystart=0,
38344                        const int zstart=0,
38345                        const int xend=(int)(~0U>>1),
38346                        const int yend=(int)(~0U>>1),
38347                        const int zend=(int)(~0U>>1),
38348                        const float xstride=1, const float ystride=1, const float zstride=1,
38349                        const float xdilation=1, const float ydilation=1, const float zdilation=1,
38350                        const bool interpolation_type=false) {
38351       if (is_empty() || !kernel) return *this;
38352       return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38353                            xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38354                            xstride,ystride,zstride,xdilation,ydilation,zdilation,
38355                            interpolation_type).move_to(*this);
38356     }
38357 
38358     template<typename t>
38359     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38360                                       const bool is_normalized=false, const unsigned int channel_mode=1,
38361                                       const int xcenter=(int)(~0U>>1),
38362                                       const int ycenter=(int)(~0U>>1),
38363                                       const int zcenter=(int)(~0U>>1),
38364                                       const int xstart=0,
38365                                       const int ystart=0,
38366                                       const int zstart=0,
38367                                       const int xend=(int)(~0U>>1),
38368                                       const int yend=(int)(~0U>>1),
38369                                       const int zend=(int)(~0U>>1),
38370                                       const float xstride=1, const float ystride=1, const float zstride=1,
38371                                       const float xdilation=1, const float ydilation=1, const float zdilation=1,
38372                                       const bool interpolation_type=false) const {
38373       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38374                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38375                         xstride,ystride,zstride,xdilation,ydilation,zdilation,
38376                         interpolation_type,false);
38377     }
38378 
38379     //! Correlate image by a kernel \newinstance.
38380     template<typename t>
38381     CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const unsigned int boundary_conditions,
38382                                    const bool is_normalized, const unsigned int channel_mode,
38383                                    const int xcenter, const int ycenter, const int zcenter,
38384                                    const int xstart, const int ystart, const int zstart,
38385                                    const int xend, const int yend, const int zend,
38386                                    const float xstride, const float ystride, const float zstride,
38387                                    const float xdilation, const float ydilation, const float zdilation,
38388                                    const bool interpolation_type, const bool is_convolve) const {
38389       typedef _cimg_Ttfloat Ttfloat;
38390       CImg<Ttfloat> res;
38391       _cimg_abort_init_openmp;
38392       cimg_abort_init;
38393 
38394       if (xstart>xend || ystart>yend || zstart>zend)
38395         throw CImgArgumentException(_cimg_instance
38396                                     "%s(): Invalid xyz-start/end arguments (start = (%d,%d,%d), end = (%d,%d,%d)).",
38397                                     cimg_instance,
38398                                     is_convolve?"convolve":"correlate",
38399                                     xstart,ystart,zstart,xend,yend,zend);
38400       if (xstride<=0 || ystride<=0 || zstride<=0)
38401         throw CImgArgumentException(_cimg_instance
38402                                     "%s(): Invalid stride arguments (%g,%g,%g).",
38403                                     cimg_instance,
38404                                     is_convolve?"convolve":"correlate",
38405                                     xstride,ystride,zstride);
38406 
38407       if (is_empty() || !kernel) return *this;
38408       int
38409         _xcenter = xcenter==(int)(~0U>>1)?kernel.width()/2 - 1 + (kernel.width()%2):
38410         std::min(xcenter,kernel.width() - 1),
38411         _ycenter = ycenter==(int)(~0U>>1)?kernel.height()/2 - 1 + (kernel.height()%2):
38412         std::min(ycenter,kernel.height() - 1),
38413         _zcenter = zcenter==(int)(~0U>>1)?kernel.depth()/2 - 1 + (kernel.depth()%2):
38414         std::min(zcenter,kernel.depth() - 1);
38415       float _xdilation = xdilation, _ydilation = ydilation, _zdilation = zdilation;
38416 
38417       CImg<t> _kernel;
38418       if (is_convolve) { // If convolution, go back to correlation
38419         if (kernel.size()/kernel.spectrum()<=27) {
38420           _kernel = CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
38421             get_mirror('x').resize(kernel,-1);
38422           _xcenter = kernel.width() - 1 - _xcenter;
38423           _ycenter = kernel.height() - 1 - _ycenter;
38424           _zcenter = kernel.depth() - _zcenter - 1;
38425         } else { _kernel = kernel.get_shared(); _xdilation*=-1; _ydilation*=-1; _zdilation*=-1; }
38426       } else _kernel = kernel.get_shared();
38427 
38428       const int
38429         _xend = xend==(int)(~0U>>1)?width() - 1:xend,
38430         _yend = yend==(int)(~0U>>1)?height() - 1:yend,
38431         _zend = zend==(int)(~0U>>1)?depth() - 1:zend,
38432         i_xstride = (int)cimg::round(xstride),
38433         i_ystride = (int)cimg::round(ystride),
38434         i_zstride = (int)cimg::round(zstride),
38435         i_xdilation = (int)cimg::round(_xdilation),
38436         i_ydilation = (int)cimg::round(_ydilation),
38437         i_zdilation = (int)cimg::round(_zdilation),
38438         res_width = _xend - xstart + 1,
38439         res_height = _yend - ystart + 1,
38440         res_depth = _zend + zstart + 1,
38441         smin = std::min(spectrum(),_kernel.spectrum()),
38442         smax = std::max(spectrum(),_kernel.spectrum()),
38443         cend = !channel_mode?spectrum()*_kernel.spectrum():smax;
38444       const ulongT
38445         res_wh = (ulongT)res_width*res_height,
38446         res_whd = res_wh*res_depth,
38447         res_siz = res_whd*res._spectrum;
38448 
38449       if (!res_whd) return CImg<Ttfloat>();
38450       res.assign(res_width,res_height,res_depth,
38451                  !channel_mode?_spectrum*_kernel._spectrum:
38452                  channel_mode==1?smax:
38453                  channel_mode==2?(int)std::ceil((float)smax/smin):1);
38454       if (channel_mode>=2) res.fill(0);
38455 
38456       const bool
38457 #if cimg_use_openmp==1
38458         is_master_thread = !omp_get_thread_num(),
38459 #else
38460         is_master_thread = true,
38461 #endif
38462         is_outer_parallel = is_master_thread &&
38463         (res._spectrum>=cimg::nb_cpus() || res_siz<=(cimg_openmp_sizefactor)*32768),
38464         is_inner_parallel = is_master_thread &&
38465         (!is_outer_parallel && res_whd>=(cimg_openmp_sizefactor)*32768),
38466         is_int_stride_dilation = xstride==i_xstride && ystride==i_ystride && zstride==i_zstride &&
38467         _xdilation==i_xdilation && _ydilation==i_ydilation && _zdilation==i_zdilation;
38468       cimg::unused(is_inner_parallel,is_outer_parallel);
38469       const int
38470         w = width(), h = height(), d = depth(),
38471         w1 = w  - 1, h1 = h - 1, d1 = d - 1,
38472         w2 = 2*w, h2 = 2*h, d2 = 2*d;
38473       const ulongT wh = (ulongT)w*h, whd = wh*d;
38474 
38475       // Reshape kernel to enable optimizations for a few cases.
38476       if (boundary_conditions==1 &&
38477           _kernel._width>1 && _kernel._height>1 &&
38478           ((_kernel._depth==1 && _kernel._width<=5 && _kernel._height<=5) ||
38479            (_kernel._depth<=3 && _kernel._width<=3 && _kernel._height<=3)) &&
38480           xstart>=0 && ystart>=0 && zstart>=0 &&
38481           _xend<width() && _yend<height() && _zend<depth() &&
38482           is_int_stride_dilation &&
38483           xstride==1 && ystride==1 && zstride==1 &&
38484           i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
38485         const unsigned int M = cimg::max(_kernel._width,_kernel._height,_kernel._depth);
38486         _kernel.assign(_kernel.get_resize(M + 1 - (M%2),M + 1 - (M%2),_kernel._depth>1?M + 1 - (M%2):1,-100,
38487                                           0,0,
38488                                           1,1,1),false);
38489         _xcenter = _ycenter = (int)M/2;
38490         if (_kernel._depth>1) _ycenter = (int)M/2;
38491       }
38492 
38493       // Optimized version for a few particular cases (3x3, 5x5 and 3x3x3 kernels, with a few other conditions).
38494       if (boundary_conditions==1 &&
38495           _kernel._width==_kernel._height &&
38496           ((_kernel._depth==1 && (_kernel._width==3 || _kernel._width==5)) ||
38497            (_kernel._depth==_kernel._width && _kernel._width==3)) &&
38498           _xcenter==_kernel.width()/2 && _ycenter==_kernel.height()/2 && _zcenter==_kernel.depth()/2 &&
38499           xstart>=0 && ystart>=0 && zstart>=0 &&
38500           _xend<width() && _yend<height() && _zend<depth() &&
38501           is_int_stride_dilation &&
38502           xstride==1 && ystride==1 && zstride==1 &&
38503           i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
38504 
38505         switch (_kernel._depth) {
38506         case 3 : { // 3x3x3 centered kernel
38507           cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38508           for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
38509             cimg_abort_test2;
38510             const CImg<T> I = get_shared_channel(c%_spectrum);
38511             const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38512             CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38513               CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38514             if (is_normalized) {
38515               const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38516               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38517                 cimg_forXYZ(res,X,Y,Z) {
38518                 const int
38519                   x = xstart + X, y = ystart + Y, z = zstart + Z,
38520                   px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38521                   py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
38522                   pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
38523                 const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
38524                                       cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
38525                                       cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
38526                                       cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
38527                                       cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
38528                                       cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
38529                                       cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
38530                                       cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
38531                                       cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
38532                 _res(X,Y,Z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
38533                                            K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
38534                                            K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
38535                                            K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
38536                                            K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
38537                                            K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
38538                                            K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
38539                                            K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
38540                                            K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
38541               }
38542             } else {
38543               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38544                 cimg_forXYZ(res,X,Y,Z) {
38545                 const int
38546                   x = xstart + X, y = ystart + Y, z = zstart + Z,
38547                   px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38548                   py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
38549                   pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
38550                 _res(X,Y,Z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
38551                                         K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
38552                                         K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
38553                                         K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
38554                                         K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
38555                                         K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
38556                                         K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
38557                                         K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
38558                                         K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
38559               }
38560             }
38561             if (channel_mode==2)
38562               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38563             else if (channel_mode==3)
38564               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38565           } _cimg_abort_catch_openmp2
38566         } break;
38567 
38568         default :
38569         case 1 :
38570           switch (_kernel._width) {
38571           case 5 : { // 5x5 centered kernel
38572             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38573             for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
38574               cimg_abort_test2;
38575               const CImg<T> I = get_shared_channel(c%_spectrum);
38576               const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38577               CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38578                 CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38579               if (is_normalized) {
38580                 const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38581                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38582                   cimg_forXYZ(res,X,Y,z) {
38583                   const int
38584                     x = xstart + X, y = ystart + Y,
38585                     px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
38586                     nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
38587                     py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
38588                     ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
38589                   const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
38590                                         cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
38591                                         cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
38592                                         cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
38593                                         cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
38594                                         cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
38595                                         cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
38596                                         cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
38597                                         cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
38598                                         cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
38599                   _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
38600                                              K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
38601                                              K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
38602                                              K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
38603                                              K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
38604                                              K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
38605                                              K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
38606                                              K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
38607                                              K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
38608                                              K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
38609                 }
38610               } else {
38611                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38612                   cimg_forXYZ(res,X,Y,z) {
38613                   const int
38614                     x = xstart + X, y = ystart + Y,
38615                     px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
38616                     nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
38617                     py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
38618                     ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
38619                   _res(X,Y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
38620                                           K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
38621                                           K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
38622                                           K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
38623                                           K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
38624                                           K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
38625                                           K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
38626                                           K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
38627                                           K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
38628                                           K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
38629                 }
38630               }
38631             if (channel_mode==2)
38632               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38633             else if (channel_mode==3)
38634               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38635             } _cimg_abort_catch_openmp2
38636           } break;
38637 
38638           case 3 : { // 3x3 centered kernel
38639             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38640             for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
38641               cimg_abort_test2;
38642               const CImg<T> I = get_shared_channel(c%_spectrum);
38643               const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38644               CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38645                 CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38646               if (is_normalized) {
38647                 const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
38648                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38649                   cimg_forXYZ(res,X,Y,z) {
38650                   const int
38651                     x = xstart + X, y = ystart + Y,
38652                     px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38653                     py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
38654                   const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
38655                                         cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
38656                                         cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
38657                   _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
38658                                              K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
38659                                              K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
38660                 }
38661               } else {
38662                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38663                   cimg_forXYZ(res,X,Y,z) {
38664                   const int
38665                     x = xstart + X, y = ystart + Y,
38666                     px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
38667                     py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
38668                   _res(X,Y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
38669                                           K[3]*I(px,y,z)  + K[4]*I(x,y,z)  + K[5]*I(nx,y,z) +
38670                                           K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
38671                 }
38672               }
38673             if (channel_mode==2)
38674               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38675             else if (channel_mode==3)
38676               cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38677             } _cimg_abort_catch_openmp2
38678           } break;
38679           }
38680         }
38681       } else if (_kernel._width==1 && _kernel._height==1 && _kernel._depth==1 &&
38682                  !_xcenter && !_ycenter && !_zcenter &&
38683                  xstart>=0 && ystart>=0 && zstart>=0 &&
38684                  _xend<width() && _yend<height() && _zend<depth() &&
38685                  xstride==1 && ystride==1 && zstride==1) {
38686 
38687         // Special optimization for 1x1 kernel.
38688         cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38689         for (int c = 0; c<cend; ++c) {
38690           const t valK = _kernel[!channel_mode?c/_spectrum:c%_kernel._spectrum];
38691           CImg<T> I = get_crop(xstart,ystart,zstart,c%_spectrum,_xend,_yend,_zend,c%_spectrum)*=valK;
38692           if (is_normalized) I.sign();
38693           switch (channel_mode) {
38694           case 0 : // All
38695           case 1 : // One for one
38696             res.get_shared_channel(c) = I;
38697             break;
38698           case 2 : // Partial sum
38699             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=I;
38700             break;
38701           case 3 : // Full sum
38702             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=I;
38703             break;
38704           }
38705         }
38706       } else { // Generic version
38707         cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
38708         for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
38709           cimg_abort_test2;
38710           const CImg<T> I = get_shared_channel(c%_spectrum);
38711           const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
38712           CImg<Ttfloat> _res = channel_mode<=1?res.get_shared_channel(c):
38713             CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
38714           Ttfloat M = 0, M2 = 0;
38715           if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = cimg::sqr(M); }
38716 
38717 #define _cimg_correlate_x_int const int ix = xstart + i_xstride*x + i_xdilation*(p - _xcenter)
38718 #define _cimg_correlate_y_int const int iy = ystart + i_ystride*y + i_ydilation*(q - _ycenter)
38719 #define _cimg_correlate_z_int const int iz = zstart + i_zstride*z + i_zdilation*(r - _zcenter)
38720 #define _cimg_correlate_x_float const float ix = xstart + xstride*x + _xdilation*(p - _xcenter)
38721 #define _cimg_correlate_y_float const float iy = ystart + ystride*y + _ydilation*(q - _ycenter)
38722 #define _cimg_correlate_z_float const float iz = zstart + zstride*z + _zdilation*(r - _zcenter)
38723 
38724 #define _cimg_correlate_x_int_dirichlet const bool is_in_x = ix>=0 && ix<w
38725 #define _cimg_correlate_y_int_dirichlet const bool is_in_y = iy>=0 && iy<h
38726 #define _cimg_correlate_z_int_dirichlet const bool is_in_z = iz>=0 && iz<d
38727 #define _cimg_correlate_x_float_dirichlet _cimg_correlate_x_int_dirichlet
38728 #define _cimg_correlate_y_float_dirichlet _cimg_correlate_y_int_dirichlet
38729 #define _cimg_correlate_z_float_dirichlet _cimg_correlate_z_int_dirichlet
38730 
38731 #define _cimg_correlate_x_int_neumann const int nix = cimg::cut(ix,0,w1)
38732 #define _cimg_correlate_y_int_neumann const int niy = cimg::cut(iy,0,h1)
38733 #define _cimg_correlate_z_int_neumann const int niz = cimg::cut(iz,0,d1)
38734 #define _cimg_correlate_x_float_neumann const float nix = cimg::cut(ix,0,w1)
38735 #define _cimg_correlate_y_float_neumann const float niy = cimg::cut(iy,0,h1)
38736 #define _cimg_correlate_z_float_neumann const float niz = cimg::cut(iz,0,d1)
38737 
38738 #define _cimg_correlate_x_int_periodic const int nix = cimg::mod(ix,w)
38739 #define _cimg_correlate_y_int_periodic const int niy = cimg::mod(iy,h)
38740 #define _cimg_correlate_z_int_periodic const int niz = cimg::mod(iz,d)
38741 #define _cimg_correlate_x_float_periodic const float nix = cimg::mod(ix,w)
38742 #define _cimg_correlate_y_float_periodic const float niy = cimg::mod(iy,h)
38743 #define _cimg_correlate_z_float_periodic const float niz = cimg::mod(iz,d)
38744 
38745 #define _cimg_correlate_x_int_mirror const int mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
38746 #define _cimg_correlate_y_int_mirror const int my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
38747 #define _cimg_correlate_z_int_mirror const int mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
38748 #define _cimg_correlate_x_float_mirror const float mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
38749 #define _cimg_correlate_y_float_mirror const float my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
38750 #define _cimg_correlate_z_float_mirror const float mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
38751 
38752 #define _cimg_correlate(type,boundary,access) \
38753           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
38754           cimg_forXYZ(res,x,y,z) { \
38755             Ttfloat val = 0; \
38756             const t *pK = K._data; \
38757             cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
38758               cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
38759                 cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
38760                   val+=*(pK++)*(access); \
38761                 } \
38762               } \
38763             } \
38764             _res(x,y,z,0,res_wh,res_whd) = val; \
38765           }
38766 
38767 #define _cimg_correlate_n(type,boundary,access) \
38768           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
38769           cimg_forXYZ(res,x,y,z) { \
38770             Ttfloat val = 0, N = 0; \
38771             const t *pK = K._data; \
38772             cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
38773               cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
38774                 cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
38775                   Ttfloat _val = access; \
38776                   val+=*(pK++)*_val; \
38777                   _val*=_val; N+=_val; \
38778                 } \
38779               } \
38780             } \
38781             N*=M2; _res(x,y,z,0,res_wh,res_whd) = N?val/std::sqrt(N):0; \
38782           }
38783 
38784           if (is_normalized) { // Normalized convolution/correlation
38785             if (is_int_stride_dilation) // Integer stride and dilation
38786               switch (boundary_conditions) {
38787               case 0 : // Dirichlet
38788                 _cimg_correlate_n(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):0);
38789                 break;
38790               case 1 : // Neumann
38791                 _cimg_correlate_n(int,neumann,I(nix,niy,niz,0,wh,whd));
38792                 break;
38793               case 2 : // Periodic
38794                 _cimg_correlate_n(int,periodic,I(nix,niy,niz,0,wh,whd));
38795                 break;
38796               case 3 : // Mirror
38797                 _cimg_correlate_n(int,mirror,I(nix,niy,niz,0,wh,whd));
38798                 break;
38799               }
38800             else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
38801               switch (boundary_conditions) {
38802               case 0 : // Dirichlet
38803                 _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
38804                 break;
38805               case 1 : // Neumann
38806                 _cimg_correlate_n(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
38807                 break;
38808               case 2 : // Periodic
38809                 _cimg_correlate_n(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
38810                 break;
38811               case 3 : // Mirror
38812                 _cimg_correlate_n(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
38813                 break;
38814               }
38815             else // Non-integer stride or dilation, nearest-neighbor interpolation
38816               switch (boundary_conditions) {
38817               case 0 : // Dirichlet
38818                 _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):0);
38819                 break;
38820               case 1 : // Neumann
38821                 _cimg_correlate_n(float,neumann,I((int)nix,(int)niy,(int)niz,0));
38822                 break;
38823               case 2 : // Periodic
38824                 _cimg_correlate_n(float,periodic,I((int)nix,(int)niy,(int)niz,0));
38825                 break;
38826               case 3 : // Mirror
38827                 _cimg_correlate_n(float,mirror,I((int)nix,(int)niy,(int)niz,0));
38828                 break;
38829               }
38830           } else { // Standard convolution/correlation
38831             if (is_int_stride_dilation) // Integer stride and dilation
38832               switch (boundary_conditions) {
38833               case 0 : // Dirichlet
38834                 _cimg_correlate(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):0);
38835                 break;
38836               case 1 : // Neumann
38837                 _cimg_correlate(int,neumann,I(nix,niy,niz,0,wh,whd));
38838                 break;
38839               case 2 : // Periodic
38840                 _cimg_correlate(int,periodic,I(nix,niy,niz,0,wh,whd));
38841                 break;
38842               case 3 : // Mirror
38843                 _cimg_correlate(int,mirror,I(nix,niy,niz,0,wh,whd));
38844                 break;
38845               }
38846             else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
38847               switch (boundary_conditions) {
38848               case 0 : // Dirichlet
38849                 _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
38850                 break;
38851               case 1 : // Neumann
38852                 _cimg_correlate(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
38853                 break;
38854               case 2 : // Periodic
38855                 _cimg_correlate(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
38856                 break;
38857               case 3 : // Mirror
38858                 _cimg_correlate(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
38859                 break;
38860               }
38861             else // Non-integer stride or dilation, nearest-neighbor interpolation
38862               switch (boundary_conditions) {
38863               case 0 : // Dirichlet
38864                 _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):0);
38865                 break;
38866               case 1 : // Neumann
38867                 _cimg_correlate(float,neumann,I((int)nix,(int)niy,(int)niz,0));
38868                 break;
38869               case 2 : // Periodic
38870                 _cimg_correlate(float,periodic,I((int)nix,(int)niy,(int)niz,0));
38871                 break;
38872               case 3 : // Mirror
38873                 _cimg_correlate(float,mirror,I((int)nix,(int)niy,(int)niz,0));
38874                 break;
38875               }
38876           }
38877           if (channel_mode==2)
38878             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_res;
38879           else if (channel_mode==3)
38880             cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_res;
38881 
38882         } _cimg_abort_catch_openmp2
38883       }
38884       cimg_abort_test;
38885       return res;
38886     }
38887 
38888     //! Convolve image by a kernel.
38889     /**
38890        \param kernel = the correlation kernel.
38891        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
38892        \param is_normalized = enable local normalization.
38893        \param channel_mode Channel processing mode.
38894                            Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
38895        \param xcenter X-coordinate of the kernel center (~0U means 'centered').
38896        \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
38897        \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
38898        \param xstart Starting X-coordinate of the instance image.
38899        \param ystart Starting Y-coordinate of the instance image.
38900        \param zstart Starting Z-coordinate of the instance image.
38901        \param xend Ending X-coordinate of the instance image.
38902        \param yend Ending Y-coordinate of the instance image.
38903        \param zend Ending Z-coordinate of the instance image.
38904        \param xstride Stride along the X-axis.
38905        \param ystride Stride along the Y-axis.
38906        \param zstride Stride along the Z-axis.
38907        \param xdilation Dilation along the X-axis.
38908        \param ydilation Dilation along the Y-axis.
38909        \param zdilation Dilation along the Z-axis.
38910        \param interpolation_type Can be { false=nearest | true=linear }.
38911        \note
38912        - The convolution of the image instance \p *this by the kernel \p kernel is defined to be:
38913        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x - \beta_x\;(i - c_x),\alpha_y\;y
38914                     - \beta_y\;(j - c_y),\alpha_z\;z - \beta_z\;(k - c_z))*kernel(i,j,k).
38915     **/
38916     template<typename t>
38917     CImg<T>& convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38918                       const bool is_normalized=false, const unsigned int channel_mode=1,
38919                       const int xcenter=(int)(~0U>>1),
38920                       const int ycenter=(int)(~0U>>1),
38921                       const int zcenter=(int)(~0U>>1),
38922                       const int xstart=0,
38923                       const int ystart=0,
38924                       const int zstart=0,
38925                       const int xend=(int)(~0U>>1),
38926                       const int yend=(int)(~0U>>1),
38927                       const int zend=(int)(~0U>>1),
38928                       const float xstride=1, const float ystride=1, const float zstride=1,
38929                       const float xdilation=1, const float ydilation=1, const float zdilation=1,
38930                       const bool interpolation_type=false) {
38931       if (is_empty() || !kernel) return *this;
38932       return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode,
38933                           xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38934                           xstride,ystride,zstride,xdilation,ydilation,zdilation,
38935                           interpolation_type).move_to(*this);
38936     }
38937 
38938     //! Convolve image by a kernel \newinstance.
38939     template<typename t>
38940     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38941                                      const bool is_normalized=false, const unsigned int channel_mode=1,
38942                                      const int xcenter=(int)(~0U>>1),
38943                                      const int ycenter=(int)(~0U>>1),
38944                                      const int zcenter=(int)(~0U>>1),
38945                                      const int xstart=0,
38946                                      const int ystart=0,
38947                                      const int zstart=0,
38948                                      const int xend=(int)(~0U>>1),
38949                                      const int yend=(int)(~0U>>1),
38950                                      const int zend=(int)(~0U>>1),
38951                                      const float xstride=1, const float ystride=1, const float zstride=1,
38952                                      const float xdilation=1, const float ydilation=1, const float zdilation=1,
38953                                      const bool interpolation_type=false) const {
38954       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38955                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38956                         xstride,ystride,zstride,xdilation,ydilation,zdilation,
38957                         interpolation_type,true);
38958     }
38959 
38960     //! Cumulate image values, optionally along specified axis.
38961     /**
38962        \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
38963     **/
38964     CImg<T>& cumulate(const char axis=0) {
38965       switch (cimg::lowercase(axis)) {
38966       case 'x' :
38967         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
38968                                                                    _height*_depth*_spectrum>=16))
38969         cimg_forYZC(*this,y,z,c) {
38970           T *ptrd = data(0,y,z,c);
38971           Tlong cumul = (Tlong)0;
38972           cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
38973         }
38974         break;
38975       case 'y' : {
38976         const ulongT w = (ulongT)_width;
38977         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 &&
38978                                                                    _width*_depth*_spectrum>=16))
38979         cimg_forXZC(*this,x,z,c) {
38980           T *ptrd = data(x,0,z,c);
38981           Tlong cumul = (Tlong)0;
38982           cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
38983         }
38984       } break;
38985       case 'z' : {
38986         const ulongT wh = (ulongT)_width*_height;
38987         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 &&
38988                                                                    _width*_depth*_spectrum>=16))
38989         cimg_forXYC(*this,x,y,c) {
38990           T *ptrd = data(x,y,0,c);
38991           Tlong cumul = (Tlong)0;
38992           cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
38993         }
38994       } break;
38995       case 'c' : {
38996         const ulongT whd = (ulongT)_width*_height*_depth;
38997         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
38998                            cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16))
38999         cimg_forXYZ(*this,x,y,z) {
39000           T *ptrd = data(x,y,z,0);
39001           Tlong cumul = (Tlong)0;
39002           cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
39003         }
39004       } break;
39005       default : { // Global cumulation
39006         Tlong cumul = (Tlong)0;
39007         cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
39008       }
39009       }
39010       return *this;
39011     }
39012 
39013     //! Cumulate image values, optionally along specified axis \newinstance.
39014     CImg<Tlong> get_cumulate(const char axis=0) const {
39015       return CImg<Tlong>(*this,false).cumulate(axis);
39016     }
39017 
39018     //! Cumulate image values, along specified axes.
39019     /**
39020        \param axes Cumulation axes, as a C-string.
39021        \note \c axes may contains multiple characters, e.g. \c "xyz"
39022     **/
39023     CImg<T>& cumulate(const char *const axes) {
39024       for (const char *s = axes; *s; ++s) cumulate(*s);
39025       return *this;
39026     }
39027 
39028     //! Cumulate image values, along specified axes \newinstance.
39029     CImg<Tlong> get_cumulate(const char *const axes) const {
39030       return CImg<Tlong>(*this,false).cumulate(axes);
39031     }
39032 
39033     //! Erode image by a structuring element.
39034     /**
39035        \param kernel Structuring element.
39036        \param boundary_conditions Boundary conditions.
39037          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
39038        \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
39039     **/
39040     template<typename t>
39041     CImg<T>& erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
39042                    const bool is_real=false) {
39043       if (is_empty() || !kernel) return *this;
39044       return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
39045     }
39046 
39047     //! Erode image by a structuring element \newinstance.
39048     template<typename t>
39049     CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
39050                              const bool is_real=false) const {
39051       if (is_empty() || !kernel) return *this;
39052       if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
39053       typedef _cimg_Tt Tt;
39054       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
39055       const int
39056         mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
39057         mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
39058         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
39059         w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
39060       const bool
39061         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
39062         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
39063       cimg::unused(is_inner_parallel,is_outer_parallel);
39064       _cimg_abort_init_openmp;
39065       cimg_abort_init;
39066       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
39067       cimg_forC(res,c) _cimg_abort_try_openmp {
39068         cimg_abort_test;
39069         const CImg<T> img = get_shared_channel(c%_spectrum);
39070         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
39071         if (is_real) { // Real erosion
39072           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
39073           for (int z = mz1; z<mze; ++z)
39074             for (int y = my1; y<mye; ++y)
39075               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
39076                 cimg_abort_test2;
39077                 Tt min_val = cimg::type<Tt>::max();
39078                 for (int zm = -mz1; zm<=mz2; ++zm)
39079                   for (int ym = -my1; ym<=my2; ++ym)
39080                     for (int xm = -mx1; xm<=mx2; ++xm) {
39081                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
39082                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
39083                       if (cval<min_val) min_val = cval;
39084                     }
39085                 res(x,y,z,c) = min_val;
39086               } _cimg_abort_catch_openmp2
39087 
39088           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39089           cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39090             cimg_abort_test2;
39091             for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39092               Tt min_val = cimg::type<Tt>::max();
39093               for (int zm = -mz1; zm<=mz2; ++zm)
39094                 for (int ym = -my1; ym<=my2; ++ym)
39095                   for (int xm = -mx1; xm<=mx2; ++xm) {
39096                     const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
39097                     Tt cval;
39098                     switch (boundary_conditions) {
39099                     case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); break;
39100                     case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); break;
39101                     case 2 : {
39102                       const int
39103                         nx = cimg::mod(x + xm,width()),
39104                         ny = cimg::mod(y + ym,height()),
39105                         nz = cimg::mod(z + zm,depth());
39106                       cval = img(nx,ny,nz) - mval;
39107                     } break;
39108                     default : {
39109                       const int
39110                         tx = cimg::mod(x + xm,w2),
39111                         ty = cimg::mod(y + ym,h2),
39112                         tz = cimg::mod(z + zm,d2),
39113                         nx = tx<width()?tx:w2 - tx - 1,
39114                         ny = ty<height()?ty:h2 - ty - 1,
39115                         nz = tz<depth()?tz:d2 - tz - 1;
39116                       cval = img(nx,ny,nz) - mval;
39117                     }
39118                     }
39119                     if (cval<min_val) min_val = cval;
39120                   }
39121               res(x,y,z,c) = min_val;
39122             }
39123           } _cimg_abort_catch_openmp2
39124 
39125         } else { // Binary erosion
39126           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
39127           for (int z = mz1; z<mze; ++z)
39128             for (int y = my1; y<mye; ++y)
39129               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
39130                 cimg_abort_test2;
39131                 Tt min_val = cimg::type<Tt>::max();
39132                 for (int zm = -mz1; zm<=mz2; ++zm)
39133                   for (int ym = -my1; ym<=my2; ++ym)
39134                     for (int xm = -mx1; xm<=mx2; ++xm)
39135                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
39136                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
39137                         if (cval<min_val) min_val = cval;
39138                       }
39139                 res(x,y,z,c) = min_val;
39140               } _cimg_abort_catch_openmp2
39141 
39142           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39143           cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39144             cimg_abort_test2;
39145             for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39146               Tt min_val = cimg::type<Tt>::max();
39147               for (int zm = -mz1; zm<=mz2; ++zm)
39148                 for (int ym = -my1; ym<=my2; ++ym)
39149                   for (int xm = -mx1; xm<=mx2; ++xm) {
39150                     if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
39151                       Tt cval;
39152                       switch (boundary_conditions) {
39153                       case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
39154                       case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
39155                       case 2 : {
39156                         const int
39157                           nx = cimg::mod(x + xm,width()),
39158                           ny = cimg::mod(y + ym,height()),
39159                           nz = cimg::mod(z + zm,depth());
39160                         cval = img(nx,ny,nz);
39161                       } break;
39162                       default : {
39163                         const int
39164                           tx = cimg::mod(x + xm,w2),
39165                           ty = cimg::mod(y + ym,h2),
39166                           tz = cimg::mod(z + zm,d2),
39167                           nx = tx<width()?tx:w2 - tx - 1,
39168                           ny = ty<height()?ty:h2 - ty - 1,
39169                           nz = tz<depth()?tz:d2 - tz - 1;
39170                         cval = img(nx,ny,nz);
39171                       }
39172                       }
39173                       if (cval<min_val) min_val = cval;
39174                     }
39175                   }
39176               res(x,y,z,c) = min_val;
39177             }
39178           } _cimg_abort_catch_openmp2
39179 
39180         }
39181       } _cimg_abort_catch_openmp
39182       cimg_abort_test;
39183       return res;
39184     }
39185 
39186     //! Erode image by a rectangular structuring element of specified size.
39187     /**
39188        \param sx Width of the structuring element.
39189        \param sy Height of the structuring element.
39190        \param sz Depth of the structuring element.
39191     **/
39192     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
39193       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
39194       if (sx>1 && _width>1) { // Along X-axis
39195         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;
39196         CImg<T> buf(L);
39197         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39198         cimg_forYZC(*this,y,z,c) {
39199           T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
39200           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39201           T cur = *ptrs; ptrs+=off; bool is_first = true;
39202           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39203             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
39204           *(ptrd++) = cur;
39205           if (ptrs>=ptrse) {
39206             T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39207           } else {
39208             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39209               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
39210               *(ptrd++) = cur;
39211             }
39212             for (int p = L - s - 1; p>0; --p) {
39213               const T val = *ptrs; ptrs+=off;
39214               if (is_first) {
39215                 const T *nptrs = ptrs - off; cur = val;
39216                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
39217                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
39218               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39219               *(ptrd++) = cur;
39220             }
39221             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39222             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39223               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
39224             }
39225             *(ptrd--) = cur;
39226             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39227               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
39228             }
39229             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39230           }
39231         }
39232       }
39233 
39234       if (sy>1 && _height>1) { // Along Y-axis
39235         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
39236           s2 = _s2>L?L:_s2;
39237         CImg<T> buf(L);
39238         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39239         cimg_forXZC(*this,x,z,c) {
39240           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39241           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39242           T cur = *ptrs; ptrs+=off; bool is_first = true;
39243           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39244             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
39245           }
39246           *(ptrd++) = cur;
39247           if (ptrs>=ptrse) {
39248             T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39249           } else {
39250             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39251               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
39252               *(ptrd++) = cur;
39253             }
39254             for (int p = L - s - 1; p>0; --p) {
39255               const T val = *ptrs; ptrs+=off;
39256               if (is_first) {
39257                 const T *nptrs = ptrs - off; cur = val;
39258                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
39259                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
39260               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39261               *(ptrd++) = cur;
39262             }
39263             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39264             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39265               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
39266             }
39267             *(ptrd--) = cur;
39268             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39269               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
39270             }
39271             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39272           }
39273         }
39274       }
39275 
39276       if (sz>1 && _depth>1) { // Along Z-axis
39277         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
39278           s2 = _s2>L?L:_s2;
39279         CImg<T> buf(L);
39280         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39281         cimg_forXYC(*this,x,y,c) {
39282           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39283           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39284           T cur = *ptrs; ptrs+=off; bool is_first = true;
39285           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39286             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
39287           }
39288           *(ptrd++) = cur;
39289           if (ptrs>=ptrse) {
39290             T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39291           } else {
39292             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39293               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
39294               *(ptrd++) = cur;
39295             }
39296             for (int p = L - s - 1; p>0; --p) {
39297               const T val = *ptrs; ptrs+=off;
39298               if (is_first) {
39299                 const T *nptrs = ptrs - off; cur = val;
39300                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
39301                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
39302               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39303               *(ptrd++) = cur;
39304             }
39305             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39306             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39307               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
39308             }
39309             *(ptrd--) = cur;
39310             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39311               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
39312             }
39313             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39314           }
39315         }
39316       }
39317       return *this;
39318     }
39319 
39320     //! Erode image by a rectangular structuring element of specified size \newinstance.
39321     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
39322       return (+*this).erode(sx,sy,sz);
39323     }
39324 
39325     //! Erode the image by a square structuring element of specified size.
39326     /**
39327        \param s Size of the structuring element.
39328     **/
39329     CImg<T>& erode(const unsigned int s) {
39330       return erode(s,s,s);
39331     }
39332 
39333     //! Erode the image by a square structuring element of specified size \newinstance.
39334     CImg<T> get_erode(const unsigned int s) const {
39335       return (+*this).erode(s);
39336     }
39337 
39338     //! Dilate image by a structuring element.
39339     /**
39340        \param kernel Structuring element.
39341        \param boundary_conditions Boundary conditions.
39342          Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
39343        \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
39344     **/
39345     template<typename t>
39346     CImg<T>& dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
39347                     const bool is_real=false) {
39348       if (is_empty() || !kernel) return *this;
39349       return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
39350     }
39351 
39352     //! Dilate image by a structuring element \newinstance.
39353     template<typename t>
39354     CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
39355                               const bool is_real=false) const {
39356       if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
39357       typedef _cimg_Tt Tt;
39358       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
39359       const int
39360         mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
39361         mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
39362         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
39363         w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
39364       const bool
39365         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
39366         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
39367       cimg::unused(is_inner_parallel,is_outer_parallel);
39368       _cimg_abort_init_openmp;
39369       cimg_abort_init;
39370       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
39371       cimg_forC(res,c) _cimg_abort_try_openmp {
39372         cimg_abort_test;
39373         const CImg<T> img = get_shared_channel(c%_spectrum);
39374         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
39375         if (is_real) { // Real dilation
39376           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
39377           for (int z = mz1; z<mze; ++z)
39378             for (int y = my1; y<mye; ++y)
39379               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
39380                 cimg_abort_test2;
39381                 Tt max_val = cimg::type<Tt>::min();
39382                 for (int zm = -mz1; zm<=mz2; ++zm)
39383                   for (int ym = -my1; ym<=my2; ++ym)
39384                     for (int xm = -mx1; xm<=mx2; ++xm) {
39385                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
39386                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
39387                       if (cval>max_val) max_val = cval;
39388                     }
39389                 res(x,y,z,c) = max_val;
39390               } _cimg_abort_catch_openmp2
39391 
39392           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39393           cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39394             cimg_abort_test2;
39395             for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39396               Tt max_val = cimg::type<Tt>::min();
39397               for (int zm = -mz1; zm<=mz2; ++zm)
39398                 for (int ym = -my1; ym<=my2; ++ym)
39399                   for (int xm = -mx1; xm<=mx2; ++xm) {
39400                     const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
39401                     Tt cval;
39402                     switch (boundary_conditions) {
39403                     case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); break;
39404                     case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); break;
39405                     case 2 : {
39406                       const int
39407                         nx = cimg::mod(x + xm,width()),
39408                         ny = cimg::mod(y + ym,height()),
39409                         nz = cimg::mod(z + zm,depth());
39410                       cval = img(nx,ny,nz) + mval;
39411                     } break;
39412                     default : {
39413                       const int
39414                         tx = cimg::mod(x + xm,w2),
39415                         ty = cimg::mod(y + ym,h2),
39416                         tz = cimg::mod(z + zm,d2),
39417                         nx = tx<width()?tx:w2 - tx - 1,
39418                         ny = ty<height()?ty:h2 - ty - 1,
39419                         nz = tz<depth()?tz:d2 - tz - 1;
39420                       cval = img(nx,ny,nz) + mval;
39421                     }
39422                     }
39423                     if (cval>max_val) max_val = cval;
39424                   }
39425               res(x,y,z,c) = max_val;
39426             }
39427           } _cimg_abort_catch_openmp2
39428 
39429         } else { // Binary dilation
39430           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
39431           for (int z = mz1; z<mze; ++z)
39432             for (int y = my1; y<mye; ++y)
39433               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
39434                 cimg_abort_test2;
39435                 Tt max_val = cimg::type<Tt>::min();
39436                 for (int zm = -mz1; zm<=mz2; ++zm)
39437                   for (int ym = -my1; ym<=my2; ++ym)
39438                     for (int xm = -mx1; xm<=mx2; ++xm)
39439                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
39440                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
39441                         if (cval>max_val) max_val = cval;
39442                       }
39443                 res(x,y,z,c) = max_val;
39444               } _cimg_abort_catch_openmp2
39445 
39446           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
39447           cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
39448             cimg_abort_test2;
39449             for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
39450               Tt max_val = cimg::type<Tt>::min();
39451               for (int zm = -mz1; zm<=mz2; ++zm)
39452                 for (int ym = -my1; ym<=my2; ++ym)
39453                   for (int xm = -mx1; xm<=mx2; ++xm) {
39454                     if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
39455                       Tt cval;
39456                       switch (boundary_conditions) {
39457                       case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
39458                       case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
39459                       case 2 : {
39460                         const int
39461                           nx = cimg::mod(x + xm,width()),
39462                           ny = cimg::mod(y + ym,height()),
39463                           nz = cimg::mod(z + zm,depth());
39464                         cval = img(nx,ny,nz);
39465                       } break;
39466                       default : {
39467                         const int
39468                           tx = cimg::mod(x + xm,w2),
39469                           ty = cimg::mod(y + ym,h2),
39470                           tz = cimg::mod(z + zm,d2),
39471                           nx = tx<width()?tx:w2 - tx - 1,
39472                           ny = ty<height()?ty:h2 - ty - 1,
39473                           nz = tz<depth()?tz:d2 - tz - 1;
39474                         cval = img(nx,ny,nz);
39475                       }
39476                       }
39477                       if (cval>max_val) max_val = cval;
39478                     }
39479                   }
39480               res(x,y,z,c) = max_val;
39481             }
39482           } _cimg_abort_catch_openmp2
39483 
39484         }
39485       } _cimg_abort_catch_openmp
39486       cimg_abort_test;
39487       return res;
39488     }
39489 
39490     //! Dilate image by a rectangular structuring element of specified size.
39491     /**
39492        \param sx Width of the structuring element.
39493        \param sy Height of the structuring element.
39494        \param sz Depth of the structuring element.
39495     **/
39496     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
39497       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
39498       if (sx>1 && _width>1) { // Along X-axis
39499         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;
39500         CImg<T> buf(L);
39501         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39502         cimg_forYZC(*this,y,z,c) {
39503           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39504           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39505           T cur = *ptrs; ptrs+=off; bool is_first = true;
39506           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39507             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39508           }
39509           *(ptrd++) = cur;
39510           if (ptrs>=ptrse) {
39511             T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39512           } else {
39513             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39514               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39515               *(ptrd++) = cur;
39516             }
39517             for (int p = L - s - 1; p>0; --p) {
39518               const T val = *ptrs; ptrs+=off;
39519               if (is_first) {
39520                 const T *nptrs = ptrs - off; cur = val;
39521                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39522                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39523               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39524               *(ptrd++) = cur;
39525             }
39526             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39527             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39528               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39529             }
39530             *(ptrd--) = cur;
39531             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39532               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39533             }
39534             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39535           }
39536         }
39537       }
39538 
39539       if (sy>1 && _height>1) { // Along Y-axis
39540         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
39541           s2 = _s2>L?L:_s2;
39542         CImg<T> buf(L);
39543         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39544         cimg_forXZC(*this,x,z,c) {
39545           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39546           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39547           T cur = *ptrs; ptrs+=off; bool is_first = true;
39548           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39549             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39550           }
39551           *(ptrd++) = cur;
39552           if (ptrs>=ptrse) {
39553             T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39554           } else {
39555             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39556               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39557               *(ptrd++) = cur;
39558             }
39559             for (int p = L - s - 1; p>0; --p) {
39560               const T val = *ptrs; ptrs+=off;
39561               if (is_first) {
39562                 const T *nptrs = ptrs - off; cur = val;
39563                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39564                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39565               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39566               *(ptrd++) = cur;
39567             }
39568             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39569             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39570               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39571             }
39572             *(ptrd--) = cur;
39573             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39574               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39575             }
39576             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39577           }
39578         }
39579       }
39580 
39581       if (sz>1 && _depth>1) { // Along Z-axis
39582         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
39583           s2 = _s2>L?L:_s2;
39584         CImg<T> buf(L);
39585         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
39586         cimg_forXYC(*this,x,y,c) {
39587           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
39588           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
39589           T cur = *ptrs; ptrs+=off; bool is_first = true;
39590           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
39591             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39592           }
39593           *(ptrd++) = cur;
39594           if (ptrs>=ptrse) {
39595             T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
39596           } else {
39597             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
39598               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
39599               *(ptrd++) = cur;
39600             }
39601             for (int p = L - s - 1; p>0; --p) {
39602               const T val = *ptrs; ptrs+=off;
39603               if (is_first) {
39604                 const T *nptrs = ptrs - off; cur = val;
39605                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
39606                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
39607               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
39608               *(ptrd++) = cur;
39609             }
39610             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
39611             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
39612               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
39613             }
39614             *(ptrd--) = cur;
39615             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
39616               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
39617             }
39618             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
39619           }
39620         }
39621       }
39622       return *this;
39623     }
39624 
39625     //! Dilate image by a rectangular structuring element of specified size \newinstance.
39626     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
39627       return (+*this).dilate(sx,sy,sz);
39628     }
39629 
39630     //! Dilate image by a square structuring element of specified size.
39631     /**
39632        \param s Size of the structuring element.
39633     **/
39634     CImg<T>& dilate(const unsigned int s) {
39635       return dilate(s,s,s);
39636     }
39637 
39638     //! Dilate image by a square structuring element of specified size \newinstance.
39639     CImg<T> get_dilate(const unsigned int s) const {
39640       return (+*this).dilate(s);
39641     }
39642 
39643     //! Compute watershed transform.
39644     /**
39645        \param priority Priority map.
39646        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
39647        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
39648        \note Non-zero values of the instance instance are propagated to zero-valued ones according to
39649        specified the priority map.
39650     **/
39651     template<typename t>
39652     CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
39653 #define _cimg_watershed_init(cond,X,Y,Z) \
39654       if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
39655 
39656 #define _cimg_watershed_propagate(cond,X,Y,Z) \
39657       if (cond) { \
39658         if ((*this)(X,Y,Z)) { \
39659           ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
39660           d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
39661           if (d<dmin) { dmin = d; nmin = ns; nlabel = (*this)(xs,ys,zs); } \
39662         } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
39663       }
39664 
39665       if (is_empty()) return *this;
39666       if (!is_sameXYZ(priority))
39667         throw CImgArgumentException(_cimg_instance
39668                                     "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
39669                                     "have different dimensions.",
39670                                     cimg_instance,
39671                                     priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
39672       if (_spectrum!=1) {
39673         cimg_forC(*this,c)
39674           get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
39675         return *this;
39676       }
39677 
39678       CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
39679       CImg<typename cimg::superset2<T,t,int>::type> Q;
39680       unsigned int sizeQ = 0;
39681       int px, nx, py, ny, pz, nz;
39682       bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
39683       const bool is_3d = _depth>1;
39684 
39685       // Find seed points and insert them in priority queue.
39686       unsigned int nb_seeds = 0;
39687       const T *ptrs = _data;
39688       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version
39689         if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
39690         seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
39691         px = x - 1; nx = x + 1;
39692         py = y - 1; ny = y + 1;
39693         pz = z - 1; nz = z + 1;
39694         is_px = px>=0; is_nx = nx<width();
39695         is_py = py>=0; is_ny = ny<height();
39696         is_pz = pz>=0; is_nz = nz<depth();
39697         _cimg_watershed_init(is_px,px,y,z);
39698         _cimg_watershed_init(is_nx,nx,y,z);
39699         _cimg_watershed_init(is_py,x,py,z);
39700         _cimg_watershed_init(is_ny,x,ny,z);
39701         if (is_3d) {
39702           _cimg_watershed_init(is_pz,x,y,pz);
39703           _cimg_watershed_init(is_nz,x,y,nz);
39704         }
39705         if (is_high_connectivity) {
39706           _cimg_watershed_init(is_px && is_py,px,py,z);
39707           _cimg_watershed_init(is_nx && is_py,nx,py,z);
39708           _cimg_watershed_init(is_px && is_ny,px,ny,z);
39709           _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
39710           if (is_3d) {
39711             _cimg_watershed_init(is_px && is_pz,px,y,pz);
39712             _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
39713             _cimg_watershed_init(is_px && is_nz,px,y,nz);
39714             _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
39715             _cimg_watershed_init(is_py && is_pz,x,py,pz);
39716             _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
39717             _cimg_watershed_init(is_py && is_nz,x,py,nz);
39718             _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
39719             _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
39720             _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
39721             _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
39722             _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
39723             _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
39724             _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
39725             _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
39726             _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
39727           }
39728         }
39729         labels(x,y,z) = nb_seeds;
39730       }
39731 
39732       // Start watershed computation.
39733       while (sizeQ) {
39734 
39735         // Get and remove point with maximal priority from the queue.
39736         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
39737         const unsigned int n = labels(x,y,z);
39738         px = x - 1; nx = x + 1;
39739         py = y - 1; ny = y + 1;
39740         pz = z - 1; nz = z + 1;
39741         is_px = px>=0; is_nx = nx<width();
39742         is_py = py>=0; is_ny = ny<height();
39743         is_pz = pz>=0; is_nz = nz<depth();
39744 
39745         // Check labels of the neighbors.
39746         Q._priority_queue_remove(sizeQ);
39747 
39748         unsigned int xs, ys, zs, ns, nmin = 0;
39749         float d, dmin = cimg::type<float>::inf();
39750         T nlabel = (T)0;
39751         _cimg_watershed_propagate(is_px,px,y,z);
39752         _cimg_watershed_propagate(is_nx,nx,y,z);
39753         _cimg_watershed_propagate(is_py,x,py,z);
39754         _cimg_watershed_propagate(is_ny,x,ny,z);
39755         if (is_3d) {
39756           _cimg_watershed_propagate(is_pz,x,y,pz);
39757           _cimg_watershed_propagate(is_nz,x,y,nz);
39758         }
39759         if (is_high_connectivity) {
39760           _cimg_watershed_propagate(is_px && is_py,px,py,z);
39761           _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
39762           _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
39763           _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
39764           if (is_3d) {
39765             _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
39766             _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
39767             _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
39768             _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
39769             _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
39770             _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
39771             _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
39772             _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
39773             _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
39774             _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
39775             _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
39776             _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
39777             _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
39778             _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
39779             _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
39780             _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
39781           }
39782         }
39783         (*this)(x,y,z) = nlabel;
39784         labels(x,y,z) = ++nmin;
39785       }
39786       return *this;
39787     }
39788 
39789     //! Compute watershed transform \newinstance.
39790     template<typename t>
39791     CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
39792       return (+*this).watershed(priority,is_high_connectivity);
39793     }
39794 
39795     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
39796     template<typename tq, typename tv>
39797     bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
39798                                 const unsigned int x, const unsigned int y, const unsigned int z,
39799                                 const unsigned int n=1) {
39800       if (is_queued(x,y,z)) return false;
39801       is_queued(x,y,z) = (tq)n;
39802       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
39803       (*this)(siz - 1,0) = (T)value;
39804       (*this)(siz - 1,1) = (T)x;
39805       (*this)(siz - 1,2) = (T)y;
39806       (*this)(siz - 1,3) = (T)z;
39807       for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
39808         cimg::swap((*this)(pos,0),(*this)(par,0));
39809         cimg::swap((*this)(pos,1),(*this)(par,1));
39810         cimg::swap((*this)(pos,2),(*this)(par,2));
39811         cimg::swap((*this)(pos,3),(*this)(par,3));
39812       }
39813       return true;
39814     }
39815 
39816     CImg<T>& _priority_queue_remove(unsigned int& siz) {
39817       (*this)(0,0) = (*this)(--siz,0);
39818       (*this)(0,1) = (*this)(siz,1);
39819       (*this)(0,2) = (*this)(siz,2);
39820       (*this)(0,3) = (*this)(siz,3);
39821       const float value = (*this)(0,0);
39822       unsigned int pos = 0, swap = 0;
39823       do {
39824         const unsigned int left = 2*pos + 1, right = left + 1;
39825         if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
39826         else if (left<siz && value<(*this)(left,0)) swap = left;
39827         else break;
39828         cimg::swap((*this)(pos,0),(*this)(swap,0));
39829         cimg::swap((*this)(pos,1),(*this)(swap,1));
39830         cimg::swap((*this)(pos,2),(*this)(swap,2));
39831         cimg::swap((*this)(pos,3),(*this)(swap,3));
39832         pos = swap;
39833       } while (true);
39834       return *this;
39835     }
39836 
39837     //! Apply recursive Deriche filter.
39838     /**
39839        \param sigma Standard deviation of the filter.
39840        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
39841        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
39842        \param boundary_conditions Boundary conditions.
39843          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
39844     **/
39845     CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
39846                      const unsigned int boundary_conditions=1) {
39847 #define _cimg_deriche_apply \
39848   CImg<doubleT> Y(N); \
39849   double *ptrY = Y._data, yb = 0, yp = 0; \
39850   T xp = (T)0; \
39851   if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \
39852   for (int m = 0; m<N; ++m) { \
39853     const T xc = *ptrX; ptrX+=off; \
39854     const double yc = *(ptrY++) = (double)(a0*xc + a1*xp - b1*yp - b2*yb); \
39855     xp = xc; yb = yp; yp = yc; \
39856   } \
39857   T xn = (T)0, xa = (T)0; \
39858   double yn = 0, ya = 0; \
39859   if (boundary_conditions) { xn = xa = *(ptrX - off); yn = ya = (double)coefn*xn; } \
39860   for (int n = N - 1; n>=0; --n) { \
39861     const T xc = *(ptrX-=off); \
39862     const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \
39863     xa = xn; xn = xc; ya = yn; yn = yc; \
39864     *ptrX = (T)(*(--ptrY)+yc); \
39865   }
39866 
39867       if (order>2)
39868         throw CImgArgumentException(_cimg_instance
39869                                     "deriche(): Invalid specified order '%d' "
39870                                     "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
39871                                     cimg_instance,
39872                                     order);
39873 
39874       const char naxis = cimg::lowercase(axis);
39875       if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
39876         throw CImgArgumentException(_cimg_instance
39877                                     "deriche(): Invalid specified axis '%c'.",
39878                                     cimg_instance,
39879                                     axis);
39880       const double
39881         nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
39882                                         naxis=='y'?_height:
39883                                         naxis=='z'?_depth:_spectrum)/100,
39884         nnsigma = nsigma<0.1f?0.1f:nsigma;
39885 
39886       if (is_empty() || (nsigma<0.1f && !order)) return *this;
39887       if (boundary_conditions>1) {
39888         const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
39889         switch (naxis) {
39890         case 'x' :
39891           return resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
39892             deriche(nnsigma,order,naxis,1).columns(border,w - 1 + border);
39893         case 'y' :
39894           return resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
39895             deriche(nnsigma,order,naxis,1).rows(border,h - 1 + border);
39896         case 'z' :
39897           return resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
39898             deriche(nnsigma,order,naxis,1).slices(border,d - 1 + border);
39899         default :
39900           return resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
39901             deriche(nnsigma,order,naxis,1).channels(border,d - 1 + border);
39902         }
39903       }
39904 
39905       const double
39906         alpha = 1.695f/nnsigma,
39907         ema = std::exp(-alpha),
39908         ema2 = std::exp(-2*alpha),
39909         b1 = -2*ema,
39910         b2 = ema2;
39911       double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
39912       switch (order) {
39913       case 0 : {
39914         const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
39915         a0 = k;
39916         a1 = k*(alpha - 1)*ema;
39917         a2 = k*(alpha + 1)*ema;
39918         a3 = -k*ema2;
39919       } break;
39920       case 1 : {
39921         const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
39922         a0 = a3 = 0;
39923         a1 = k*ema;
39924         a2 = -a1;
39925       } break;
39926       default : {
39927         const double
39928           ea = std::exp(-alpha),
39929           k = -(ema2 - 1)/(2*alpha*ema),
39930           kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
39931         a0 = kn;
39932         a1 = -kn*(1 + k*alpha)*ema;
39933         a2 = kn*(1 - k*alpha)*ema;
39934         a3 = -kn*ema2;
39935       } break;
39936       }
39937 
39938       coefp = (a0 + a1)/(1 + b1 + b2);
39939       coefn = (a2 + a3)/(1 + b1 + b2);
39940       switch (naxis) {
39941       case 'x' : {
39942         const int N = width();
39943         const ulongT off = 1U;
39944         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39945                                                                    _height*_depth*_spectrum>=16))
39946         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
39947       } break;
39948       case 'y' : {
39949         const int N = height();
39950         const ulongT off = (ulongT)_width;
39951         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39952                                                                    _height*_depth*_spectrum>=16))
39953         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
39954       } break;
39955       case 'z' : {
39956         const int N = depth();
39957         const ulongT off = (ulongT)_width*_height;
39958         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39959                                                                    _height*_depth*_spectrum>=16))
39960         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
39961       } break;
39962       default : {
39963         const int N = spectrum();
39964         const ulongT off = (ulongT)_width*_height*_depth;
39965         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39966                                                                    _height*_depth*_spectrum>=16))
39967         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
39968       }
39969       }
39970       return *this;
39971     }
39972 
39973     //! Apply recursive Deriche filter \newinstance.
39974     CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
39975                              const unsigned int boundary_conditions=1) const {
39976       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
39977     }
39978 
39979     // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
39980     /*
39981        \param ptr the pointer of the data
39982        \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
39983        \param N size of the data
39984        \param off the offset between two data point
39985        \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative
39986        \param boundary_conditions Boundary conditions.
39987          Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39988        \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
39989     */
39990     static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
39991                                       const unsigned int order, const bool boundary_conditions) {
39992       double val[4] = { 0 };  // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
39993       const double
39994         sumsq = filter[0], sum = sumsq * sumsq,
39995         a1 = filter[1], a2 = filter[2], a3 = filter[3],
39996         scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) );
39997       double M[9]; // Triggs matrix
39998       M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2);
39999       M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
40000       M[2] = scaleM * a3 * (a1 + a3 * a2);
40001       M[3] = scaleM * (a1 + a3 * a2);
40002       M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1);
40003       M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.);
40004       M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
40005       M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
40006       M[8] = scaleM * a3 * (a1 + a3 * a2);
40007       switch (order) {
40008       case 0 : {
40009         const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
40010         for (int pass = 0; pass<2; ++pass) {
40011           if (!pass) {
40012             for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
40013           } else {
40014             // Apply Triggs boundary conditions
40015             const double
40016               uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3),
40017               unp  = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
40018             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
40019             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
40020             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
40021             *data = (T)val[0];
40022             data -= off;
40023             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40024           }
40025           for (int n = pass; n<N; ++n) {
40026             val[0] = (*data);
40027             if (pass) val[0] *= sum;
40028             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
40029             *data = (T)val[0];
40030             if (!pass) data += off; else data -= off;
40031             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40032           }
40033           if (!pass) data -= off;
40034         }
40035       } break;
40036       case 1 : {
40037         double x[3]; // [front,center,back]
40038         for (int pass = 0; pass<2; ++pass) {
40039           if (!pass) {
40040             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
40041             for (int k = 0; k<4; ++k) val[k] = 0;
40042           } else {
40043             // Apply Triggs boundary conditions
40044             const double
40045               unp  = val[1], unp1 = val[2], unp2 = val[3];
40046             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
40047             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
40048             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
40049             *data = (T)val[0];
40050             data -= off;
40051             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40052           }
40053           for (int n = pass; n<N - 1; ++n) {
40054             if (!pass) {
40055               x[0] = *(data + off);
40056               val[0] = 0.5f * (x[0] - x[2]);
40057             } else val[0] = (*data) * sum;
40058             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
40059             *data = (T)val[0];
40060             if (!pass) {
40061               data += off;
40062               for (int k = 2; k>0; --k) x[k] = x[k - 1];
40063             } else { data-=off;}
40064             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40065           }
40066           *data = (T)0;
40067         }
40068       } break;
40069       case 2: {
40070         double x[3]; // [front,center,back]
40071         for (int pass = 0; pass<2; ++pass) {
40072           if (!pass) {
40073             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
40074             for (int k = 0; k<4; ++k) val[k] = 0;
40075           } else {
40076             // Apply Triggs boundary conditions
40077             const double
40078               unp  = val[1], unp1 = val[2], unp2 = val[3];
40079             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
40080             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
40081             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
40082             *data = (T)val[0];
40083             data -= off;
40084             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40085           }
40086           for (int n = pass; n<N - 1; ++n) {
40087             if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
40088             else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
40089             for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
40090             *data = (T)val[0];
40091             if (!pass) data += off; else data -= off;
40092             for (int k = 2; k>0; --k) x[k] = x[k - 1];
40093             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40094           }
40095           *data = (T)0;
40096         }
40097       } break;
40098       case 3: {
40099         double x[3]; // [front,center,back]
40100         for (int pass = 0; pass<2; ++pass) {
40101           if (!pass) {
40102             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
40103             for (int k = 0; k<4; ++k) val[k] = 0;
40104           } else {
40105             // Apply Triggs boundary conditions
40106             const double
40107               unp = val[1], unp1 = val[2], unp2 = val[3];
40108             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
40109             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
40110             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
40111             *data = (T)val[0];
40112             data -= off;
40113             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40114           }
40115           for (int n = pass; n<N - 1; ++n) {
40116             if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
40117             else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
40118             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
40119             *data = (T)val[0];
40120             if (!pass) data += off; else data -= off;
40121             for (int k = 2; k>0; --k) x[k] = x[k - 1];
40122             for (int k = 3; k>0; --k) val[k] = val[k - 1];
40123           }
40124           *data = (T)0;
40125         }
40126       } break;
40127       }
40128     }
40129 
40130     //! Van Vliet recursive Gaussian filter.
40131     /**
40132        \param sigma standard deviation of the Gaussian filter
40133        \param order the order of the filter 0,1,2,3
40134        \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
40135        \param boundary_conditions Boundary conditions.
40136          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
40137        \note dirichlet boundary condition has a strange behavior
40138 
40139        I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
40140        IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
40141 
40142        (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
40143 
40144        Boundary conditions (only for order 0) using Triggs matrix, from
40145        B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
40146        recursive filtering. IEEE Trans. Signal Processing,
40147        vol. 54, pp. 2365-2367, 2006.
40148     **/
40149     CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
40150                       const unsigned int boundary_conditions=1) {
40151 
40152       if (order>2)
40153         throw CImgArgumentException(_cimg_instance
40154                                     "deriche(): Invalid specified order '%d' "
40155                                     "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
40156                                     cimg_instance,
40157                                     order);
40158 
40159       const char naxis = cimg::lowercase(axis);
40160       if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
40161         throw CImgArgumentException(_cimg_instance
40162                                     "deriche(): Invalid specified axis '%c'.",
40163                                     cimg_instance,
40164                                     axis);
40165       const double
40166         nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
40167                                         naxis=='y'?_height:
40168                                         naxis=='z'?_depth:_spectrum)/100,
40169         nnsigma = nsigma<0.5f?0.5f:nsigma;
40170 
40171       if (is_empty() || (nsigma<0.1f && !order)) return *this;
40172       if (nsigma<0.5f) return deriche(nsigma,order,axis,boundary_conditions);
40173       if (!cimg::type<T>::is_float())
40174         return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
40175 
40176       if (boundary_conditions>1) {
40177         const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
40178         switch (naxis) {
40179         case 'x' :
40180           return resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
40181             vanvliet(nnsigma,order,naxis,1).columns(border,w - 1 + border);
40182         case 'y' :
40183           return resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
40184             vanvliet(nnsigma,order,naxis,1).rows(border,h - 1 + border);
40185         case 'z' :
40186           return resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
40187             vanvliet(nnsigma,order,naxis,1).slices(border,d - 1 + border);
40188         default :
40189           return resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
40190             vanvliet(nnsigma,order,naxis,1).channels(border,d - 1 + border);
40191         }
40192       }
40193 
40194       const double
40195         m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
40196         m1sq = m1 * m1, m2sq = m2 * m2,
40197         q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
40198         qsq = q * q,
40199         scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
40200         b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
40201         b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
40202         b3 = -qsq * q / scale,
40203         B = ( m0 * (m1sq + m2sq) ) / scale;
40204       double filter[4];
40205       filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
40206       switch (naxis) {
40207       case 'x' : {
40208         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40209                                                                    _height*_depth*_spectrum>=16))
40210         cimg_forYZC(*this,y,z,c)
40211           _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
40212       } break;
40213       case 'y' : {
40214         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40215                                                                    _height*_depth*_spectrum>=16))
40216         cimg_forXZC(*this,x,z,c)
40217           _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
40218       } break;
40219       case 'z' : {
40220         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40221                                                                    _height*_depth*_spectrum>=16))
40222         cimg_forXYC(*this,x,y,c)
40223           _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
40224                                 order,boundary_conditions);
40225       } break;
40226       default : {
40227         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40228                                                                    _height*_depth*_spectrum>=16))
40229         cimg_forXYZ(*this,x,y,z)
40230           _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
40231                                 order,boundary_conditions);
40232       }
40233       }
40234       return *this;
40235     }
40236 
40237     //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
40238     CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
40239                               const unsigned int boundary_conditions=1) const {
40240       return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
40241     }
40242 
40243     //! Blur image.
40244     /**
40245        \param sigma_x Standard deviation of the blur, along the X-axis.
40246        \param sigma_y Standard deviation of the blur, along the Y-axis.
40247        \param sigma_z Standard deviation of the blur, along the Z-axis.
40248        \param boundary_conditions Boundary conditions.
40249          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
40250        \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
40251        \note
40252        - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian).
40253        - This is a recursive algorithm, not depending on the values of the standard deviations.
40254        \see deriche(), vanvliet().
40255     **/
40256     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
40257                   const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
40258       if (is_empty()) return *this;
40259       if (is_gaussian) {
40260         if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
40261         if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
40262         if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
40263       } else {
40264         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
40265         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
40266         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
40267       }
40268       return *this;
40269     }
40270 
40271     //! Blur image \newinstance.
40272     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
40273                           const unsigned int boundary_conditions=1, const bool is_gaussian=true) const {
40274       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
40275     }
40276 
40277     //! Blur image isotropically.
40278     /**
40279        \param sigma Standard deviation of the blur.
40280        \param boundary_conditions Boundary conditions.
40281          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
40282        \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise.
40283        \see deriche(), vanvliet().
40284     **/
40285     CImg<T>& blur(const float sigma, const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
40286       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
40287       return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
40288     }
40289 
40290     //! Blur image isotropically \newinstance.
40291     CImg<Tfloat> get_blur(const float sigma, const unsigned int boundary_conditions=1,
40292                           const bool is_gaussian=true) const {
40293       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
40294     }
40295 
40296     //! Blur image anisotropically, directed by a field of diffusion tensors.
40297     /**
40298        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
40299        \param amplitude Amplitude of the smoothing.
40300        \param dl Spatial discretization.
40301        \param da Angular discretization.
40302        \param gauss_prec Precision of the diffusion process.
40303        \param interpolation_type Interpolation scheme.
40304          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
40305        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
40306     **/
40307     template<typename t>
40308     CImg<T>& blur_anisotropic(const CImg<t>& G,
40309                               const float amplitude=60, const float dl=0.8f, const float da=30,
40310                               const float gauss_prec=2, const unsigned int interpolation_type=0,
40311                               const bool is_fast_approx=1) {
40312 
40313       // Check arguments and init variables
40314       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
40315         throw CImgArgumentException(_cimg_instance
40316                                     "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
40317                                     cimg_instance,
40318                                     G._width,G._height,G._depth,G._spectrum,G._data);
40319       if (is_empty() || dl<0) return *this;
40320       const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
40321       unsigned int iamplitude = cimg::round(namplitude);
40322       const bool is_3d = (G._spectrum==6);
40323       T val_min, val_max = max_min(val_min);
40324       _cimg_abort_init_openmp;
40325       cimg_abort_init;
40326 
40327       if (da<=0) {  // Iterated oriented Laplacians
40328         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
40329         for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
40330           Tfloat *ptrd = velocity._data, veloc_max = 0;
40331           if (is_3d) // 3D version
40332             cimg_forC(*this,c) {
40333               cimg_abort_test;
40334               CImg_3x3x3(I,Tfloat);
40335               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40336                 const Tfloat
40337                   ixx = Incc + Ipcc - 2*Iccc,
40338                   ixy = (Innc + Ippc - Inpc - Ipnc)/4,
40339                   ixz = (Incn + Ipcp - Incp - Ipcn)/4,
40340                   iyy = Icnc + Icpc - 2*Iccc,
40341                   iyz = (Icnn + Icpp - Icnp - Icpn)/4,
40342                   izz = Iccn + Iccp - 2*Iccc,
40343                   veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
40344                                    G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
40345                 *(ptrd++) = veloc;
40346                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40347               }
40348             }
40349           else // 2D version
40350             cimg_forZC(*this,z,c) {
40351               cimg_abort_test;
40352               CImg_3x3(I,Tfloat);
40353               cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
40354                 const Tfloat
40355                   ixx = Inc + Ipc - 2*Icc,
40356                   ixy = (Inn + Ipp - Inp - Ipn)/4,
40357                   iyy = Icn + Icp - 2*Icc,
40358                   veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
40359                 *(ptrd++) = veloc;
40360                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40361               }
40362             }
40363           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
40364         }
40365       } else { // LIC-based smoothing
40366         const ulongT whd = (ulongT)_width*_height*_depth;
40367         const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
40368         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
40369         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
40370         int N = 0;
40371         if (is_3d) { // 3D version
40372           for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
40373             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
40374               da2 = datmp<1?360.f:datmp;
40375             for (float theta = 0; theta<360; (theta+=da2),++N) {
40376               const float
40377                 thetar = (float)(theta*cimg::PI/180),
40378                 vx = (float)(std::cos(thetar)*std::cos(phir)),
40379                 vy = (float)(std::sin(thetar)*std::cos(phir)),
40380                 vz = (float)std::sin(phir);
40381               const t
40382                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
40383                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
40384               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);
40385               cimg_forXYZ(G,xg,yg,zg) {
40386                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
40387                 const float
40388                   u = (float)(a*vx + b*vy + c*vz),
40389                   v = (float)(b*vx + d*vy + e*vz),
40390                   w = (float)(c*vx + e*vy + f*vz),
40391                   n = 1e-5f + cimg::hypot(u,v,w),
40392                   dln = dl/n;
40393                 *(pd0++) = (Tfloat)(u*dln);
40394                 *(pd1++) = (Tfloat)(v*dln);
40395                 *(pd2++) = (Tfloat)(w*dln);
40396                 *(pd3++) = (Tfloat)n;
40397               }
40398 
40399               cimg_abort_test;
40400               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40401                                  cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
40402                                  firstprivate(val))
40403               cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
40404                 cimg_abort_test2;
40405                 cimg_forX(*this,x) {
40406                   val.fill(0);
40407                   const float
40408                     n = (float)W(x,y,z,3),
40409                     fsigma = (float)(n*sqrt2amplitude),
40410                     fsigma2 = 2*fsigma*fsigma,
40411                     length = gauss_prec*fsigma;
40412                   float
40413                     S = 0,
40414                     X = (float)x,
40415                     Y = (float)y,
40416                     Z = (float)z;
40417                   switch (interpolation_type) {
40418                   case 0 : { // Nearest neighbor
40419                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
40420                       const int
40421                         cx = (int)(X + 0.5f),
40422                         cy = (int)(Y + 0.5f),
40423                         cz = (int)(Z + 0.5f);
40424                       const float
40425                         u = (float)W(cx,cy,cz,0),
40426                         v = (float)W(cx,cy,cz,1),
40427                         w = (float)W(cx,cy,cz,2);
40428                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
40429                       else {
40430                         const float coef = (float)std::exp(-l*l/fsigma2);
40431                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
40432                         S+=coef;
40433                       }
40434                       X+=u; Y+=v; Z+=w;
40435                     }
40436                   } break;
40437                   case 1 : { // Linear interpolation
40438                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
40439                       const float
40440                         u = (float)(W._linear_atXYZ(X,Y,Z,0)),
40441                         v = (float)(W._linear_atXYZ(X,Y,Z,1)),
40442                         w = (float)(W._linear_atXYZ(X,Y,Z,2));
40443                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
40444                       else {
40445                         const float coef = (float)std::exp(-l*l/fsigma2);
40446                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
40447                         S+=coef;
40448                       }
40449                       X+=u; Y+=v; Z+=w;
40450                     }
40451                   } break;
40452                   default : { // 2nd order Runge Kutta
40453                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
40454                       const float
40455                         u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
40456                         v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
40457                         w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
40458                         u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
40459                         v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
40460                         w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
40461                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
40462                       else {
40463                         const float coef = (float)std::exp(-l*l/fsigma2);
40464                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
40465                         S+=coef;
40466                       }
40467                       X+=u; Y+=v; Z+=w;
40468                     }
40469                   } break;
40470                   }
40471                   Tfloat *ptrd = res.data(x,y,z);
40472                   if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
40473                   else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
40474                 }
40475               } _cimg_abort_catch_openmp2
40476             }
40477           }
40478         } else { // 2D LIC algorithm
40479           for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
40480             const float thetar = (float)(theta*cimg::PI/180),
40481               vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
40482             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
40483             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
40484             cimg_forXY(G,xg,yg) {
40485               const t a = *(pa++), b = *(pb++), c = *(pc++);
40486               const float
40487                 u = (float)(a*vx + b*vy),
40488                 v = (float)(b*vx + c*vy),
40489                 n = std::max(1e-5f,cimg::hypot(u,v)),
40490                 dln = dl/n;
40491               *(pd0++) = (Tfloat)(u*dln);
40492               *(pd1++) = (Tfloat)(v*dln);
40493               *(pd2++) = (Tfloat)n;
40494             }
40495 
40496             cimg_abort_test;
40497             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
40498                                firstprivate(val))
40499             cimg_forY(*this,y) _cimg_abort_try_openmp2 {
40500               cimg_abort_test2;
40501               cimg_forX(*this,x) {
40502                 val.fill(0);
40503                 const float
40504                   n = (float)W(x,y,0,2),
40505                   fsigma = (float)(n*sqrt2amplitude),
40506                   fsigma2 = 2*fsigma*fsigma,
40507                   length = gauss_prec*fsigma;
40508                 float
40509                   S = 0,
40510                   X = (float)x,
40511                   Y = (float)y;
40512                 switch (interpolation_type) {
40513                 case 0 : { // Nearest-neighbor
40514                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40515                     const int
40516                       cx = (int)(X + 0.5f),
40517                       cy = (int)(Y + 0.5f);
40518                     const float
40519                       u = (float)W(cx,cy,0,0),
40520                       v = (float)W(cx,cy,0,1);
40521                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
40522                     else {
40523                       const float coef = (float)std::exp(-l*l/fsigma2);
40524                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
40525                       S+=coef;
40526                     }
40527                     X+=u; Y+=v;
40528                   }
40529                 } break;
40530                 case 1 : { // Linear interpolation
40531                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40532                     const float
40533                       u = (float)(W._linear_atXY(X,Y,0,0)),
40534                       v = (float)(W._linear_atXY(X,Y,0,1));
40535                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
40536                     else {
40537                       const float coef = (float)std::exp(-l*l/fsigma2);
40538                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
40539                       S+=coef;
40540                     }
40541                     X+=u; Y+=v;
40542                   }
40543                 } break;
40544                 default : { // 2nd-order Runge-kutta interpolation
40545                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
40546                     const float
40547                       u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
40548                       v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
40549                       u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
40550                       v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
40551                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
40552                     else {
40553                       const float coef = (float)std::exp(-l*l/fsigma2);
40554                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
40555                       S+=coef;
40556                     }
40557                     X+=u; Y+=v;
40558                   }
40559                 }
40560                 }
40561                 Tfloat *ptrd = res.data(x,y);
40562                 if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
40563                 else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
40564               }
40565             } _cimg_abort_catch_openmp2
40566           }
40567         }
40568         const Tfloat *ptrs = res._data;
40569         cimg_for(*this,ptrd,T) {
40570           const Tfloat _val = *(ptrs++)/N;
40571           *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
40572         }
40573       }
40574       cimg_abort_test;
40575       return *this;
40576     }
40577 
40578     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
40579     template<typename t>
40580     CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
40581                                       const float amplitude=60, const float dl=0.8f, const float da=30,
40582                                       const float gauss_prec=2, const unsigned int interpolation_type=0,
40583                                       const bool is_fast_approx=true) const {
40584       return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
40585     }
40586 
40587     //! Blur image anisotropically, in an edge-preserving way.
40588     /**
40589        \param amplitude Amplitude of the smoothing.
40590        \param sharpness Sharpness.
40591        \param anisotropy Anisotropy.
40592        \param alpha Standard deviation of the gradient blur.
40593        \param sigma Standard deviation of the structure tensor blur.
40594        \param dl Spatial discretization.
40595        \param da Angular discretization.
40596        \param gauss_prec Precision of the diffusion process.
40597        \param interpolation_type Interpolation scheme.
40598          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
40599        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
40600      **/
40601     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
40602                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
40603                               const float gauss_prec=2, const unsigned int interpolation_type=0,
40604                               const bool is_fast_approx=true) {
40605       const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
40606       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
40607       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
40608                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
40609     }
40610 
40611     //! Blur image anisotropically, in an edge-preserving way \newinstance.
40612     CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
40613                                       const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
40614                                       const float da=30, const float gauss_prec=2,
40615                                       const unsigned int interpolation_type=0,
40616                                       const bool is_fast_approx=true) const {
40617       return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
40618                                                         interpolation_type,is_fast_approx);
40619     }
40620 
40621     //! Blur image, with the joint bilateral filter.
40622     /**
40623        \param guide Image used to model the smoothing weights.
40624        \param sigma_x Amount of blur along the X-axis.
40625        \param sigma_y Amount of blur along the Y-axis.
40626        \param sigma_z Amount of blur along the Z-axis.
40627        \param sigma_r Amount of blur along the value axis.
40628        \param sampling_x Amount of downsampling along the X-axis used for the approximation.
40629          Defaults (0) to sigma_x.
40630        \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
40631          Defaults (0) to sigma_y.
40632        \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
40633          Defaults (0) to sigma_z.
40634        \param sampling_r Amount of downsampling along the value axis used for the approximation.
40635          Defaults (0) to sigma_r.
40636        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
40637        (extended for 3D volumetric images).
40638        It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
40639     **/
40640     template<typename t>
40641     CImg<T>& blur_bilateral(const CImg<t>& guide,
40642                             const float sigma_x, const float sigma_y,
40643                             const float sigma_z, const float sigma_r,
40644                             const float sampling_x, const float sampling_y,
40645                             const float sampling_z, const float sampling_r) {
40646       if (!is_sameXYZ(guide))
40647         throw CImgArgumentException(_cimg_instance
40648                                     "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
40649                                     cimg_instance,
40650                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
40651       if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
40652       T edge_min, edge_max = guide.max_min(edge_min);
40653       if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
40654       const float
40655         edge_delta = (float)(edge_max - edge_min),
40656         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
40657         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
40658         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
40659         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100,
40660         _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f),
40661         _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f),
40662         _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f),
40663         _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
40664         derived_sigma_x = _sigma_x / _sampling_x,
40665         derived_sigma_y = _sigma_y / _sampling_y,
40666         derived_sigma_z = _sigma_z / _sampling_z,
40667         derived_sigma_r = _sigma_r / _sampling_r;
40668       const int
40669         padding_x = (int)(2*derived_sigma_x) + 1,
40670         padding_y = (int)(2*derived_sigma_y) + 1,
40671         padding_z = (int)(2*derived_sigma_z) + 1,
40672         padding_r = (int)(2*derived_sigma_r) + 1;
40673       const unsigned int
40674         bx = (unsigned int)((_width  - 1)/_sampling_x + 1 + 2*padding_x),
40675         by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
40676         bz = (unsigned int)((_depth  - 1)/_sampling_z + 1 + 2*padding_z),
40677         br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
40678       if (bx>0 || by>0 || bz>0 || br>0) {
40679         const bool is_3d = (_depth>1);
40680         if (is_3d) { // 3D version of the algorithm
40681           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
40682           cimg_forC(*this,c) {
40683             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
40684             bgrid.fill(0); bgridw.fill(0);
40685             cimg_forXYZ(*this,x,y,z) {
40686               const T val = (*this)(x,y,z,c);
40687               const float edge = (float)_guide(x,y,z);
40688               const int
40689                 X = (int)cimg::round(x/_sampling_x) + padding_x,
40690                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
40691                 Z = (int)cimg::round(z/_sampling_z) + padding_z,
40692                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
40693               bgrid(X,Y,Z,R)+=(float)val;
40694               bgridw(X,Y,Z,R)+=1;
40695             }
40696             bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
40697             bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
40698 
40699             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096))
40700             cimg_forXYZ(*this,x,y,z) {
40701               const float edge = (float)_guide(x,y,z);
40702               const float
40703                 X = x/_sampling_x + padding_x,
40704                 Y = y/_sampling_y + padding_y,
40705                 Z = z/_sampling_z + padding_z,
40706                 R = (edge - edge_min)/_sampling_r + padding_r;
40707               const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
40708               (*this)(x,y,z,c) = (T)(bval0/bval1);
40709             }
40710           }
40711         } else { // 2D version of the algorithm
40712           CImg<floatT> bgrid(bx,by,br,2);
40713           cimg_forC(*this,c) {
40714             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
40715             bgrid.fill(0);
40716             cimg_forXY(*this,x,y) {
40717               const T val = (*this)(x,y,c);
40718               const float edge = (float)_guide(x,y);
40719               const int
40720                 X = (int)cimg::round(x/_sampling_x) + padding_x,
40721                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
40722                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
40723               bgrid(X,Y,R,0)+=(float)val;
40724               bgrid(X,Y,R,1)+=1;
40725             }
40726             bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
40727 
40728             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096))
40729             cimg_forXY(*this,x,y) {
40730               const float edge = (float)_guide(x,y);
40731               const float
40732                 X = x/_sampling_x + padding_x,
40733                 Y = y/_sampling_y + padding_y,
40734                 R = (edge - edge_min)/_sampling_r + padding_r;
40735               const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
40736               (*this)(x,y,c) = (T)(bval0/bval1);
40737             }
40738           }
40739         }
40740       }
40741       return *this;
40742     }
40743 
40744     //! Blur image, with the joint bilateral filter \newinstance.
40745     template<typename t>
40746     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
40747                                     const float sigma_x, const float sigma_y,
40748                                     const float sigma_z, const float sigma_r,
40749                                     const float sampling_x, const float sampling_y,
40750                                     const float sampling_z, const float sampling_r) const {
40751       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
40752                                                       sampling_x,sampling_y,sampling_z,sampling_r);
40753     }
40754 
40755     //! Blur image using the joint bilateral filter.
40756     /**
40757        \param guide Image used to model the smoothing weights.
40758        \param sigma_s Amount of blur along the XYZ-axes.
40759        \param sigma_r Amount of blur along the value axis.
40760        \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
40761        \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
40762     **/
40763     template<typename t>
40764     CImg<T>& blur_bilateral(const CImg<t>& guide,
40765                             const float sigma_s, const float sigma_r,
40766                             const float sampling_s=0, const float sampling_r=0) {
40767       const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
40768       return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
40769     }
40770 
40771     //! Blur image using the bilateral filter \newinstance.
40772     template<typename t>
40773     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
40774                                     const float sigma_s, const float sigma_r,
40775                                     const float sampling_s=0, const float sampling_r=0) const {
40776       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
40777     }
40778 
40779     // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
40780     /*
40781       \param ptr the pointer of the data
40782       \param N size of the data
40783       \param boxsize Size of the box filter (can be subpixel).
40784       \param off the offset between two data point
40785       \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative.
40786       \param boundary_conditions Boundary conditions.
40787         Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
40788     */
40789     static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
40790                                      const int order, const unsigned int boundary_conditions,
40791                                      const unsigned int nb_iter) {
40792       const int nboundary_conditions = boundary_conditions>1 && boxsize<=3?1:boundary_conditions;
40793 
40794       // Smooth.
40795       if (boxsize>1 && nb_iter) {
40796         const int w2 = (int)(boxsize - 1)/2;
40797         const unsigned int winsize = 2*w2 + 1U;
40798         const double frac = (boxsize - winsize)/2.;
40799         CImg<T> win(winsize);
40800         for (unsigned int iter = 0; iter<nb_iter; ++iter) {
40801           Tdouble sum = 0; // window sum
40802           for (int x = -w2; x<=w2; ++x) {
40803             win[x + w2] = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x);
40804             sum+=win[x + w2];
40805           }
40806           int ifirst = 0, ilast = 2*w2;
40807           T
40808             prev = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-w2 - 1),
40809             next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,w2 + 1);
40810           for (int x = 0; x < N - 1; ++x) {
40811             const double sum2 = sum + frac * (prev + next);
40812             ptr[x*off] = (T)(sum2/boxsize);
40813             prev = win[ifirst];
40814             sum-=prev;
40815             ifirst = (int)((ifirst + 1)%winsize);
40816             ilast = (int)((ilast + 1)%winsize);
40817             win[ilast] = next;
40818             sum+=next;
40819             next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + w2 + 2);
40820           }
40821           const double sum2 = sum + frac * (prev + next);
40822           ptr[(N - 1)*off] = (T)(sum2/boxsize);
40823         }
40824       }
40825 
40826       // Derive.
40827       switch (order) {
40828       case 0 :
40829         break;
40830       case 1 : {
40831         Tfloat
40832           p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
40833           c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
40834           n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
40835         for (int x = 0; x<N - 1; ++x) {
40836           ptr[x*off] = (T)((n-p)/2.);
40837           p = c;
40838           c = n;
40839           n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
40840         }
40841         ptr[(N - 1)*off] = (T)((n-p)/2.);
40842       } break;
40843       case 2: {
40844         Tfloat
40845           p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
40846           c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
40847           n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
40848         for (int x = 0; x<N - 1; ++x) {
40849           ptr[x*off] = (T)(n - 2*c + p);
40850           p = c;
40851           c = n;
40852           n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
40853         }
40854         ptr[(N - 1)*off] = (T)(n - 2*c + p);
40855       } break;
40856       }
40857     }
40858 
40859     static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
40860                                    const unsigned int boundary_conditions, const int x) {
40861       switch (boundary_conditions) {
40862       case 0 : // Dirichlet
40863         return x<0 || x>=N?(T)0:ptr[x*off];
40864       case 1 : { // Neumann
40865         const int nx = x<0?0:x>=N?N - 1:x;
40866         return ptr[nx*off];
40867       }
40868       case 2 : { // Periodic
40869         const int nx = cimg::mod(x,N);
40870         return ptr[nx*off];
40871       }
40872       default : { // Mirror
40873         const int
40874           N2 = 2*N,
40875           tx = cimg::mod(x,N2),
40876           nx = tx<N?tx:N2 - tx - 1;
40877         return ptr[nx*off];
40878       }
40879       }
40880       return (T)0;
40881     }
40882 
40883     // Apply box filter of order 0,1,2.
40884     /**
40885       \param boxsize Size of the box window (can be subpixel)
40886       \param order the order of the filter 0,1 or 2.
40887       \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
40888       \param boundary_conditions Boundary conditions.
40889         Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
40890       \param nb_iter Number of filter iterations.
40891     **/
40892     CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
40893                        const unsigned int boundary_conditions=1,
40894                        const unsigned int nb_iter=1) {
40895       const char naxis = cimg::lowercase(axis);
40896       const float nboxsize = boxsize>=0?boxsize:-boxsize*
40897         (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
40898       if (is_empty() || !nboxsize || (nboxsize<=1 && !order)) return *this;
40899       switch (naxis) {
40900       case 'x' : {
40901         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40902                                                                    _height*_depth*_spectrum>=16))
40903         cimg_forYZC(*this,y,z,c)
40904           _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
40905       } break;
40906       case 'y' : {
40907         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40908                                                                    _height*_depth*_spectrum>=16))
40909         cimg_forXZC(*this,x,z,c)
40910           _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
40911       } break;
40912       case 'z' : {
40913         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40914                                                                    _height*_depth*_spectrum>=16))
40915         cimg_forXYC(*this,x,y,c)
40916           _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
40917       } break;
40918       default : {
40919         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40920                                                                    _height*_depth*_spectrum>=16))
40921         cimg_forXYZ(*this,x,y,z)
40922           _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
40923                                order,boundary_conditions,nb_iter);
40924       }
40925       }
40926       return *this;
40927     }
40928 
40929     // Apply box filter of order 0,1 or 2 \newinstance.
40930     CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
40931                                const unsigned int boundary_conditions=1,
40932                                const unsigned int nb_iter=1) const {
40933       return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
40934     }
40935 
40936     //! Blur image with a box filter.
40937     /**
40938        \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
40939        \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
40940        \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
40941        \param boundary_conditions Boundary conditions.
40942          Can be <tt>{ false=dirichlet | true=neumann | 2=periodic | 3=mirror }</tt>.
40943        \param nb_iter Number of filter iterations.
40944        \note
40945        - This is a recursive algorithm, not depending on the values of the box kernel size.
40946        \see blur().
40947     **/
40948     CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
40949                       const unsigned int boundary_conditions=1,
40950                       const unsigned int nb_iter=1) {
40951       if (is_empty()) return *this;
40952       if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
40953       if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
40954       if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
40955       return *this;
40956     }
40957 
40958     //! Blur image with a box filter \newinstance.
40959     CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
40960                               const unsigned int boundary_conditions=1) const {
40961       return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
40962     }
40963 
40964     //! Blur image with a box filter.
40965     /**
40966        \param boxsize Size of the box window (can be subpixel).
40967        \param boundary_conditions Boundary conditions.
40968          Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
40969        \see deriche(), vanvliet().
40970     **/
40971     CImg<T>& blur_box(const float boxsize, const unsigned int boundary_conditions=1) {
40972       const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
40973       return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
40974     }
40975 
40976     //! Blur image with a box filter \newinstance.
40977     CImg<Tfloat> get_blur_box(const float boxsize, const unsigned int boundary_conditions=1) const {
40978       return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
40979     }
40980 
40981     //! Blur image, with the image guided filter.
40982     /**
40983        \param guide Image used to guide the smoothing process.
40984        \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
40985        \param regularization Regularization parameter.
40986                              If negative, it is expressed as a percentage of the guide value range.
40987        \note This method implements the filtering algorithm described in:
40988        He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
40989        IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
40990     **/
40991     template<typename t>
40992     CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
40993       return get_blur_guided(guide,radius,regularization).move_to(*this);
40994     }
40995 
40996     //! Blur image, with the image guided filter \newinstance.
40997     template<typename t>
40998     CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
40999       if (!is_sameXYZ(guide))
41000         throw CImgArgumentException(_cimg_instance
41001                                     "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
41002                                     cimg_instance,
41003                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
41004       if (is_empty() || !radius) return *this;
41005       const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
41006       float _regularization = regularization;
41007       if (regularization<0) {
41008         T edge_min, edge_max = guide.max_min(edge_min);
41009         if (edge_min==edge_max) return *this;
41010         _regularization = -regularization*(edge_max - edge_min)/100;
41011       }
41012       _regularization = std::max(_regularization,0.01f);
41013       const unsigned int psize = (unsigned int)(1 + 2*_radius);
41014       CImg<Tfloat>
41015         mean_p = get_blur_box(psize,true),
41016         mean_I = guide.get_blur_box(psize,true).resize(mean_p),
41017         cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
41018         var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
41019         &a = cov_Ip.div(var_I+=_regularization),
41020         &b = mean_p-=a.get_mul(mean_I);
41021       a.blur_box(psize,true);
41022       b.blur_box(psize,true);
41023       return a.mul(guide)+=b;
41024     }
41025 
41026     //! Blur image using patch-based space.
41027     /**
41028        \param guide Image used to model the smoothing weights.
41029        \param sigma_s Amount of blur along the XYZ-axes.
41030        \param sigma_r Amount of blur along the value axis.
41031        \param patch_size Size of the patches.
41032        \param lookup_size Size of the window to search similar patches.
41033        \param smoothness Smoothness for the patch comparison.
41034        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
41035     **/
41036     template<typename t>
41037     CImg<T>& blur_patch(const CImg<t>& guide,
41038                         const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
41039                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
41040       if (is_empty() || !patch_size || !lookup_size) return *this;
41041       return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
41042     }
41043 
41044     //! Blur image using patch-based space \newinstance.
41045     template<typename t>
41046     CImg<Tfloat> get_blur_patch(const CImg<t>& guide,
41047                                 const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
41048                                 const unsigned int lookup_size=4, const float smoothness=0,
41049                                 const bool is_fast_approx=true) const {
41050 
41051 #define _cimg_blur_patch3d_fast(N) { \
41052       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
41053                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
41054                          firstprivate(P,Q)) \
41055       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
41056         cimg_abort_test2; \
41057         cimg_def##N##x##N##x##N(res,x,y,z); \
41058         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
41059         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
41060                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
41061         tfloat sum_weights = 0; \
41062         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
41063           if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) { \
41064             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
41065             tfloat distance2 = 0; \
41066             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
41067             distance2/=Pnorm; \
41068             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
41069               alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0:1; \
41070             sum_weights+=weight; \
41071             cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
41072           } \
41073         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
41074         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
41075     } _cimg_abort_catch_openmp2 }
41076 
41077 #define _cimg_blur_patch3d(N) { \
41078       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
41079                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
41080                          firstprivate(P,Q)) \
41081       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
41082         cimg_abort_test2; \
41083         cimg_def##N##x##N##x##N(res,x,y,z); \
41084         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
41085         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
41086                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
41087         tfloat sum_weights = 0, weight_max = 0; \
41088         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
41089           tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
41090           tfloat distance2 = 0; \
41091           pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
41092           distance2/=Pnorm; \
41093           const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
41094             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \
41095           if (weight>weight_max) weight_max = weight; \
41096           sum_weights+=weight; \
41097           cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
41098         } \
41099         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \
41100         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
41101         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
41102       } _cimg_abort_catch_openmp2 }
41103 
41104 #define _cimg_blur_patch2d_fast(N) { \
41105         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
41106                            firstprivate(P,Q)) \
41107         cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
41108           cimg_abort_test2; \
41109           cimg_def##N##x##N(res,x,y); \
41110           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
41111           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
41112           tfloat sum_weights = 0; \
41113           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
41114             if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))<sigma_r3) { \
41115               tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
41116               tfloat distance2 = 0; \
41117               pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
41118               distance2/=Pnorm; \
41119               const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
41120                 alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0:1; \
41121               sum_weights+=weight; \
41122               cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
41123             } \
41124           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
41125           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
41126         } _cimg_abort_catch_openmp2 }
41127 
41128 #define _cimg_blur_patch2d(N) { \
41129         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
41130                            firstprivate(P,Q)) \
41131         cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
41132           cimg_abort_test2; \
41133           cimg_def##N##x##N(res,x,y); \
41134           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
41135           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
41136           tfloat sum_weights = 0, weight_max = 0; \
41137           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
41138             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
41139             tfloat distance2 = 0; \
41140             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
41141             distance2/=Pnorm; \
41142             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
41143               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \
41144             if (weight>weight_max) weight_max = weight; \
41145             sum_weights+=weight; \
41146             cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
41147           } \
41148           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \
41149           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
41150           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
41151     } _cimg_abort_catch_openmp2 }
41152 
41153       typedef _cimg_tfloat tfloat;
41154       if (!is_sameXYZ(guide))
41155         throw CImgArgumentException(_cimg_instance
41156                                     "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
41157                                     cimg_instance,
41158                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
41159       if (is_empty() || !patch_size || !lookup_size) return +*this;
41160       Tfloat val_min, val_max = (Tfloat)max_min(val_min);
41161       _cimg_abort_init_openmp;
41162       cimg_abort_init;
41163 
41164       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
41165       const CImg<tfloat>
41166         __guide = guide?CImg<tfloat>(guide,guide.pixel_type()==cimg::type<tfloat>::string()):
41167                         CImg<tfloat>(*this,pixel_type()==cimg::type<tfloat>::string()),
41168         _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared();
41169       CImg<tfloat> P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P);
41170 
41171       t guide_min = (t)0, guide_max = (t)0;
41172       if (sigma_r<0) guide_max = guide.max_min(guide_min);
41173       const float
41174         guide_delta = (float)(guide_max - guide_min),
41175         _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
41176         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100,
41177         sigma_s2 = _sigma_s*_sigma_s,
41178         sigma_r2 = _sigma_r*_sigma_r,
41179         sigma_r3 = 3*_sigma_r,
41180         Pnorm = P.size()*sigma_r2;
41181       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
41182       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
41183       cimg::unused(N2,N3);
41184       if (_depth>1) switch (patch_size) { // 3D
41185         case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
41186         case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
41187         default : {
41188           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
41189           if (is_fast_approx) {
41190             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41191                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
41192                                firstprivate(P,Q))
41193             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Fast
41194               cimg_abort_test2;
41195               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
41196               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
41197                 x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
41198               tfloat sum_weights = 0;
41199               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
41200                 if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) {
41201                   (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
41202                   const tfloat
41203                     dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
41204                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
41205                     weight = distance2>3?0:1;
41206                   sum_weights+=weight;
41207                   cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
41208                 }
41209               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
41210               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
41211             } _cimg_abort_catch_openmp2
41212           } else {
41213             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41214                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
41215                                firstprivate(P,Q))
41216             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Exact
41217               cimg_abort_test2;
41218               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
41219               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
41220                         x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
41221               tfloat sum_weights = 0, weight_max = 0;
41222               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
41223                 (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
41224                 const tfloat
41225                   dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
41226                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
41227                   weight = std::exp(-distance2);
41228                 if (weight>weight_max) weight_max = weight;
41229                 sum_weights+=weight;
41230                 cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
41231               }
41232               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c);
41233               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
41234               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
41235             } _cimg_abort_catch_openmp2
41236           }
41237         }
41238         } else switch (patch_size) { // 2D
41239         case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
41240         case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
41241         case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
41242         case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
41243         case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
41244         case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
41245         case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
41246         case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
41247         default : { // Fast
41248           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
41249           if (is_fast_approx) {
41250             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
41251                                firstprivate(P,Q))
41252             cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Fast
41253               cimg_abort_test2;
41254               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
41255               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
41256               tfloat sum_weights = 0;
41257               cimg_for_inXY(res,x0,y0,x1,y1,p,q)
41258                 if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))<sigma_r3) {
41259                   (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
41260                   const tfloat
41261                     dx = (tfloat)x - p, dy = (tfloat)y - q,
41262                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
41263                     weight = distance2>3?0:1;
41264                   sum_weights+=weight;
41265                   cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
41266                 }
41267               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
41268               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
41269             } _cimg_abort_catch_openmp2
41270           } else {
41271             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
41272                                firstprivate(P,Q))
41273             cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Exact
41274               cimg_abort_test2;
41275               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
41276               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
41277               tfloat sum_weights = 0, weight_max = 0;
41278               cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
41279                 (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
41280                 const tfloat
41281                   dx = (tfloat)x - p, dy = (tfloat)y - q,
41282                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
41283                   weight = std::exp(-distance2);
41284                 if (weight>weight_max) weight_max = weight;
41285                 sum_weights+=weight;
41286                 cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
41287               }
41288               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c);
41289               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
41290               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
41291             } _cimg_abort_catch_openmp2
41292           }
41293         }
41294         }
41295       cimg_abort_test;
41296       return res.cut(val_min,val_max);
41297     }
41298 
41299     //! Blur image using patch-based space \simplification.
41300     CImg<T>& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
41301                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
41302       return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
41303     }
41304 
41305     //! Blur image using patch-based space \simplification \newinstance.
41306     CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
41307                                 const unsigned int lookup_size=4, const float smoothness=0,
41308                                 const bool is_fast_approx=true) const {
41309       return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
41310     }
41311 
41312     //! Blur image with the median filter.
41313     /**
41314        \param n Size of the median filter.
41315        \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
41316     **/
41317     CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
41318       if (!n) return *this;
41319       return get_blur_median(n,threshold).move_to(*this);
41320     }
41321 
41322     //! Blur image with the median filter \newinstance.
41323     CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
41324       if (is_empty() || n<=1) return +*this;
41325       CImg<T> res(_width,_height,_depth,_spectrum);
41326       T *ptrd = res._data;
41327       cimg::unused(ptrd);
41328       const int hr = (int)n/2, hl = n - hr - 1;
41329       if (res._depth!=1) { // 3D
41330         if (threshold>0)
41331           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
41332                                                                      _height*_depth*_spectrum>=4))
41333           cimg_forXYZC(*this,x,y,z,c) { // With threshold
41334             const int
41335               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
41336               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
41337               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
41338             const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
41339             CImg<T> values(n*n*n);
41340             unsigned int nb_values = 0;
41341             T *_ptrd = values.data();
41342             cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
41343               if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; }
41344             res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
41345           }
41346         else
41347           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
41348                                                                      _height*_depth*_spectrum>=4))
41349           cimg_forXYZC(*this,x,y,z,c) { // Without threshold
41350             const int
41351               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
41352               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
41353               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
41354             res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
41355           }
41356       } else {
41357         if (threshold>0)
41358           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
41359                                                                      _height*_spectrum>=4))
41360           cimg_forXYC(*this,x,y,c) { // With threshold
41361             const int
41362               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
41363               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
41364                                         nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
41365             const Tfloat val0 = (Tfloat)(*this)(x,y,c);
41366             CImg<T> values(n*n);
41367             unsigned int nb_values = 0;
41368             T *_ptrd = values.data();
41369             cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
41370               if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; }
41371             res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
41372           }
41373         else {
41374           const int
41375             w1 = width() - 1, h1 = height() - 1,
41376             w2 = width() - 2, h2 = height() - 2,
41377             w3 = width() - 3, h3 = height() - 3,
41378             w4 = width() - 4, h4 = height() - 4;
41379           switch (n) { // Without threshold
41380           case 3 : {
41381             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
41382             cimg_forC(*this,c) {
41383               CImg<T> I(9);
41384               cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
41385                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
41386               cimg_for_borderXY(*this,x,y,1)
41387                 res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
41388                                       std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
41389             }
41390           } break;
41391           case 5 : {
41392             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
41393             cimg_forC(*this,c) {
41394               CImg<T> I(25);
41395               cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
41396                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
41397                                           I[5],I[6],I[7],I[8],I[9],
41398                                           I[10],I[11],I[12],I[13],I[14],
41399                                           I[15],I[16],I[17],I[18],I[19],
41400                                           I[20],I[21],I[22],I[23],I[24]);
41401               cimg_for_borderXY(*this,x,y,2)
41402                 res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
41403                                       std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
41404             }
41405           } break;
41406           case 7 : {
41407             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
41408             cimg_forC(*this,c) {
41409               CImg<T> I(49);
41410               cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
41411                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
41412                                           I[7],I[8],I[9],I[10],I[11],I[12],I[13],
41413                                           I[14],I[15],I[16],I[17],I[18],I[19],I[20],
41414                                           I[21],I[22],I[23],I[24],I[25],I[26],I[27],
41415                                           I[28],I[29],I[30],I[31],I[32],I[33],I[34],
41416                                           I[35],I[36],I[37],I[38],I[39],I[40],I[41],
41417                                           I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
41418               cimg_for_borderXY(*this,x,y,3)
41419                 res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
41420                                       std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
41421             }
41422           } break;
41423           default : {
41424             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41425                                cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4))
41426             cimg_forXYC(*this,x,y,c) {
41427               const int
41428                 x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
41429                 nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
41430                                           nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
41431               res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
41432             }
41433           }
41434           }
41435         }
41436       }
41437       return res;
41438     }
41439 
41440     //! Sharpen image.
41441     /**
41442        \param amplitude Sharpening amplitude
41443        \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
41444        \param edge Edge threshold (shock filters only).
41445        \param alpha Gradient smoothness (shock filters only).
41446        \param sigma Tensor smoothness (shock filters only).
41447     **/
41448     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
41449                      const float alpha=0, const float sigma=0) {
41450       if (is_empty()) return *this;
41451       T val_min, val_max = max_min(val_min);
41452       const float nedge = edge/2;
41453       CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
41454 
41455       if (_depth>1) { // 3D
41456         if (sharpen_type) { // Shock filters
41457           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
41458           if (sigma>0) G.blur(sigma);
41459           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
41460                                                                      _height*_depth>=16))
41461           cimg_forYZ(G,y,z) {
41462             Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
41463               *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
41464             CImg<Tfloat> val, vec;
41465             cimg_forX(G,x) {
41466               G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
41467               if (val[0]<0) val[0] = 0;
41468               if (val[1]<0) val[1] = 0;
41469               if (val[2]<0) val[2] = 0;
41470               *(ptrG0++) = vec(0,0);
41471               *(ptrG1++) = vec(0,1);
41472               *(ptrG2++) = vec(0,2);
41473               *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
41474             }
41475           }
41476           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
41477                                                          _spectrum>=2))
41478           cimg_forC(*this,c) {
41479             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41480             CImg_3x3x3(I,Tfloat);
41481             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41482               const Tfloat
41483                 u = G(x,y,z,0),
41484                 v = G(x,y,z,1),
41485                 w = G(x,y,z,2),
41486                 amp = G(x,y,z,3),
41487                 ixx = Incc + Ipcc - 2*Iccc,
41488                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
41489                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
41490                 iyy = Icnc + Icpc - 2*Iccc,
41491                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
41492                 izz = Iccn + Iccp - 2*Iccc,
41493                 ixf = Incc - Iccc,
41494                 ixb = Iccc - Ipcc,
41495                 iyf = Icnc - Iccc,
41496                 iyb = Iccc - Icpc,
41497                 izf = Iccn - Iccc,
41498                 izb = Iccc - Iccp,
41499                 itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
41500                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
41501                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
41502               *(ptrd++) = veloc;
41503               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41504             }
41505             _veloc_max[c] = veloc_max;
41506           }
41507         } else  // Inverse diffusion
41508           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
41509                                                          _spectrum>=2))
41510           cimg_forC(*this,c) {
41511             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41512             CImg_3x3x3(I,Tfloat);
41513             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41514               const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
41515               *(ptrd++) = veloc;
41516               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41517             }
41518             _veloc_max[c] = veloc_max;
41519           }
41520       } else { // 2D
41521         if (sharpen_type) { // Shock filters
41522           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
41523           if (sigma>0) G.blur(sigma);
41524           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
41525                                                          _height>=(cimg_openmp_sizefactor)*16))
41526           cimg_forY(G,y) {
41527             CImg<Tfloat> val, vec;
41528             Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
41529             cimg_forX(G,x) {
41530               G.get_tensor_at(x,y).symmetric_eigen(val,vec);
41531               if (val[0]<0) val[0] = 0;
41532               if (val[1]<0) val[1] = 0;
41533               *(ptrG0++) = vec(0,0);
41534               *(ptrG1++) = vec(0,1);
41535               *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
41536             }
41537           }
41538           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
41539                                                          _spectrum>=2))
41540           cimg_forC(*this,c) {
41541             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41542             CImg_3x3(I,Tfloat);
41543             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41544               const Tfloat
41545                 u = G(x,y,0),
41546                 v = G(x,y,1),
41547                 amp = G(x,y,2),
41548                 ixx = Inc + Ipc - 2*Icc,
41549                 ixy = (Inn + Ipp - Inp - Ipn)/4,
41550                 iyy = Icn + Icp - 2*Icc,
41551                 ixf = Inc - Icc,
41552                 ixb = Icc - Ipc,
41553                 iyf = Icn - Icc,
41554                 iyb = Icc - Icp,
41555                 itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
41556                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
41557                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
41558               *(ptrd++) = veloc;
41559               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41560             }
41561             _veloc_max[c] = veloc_max;
41562           }
41563         } else // Inverse diffusion
41564           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
41565                                                          _spectrum>=2))
41566           cimg_forC(*this,c) {
41567             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
41568             CImg_3x3(I,Tfloat);
41569             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41570               const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
41571               *(ptrd++) = veloc;
41572               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
41573             }
41574             _veloc_max[c] = veloc_max;
41575           }
41576       }
41577       const Tfloat veloc_max = _veloc_max.max();
41578       if (veloc_max<=0) return *this;
41579       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
41580     }
41581 
41582     //! Sharpen image \newinstance.
41583     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
41584                         const float alpha=0, const float sigma=0) const {
41585       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
41586     }
41587 
41588     //! Return image gradient.
41589     /**
41590        \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
41591        \param scheme = Numerical scheme used for the gradient computation:
41592        - -1 = Backward finite differences
41593        - 0 = Centered finite differences (default)
41594        - 1 = Forward finite differences
41595        - 2 = Using Sobel kernels
41596        - 3 = Using rotation invariant kernels
41597        - 4 = Using Deriche recursive filter.
41598        - 5 = Using Van Vliet recursive filter.
41599     **/
41600     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=0) const {
41601       CImgList<Tfloat> res;
41602       char __axes[4] = { 0 };
41603       const char *_axes = axes?axes:__axes;
41604       if (!axes) {
41605         unsigned int k = 0;
41606         if (_width>1) __axes[k++] = 'x';
41607         if (_height>1) __axes[k++] = 'y';
41608         if (_depth>1) __axes[k++] = 'z';
41609       }
41610 
41611       CImg<Tfloat> grad;
41612       while (*_axes) {
41613         const char axis = cimg::lowercase(*(_axes++));
41614         if (axis!='x' && axis!='y' && axis!='z')
41615           throw CImgArgumentException(_cimg_instance
41616                                       "get_gradient(): Invalid specified axes '%s'.",
41617                                       cimg_instance,
41618                                       axes);
41619         const longT off = axis=='x'?1:axis=='y'?_width:_width*_height;
41620         if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) {
41621           grad.assign(_width,_height,_depth,_spectrum,0).move_to(res);
41622           continue;
41623         }
41624 
41625         const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme;
41626         switch (_scheme) {
41627         case -1 : { // Backward finite differences
41628           grad.assign(_width,_height,_depth,_spectrum);
41629           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41630           cimg_forXYZC(*this,x,y,z,c) {
41631             const ulongT pos = offset(x,y,z,c);
41632             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
41633               grad[pos] = 0;
41634             else
41635               grad[pos] = (Tfloat)_data[pos] - _data[pos - off];
41636           }
41637           grad.move_to(res);
41638         } break;
41639         case 1 : { // Forward finite differences
41640           grad.assign(_width,_height,_depth,_spectrum);
41641           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41642           cimg_forXYZC(*this,x,y,z,c) {
41643             const ulongT pos = offset(x,y,z,c);
41644             if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
41645               grad[pos] = 0;
41646             else
41647               grad[pos] = (Tfloat)_data[pos + off] - _data[pos];
41648           }
41649           grad.move_to(res);
41650         } break;
41651         case 2 : { // Sobel scheme
41652           grad.assign(_width,_height,_depth,_spectrum);
41653           if (axis=='x') // X-axis
41654             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41655                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41656                                               _depth*_spectrum>=2))
41657             cimg_forZC(*this,z,c) {
41658               CImg_3x3(I,Tfloat);
41659               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn;
41660             }
41661           else // Y-axis
41662             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41663                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41664                                               _depth*_spectrum>=2))
41665             cimg_forZC(*this,z,c) {
41666               CImg_3x3(I,Tfloat);
41667               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
41668             }
41669           grad.move_to(res);
41670         } break;
41671         case 3 : { // Rotation invariant scheme
41672           const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1));
41673           grad.assign(_width,_height,_depth,_spectrum);
41674           if (axis=='x') // X-axis
41675             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41676                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41677                                               _depth*_spectrum>=2))
41678             cimg_forZC(*this,z,c) {
41679               CImg_3x3(I,Tfloat);
41680               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;
41681             }
41682           else // Y-axis
41683             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41684                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41685                                               _depth*_spectrum>=2))
41686             cimg_forZC(*this,z,c) {
41687               CImg_3x3(I,Tfloat);
41688               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;
41689             }
41690           grad.move_to(res);
41691         } break;
41692         case 4 : // Deriche filter
41693           get_deriche(0,1,axis).move_to(res);
41694           break;
41695         case 5 : // Van Vliet filter
41696           get_vanvliet(0,1,axis).move_to(res);
41697           break;
41698         default : { // Central finite differences
41699           grad.assign(_width,_height,_depth,_spectrum);
41700           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41701           cimg_forXYZC(*this,x,y,z,c) {
41702             const ulongT pos = offset(x,y,z,c);
41703             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
41704               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2;
41705             else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
41706               grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2;
41707             else
41708               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2;
41709           }
41710           grad.move_to(res);
41711         } break;
41712         }
41713       }
41714       return res;
41715     }
41716 
41717     //! Return image hessian.
41718     /**
41719        \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
41720     **/
41721     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
41722       CImgList<Tfloat> res;
41723       char __axes[12] = { 0 };
41724       const char *_axes = axes?axes:__axes;
41725       if (!axes) {
41726         unsigned int k = 0;
41727         if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; }
41728         if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; }
41729         if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; }
41730         if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; }
41731         if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; }
41732         if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; }
41733       }
41734       const unsigned int len = (unsigned int)std::strlen(_axes);
41735       if (len%2)
41736         throw CImgArgumentException(_cimg_instance
41737                                     "get_hessian(): Invalid specified axes '%s'.",
41738                                     cimg_instance,
41739                                     axes);
41740       CImg<Tfloat> hess;
41741       for (unsigned int k = 0; k<len; k+=2) {
41742         const char
41743           _axis1 = cimg::lowercase(_axes[k]),
41744           _axis2 = cimg::lowercase(_axes[k + 1]),
41745           axis1 = std::min(_axis1,_axis2),
41746           axis2 = std::max(_axis2,_axis2);
41747         if (axis1!='x' && axis1!='y' && axis1!='z' &&
41748             axis2!='x' && axis2!='y' && axis2!='z')
41749           throw CImgArgumentException(_cimg_instance
41750                                       "get_hessian(): Invalid specified axes '%s'.",
41751                                       cimg_instance,
41752                                       axes);
41753         const longT off = axis1=='x'?1:axis1=='y'?_width:_width*_height;
41754 
41755         hess.assign(_width,_height,_depth,_spectrum);
41756 
41757         if (((axis1=='x' || axis2=='x') && _width==1) ||
41758             ((axis1=='y' || axis2=='y') && _height==1) ||
41759             ((axis1=='z' || axis2=='z') && _depth==1)) {
41760           hess.fill(0).move_to(res);
41761           continue;
41762         }
41763 
41764         if (axis1==axis2) // Ixx, Iyy, Izz
41765           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
41766           cimg_forXYZC(*this,x,y,z,c) {
41767             const ulongT pos = offset(x,y,z,c);
41768             if ((axis1=='x' && !x) || (axis1=='y' && !y) || (axis1=='z' && !z))
41769               hess[pos] = (Tfloat)_data[pos + off] - _data[pos];
41770             else if ((axis1=='x' && x==width() - 1) ||
41771                      (axis1=='y' && y==height() - 1) ||
41772                      (axis1=='z' && z==depth() - 1))
41773               hess[pos] = (Tfloat)_data[pos - off] - _data[pos];
41774             else
41775               hess[pos] = (Tfloat)_data[pos + off] + _data[pos - off] - 2*_data[pos];
41776           }
41777         else if (axis1=='x' && axis2=='y') // Ixy
41778           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41779                              cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
41780                                             _depth*_spectrum>=2))
41781           cimg_forZC(*this,z,c) {
41782             CImg_3x3(I,Tfloat);
41783             cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4;
41784           }
41785         else if (axis1=='x' && axis2=='z') // Ixz
41786           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
41787                                                          _spectrum>=2))
41788           cimg_forC(*this,c) {
41789             CImg_3x3x3(I,Tfloat);
41790             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4;
41791           }
41792         else // Iyz
41793           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
41794                                                          _spectrum>=2))
41795           cimg_forC(*this,c) {
41796             CImg_3x3x3(I,Tfloat);
41797             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4;
41798           }
41799         hess.move_to(res);
41800       }
41801       return res;
41802     }
41803 
41804     //! Compute image Laplacian.
41805     CImg<T>& laplacian() {
41806       return get_laplacian().move_to(*this);
41807     }
41808 
41809     //! Compute image Laplacian \newinstance.
41810     CImg<Tfloat> get_laplacian() const {
41811       if (is_empty()) return CImg<Tfloat>();
41812       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
41813       if (_depth>1) { // 3D
41814         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41815                                                        _spectrum>=2))
41816         cimg_forC(*this,c) {
41817           Tfloat *ptrd = res.data(0,0,0,c);
41818           CImg_3x3x3(I,Tfloat);
41819           cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
41820         }
41821       } else if (_height>1) { // 2D
41822         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41823                                                        _depth*_spectrum>=2))
41824         cimg_forC(*this,c) {
41825           Tfloat *ptrd = res.data(0,0,0,c);
41826           CImg_3x3(I,Tfloat);
41827           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
41828         }
41829       } else { // 1D
41830         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 &&
41831                                                        _height*_depth*_spectrum>=2))
41832         cimg_forC(*this,c) {
41833           Tfloat *ptrd = res.data(0,0,0,c);
41834           CImg_3x3(I,Tfloat);
41835           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
41836         }
41837       }
41838       return res;
41839     }
41840 
41841     //! Compute the structure tensor field of an image.
41842     /**
41843        \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
41844     **/
41845     CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
41846       return get_structure_tensors(is_fwbw_scheme).move_to(*this);
41847     }
41848 
41849     //! Compute the structure tensor field of an image \newinstance.
41850     CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
41851       if (is_empty()) return *this;
41852       CImg<Tfloat> res;
41853       if (_depth>1) { // 3D
41854         res.assign(_width,_height,_depth,6,0);
41855         if (!is_fwbw_scheme) { // Classical central finite differences
41856           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41857                                                          _spectrum>=2))
41858           cimg_forC(*this,c) {
41859             Tfloat
41860               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
41861               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
41862             CImg_3x3x3(I,Tfloat);
41863             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41864               const Tfloat
41865                 ix = (Incc - Ipcc)/2,
41866                 iy = (Icnc - Icpc)/2,
41867                 iz = (Iccn - Iccp)/2;
41868               *(ptrd0++)+=ix*ix;
41869               *(ptrd1++)+=ix*iy;
41870               *(ptrd2++)+=ix*iz;
41871               *(ptrd3++)+=iy*iy;
41872               *(ptrd4++)+=iy*iz;
41873               *(ptrd5++)+=iz*iz;
41874             }
41875           }
41876         } else { // Forward/backward finite differences
41877           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
41878                                                          _spectrum>=2))
41879           cimg_forC(*this,c) {
41880             Tfloat
41881               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
41882               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
41883             CImg_3x3x3(I,Tfloat);
41884             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
41885               const Tfloat
41886                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
41887                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
41888                 izf = Iccn - Iccc, izb = Iccc - Iccp;
41889               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
41890               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
41891               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
41892               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
41893               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
41894               *(ptrd5++)+=(izf*izf + izb*izb)/2;
41895             }
41896           }
41897         }
41898       } else { // 2D
41899         res.assign(_width,_height,_depth,3,0);
41900         if (!is_fwbw_scheme) { // Classical central finite differences
41901           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41902                                                          _depth*_spectrum>=2))
41903           cimg_forC(*this,c) {
41904             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
41905             CImg_3x3(I,Tfloat);
41906             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41907               const Tfloat
41908                 ix = (Inc - Ipc)/2,
41909                 iy = (Icn - Icp)/2;
41910               *(ptrd0++)+=ix*ix;
41911               *(ptrd1++)+=ix*iy;
41912               *(ptrd2++)+=iy*iy;
41913             }
41914           }
41915         } else { // Forward/backward finite differences (version 2)
41916           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
41917                                                          _depth*_spectrum>=2))
41918           cimg_forC(*this,c) {
41919             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
41920             CImg_3x3(I,Tfloat);
41921             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
41922               const Tfloat
41923                 ixf = Inc - Icc, ixb = Icc - Ipc,
41924                 iyf = Icn - Icc, iyb = Icc - Icp;
41925               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
41926               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
41927               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
41928             }
41929           }
41930         }
41931       }
41932       return res;
41933     }
41934 
41935     //! Compute field of diffusion tensors for edge-preserving smoothing.
41936     /**
41937        \param sharpness Sharpness
41938        \param anisotropy Anisotropy
41939        \param alpha Standard deviation of the gradient blur.
41940        \param sigma Standard deviation of the structure tensor blur.
41941        \param is_sqrt Tells if the square root of the tensor field is computed instead.
41942     **/
41943     CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
41944                                const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
41945       CImg<Tfloat> res;
41946       const float
41947         nsharpness = std::max(sharpness,1e-5f),
41948         power1 = (is_sqrt?0.5f:1)*nsharpness,
41949         power2 = power1/(1e-7f + 1 - anisotropy);
41950       blur(alpha).normalize(0,(T)255);
41951 
41952       if (_depth>1) { // 3D
41953         get_structure_tensors().move_to(res).blur(sigma);
41954         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
41955                                                                    _height*_depth>=(cimg_openmp_sizefactor)*256))
41956         cimg_forYZ(*this,y,z) {
41957           Tfloat
41958             *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
41959             *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
41960           CImg<floatT> val(3), vec(3,3);
41961           cimg_forX(*this,x) {
41962             res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
41963             const float
41964               _l1 = val[2], _l2 = val[1], _l3 = val[0],
41965               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
41966               ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
41967               vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
41968               wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
41969               n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
41970               n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
41971             *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
41972             *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
41973             *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
41974             *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
41975             *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
41976             *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
41977           }
41978         }
41979       } else { // for 2D images
41980         get_structure_tensors().move_to(res).blur(sigma);
41981         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
41982                                                        _height>=(cimg_openmp_sizefactor)*256))
41983         cimg_forY(*this,y) {
41984           Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
41985           CImg<floatT> val(2), vec(2,2);
41986           cimg_forX(*this,x) {
41987             res.get_tensor_at(x,y).symmetric_eigen(val,vec);
41988             const float
41989               _l1 = val[1], _l2 = val[0],
41990               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
41991               ux = vec(1,0), uy = vec(1,1),
41992               vx = vec(0,0), vy = vec(0,1),
41993               n1 = (float)std::pow(1 + l1 + l2,-power1),
41994               n2 = (float)std::pow(1 + l1 + l2,-power2);
41995             *(ptrd0++) = n1*ux*ux + n2*vx*vx;
41996             *(ptrd1++) = n1*ux*uy + n2*vx*vy;
41997             *(ptrd2++) = n1*uy*uy + n2*vy*vy;
41998           }
41999         }
42000       }
42001       return res.move_to(*this);
42002     }
42003 
42004     //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
42005     CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
42006                                        const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
42007       return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
42008     }
42009 
42010     //! Estimate displacement field between two images.
42011     /**
42012        \param source Reference image.
42013        \param smoothness Smoothness of estimated displacement field.
42014        \param precision Precision required for algorithm convergence.
42015        \param nb_scales Number of scales used to estimate the displacement field.
42016        \param iteration_max Maximum number of iterations allowed for one scale.
42017        \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
42018        \param guide Image used as the initial correspondence estimate for the algorithm.
42019        'guide' may have a last channel with boolean values (0=false | other=true) that
42020        tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
42021     **/
42022     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f,
42023                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
42024                           const bool is_backward=false,
42025                           const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
42026       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
42027         move_to(*this);
42028     }
42029 
42030     //! Estimate displacement field between two images \newinstance.
42031     CImg<floatT> get_displacement(const CImg<T>& source,
42032                                   const float smoothness=0.1f, const float precision=5.f,
42033                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
42034                                   const bool is_backward=false,
42035                                   const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
42036       if (is_empty() || !source) return +*this;
42037       if (!is_sameXYZC(source))
42038         throw CImgArgumentException(_cimg_instance
42039                                     "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
42040                                     "different dimensions.",
42041                                     cimg_instance,
42042                                     source._width,source._height,source._depth,source._spectrum,source._data);
42043       if (precision<0)
42044         throw CImgArgumentException(_cimg_instance
42045                                     "displacement(): Invalid specified precision %g "
42046                                     "(should be >=0)",
42047                                     cimg_instance,
42048                                     precision);
42049 
42050       const bool is_3d = source._depth>1;
42051       const unsigned int constraint = is_3d?3:2;
42052 
42053       if (guide &&
42054           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
42055         throw CImgArgumentException(_cimg_instance
42056                                     "displacement(): Specified guide (%u,%u,%u,%u,%p) "
42057                                     "has invalid dimensions.",
42058                                     cimg_instance,
42059                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
42060 
42061       const unsigned int
42062         mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
42063         _nb_scales = nb_scales>0?nb_scales:
42064         (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1);
42065 
42066       const float _precision = (float)std::pow(10.,-(double)precision);
42067       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
42068       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
42069 
42070       CImg<floatT> U, V;
42071       floatT bound = 0;
42072       for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
42073         const float factor = (float)std::pow(1.5,(double)scale);
42074         const unsigned int
42075           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
42076           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
42077           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
42078         if (sw<5 && sh<5 && (!is_3d || sd<5)) continue;  // Skip too small scales
42079         const CImg<Tfloat>
42080           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
42081           I2 = (get_resize(I1,2)-=tm)/=tdelta;
42082         if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
42083         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
42084         else {
42085           if (guide)
42086             guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
42087           else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
42088         }
42089 
42090         float dt = 2, energy = cimg::type<float>::max();
42091         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
42092         cimg_abort_init;
42093 
42094         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
42095           cimg_abort_test;
42096           float _energy = 0;
42097 
42098           if (is_3d) { // 3D version
42099             if (smoothness>=0) // Isotropic regularization
42100               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
42101                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
42102                                                 _width>=(cimg_openmp_sizefactor)*16)
42103                                  reduction(+:_energy))
42104               cimg_forYZ(U,y,z) {
42105                 const int
42106                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
42107                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
42108                 cimg_for3X(U,x) {
42109                   const float
42110                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
42111                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
42112                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
42113                   float delta_I = 0, _energy_regul = 0;
42114                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
42115                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
42116                   cimg_forC(U,c) {
42117                     const float
42118                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
42119                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
42120                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
42121                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
42122                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
42123                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
42124                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
42125                                                           smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
42126                     _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
42127                   }
42128                   if (is_backward) { // Constraint displacement vectors to stay in image
42129                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
42130                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
42131                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
42132                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
42133                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
42134                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
42135                   } else {
42136                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
42137                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
42138                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
42139                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
42140                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
42141                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
42142                   }
42143                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
42144                 }
42145                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
42146                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
42147                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
42148                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
42149                   }
42150               } else { // Anisotropic regularization
42151               const float nsmoothness = -smoothness;
42152               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
42153                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
42154                                                 _width>=(cimg_openmp_sizefactor)*16)
42155                                  reduction(+:_energy))
42156               cimg_forYZ(U,y,z) {
42157                 const int
42158                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
42159                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
42160                 cimg_for3X(U,x) {
42161                   const float
42162                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
42163                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
42164                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
42165                   float delta_I = 0, _energy_regul = 0;
42166                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
42167                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
42168                   cimg_forC(U,c) {
42169                     const float
42170                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
42171                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
42172                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
42173                       N2 = Ux*Ux + Uy*Uy + Uz*Uz,
42174                       N = std::sqrt(N2),
42175                       N3 = 1e-5f + N2*N,
42176                       coef_a = (1 - Ux*Ux/N2)/N,
42177                       coef_b = -2*Ux*Uy/N3,
42178                       coef_c = -2*Ux*Uz/N3,
42179                       coef_d = (1 - Uy*Uy/N2)/N,
42180                       coef_e = -2*Uy*Uz/N3,
42181                       coef_f = (1 - Uz*Uz/N2)/N,
42182                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
42183                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
42184                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
42185                       Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
42186                       Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
42187                       Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
42188                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
42189                                                           nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
42190                                                                          coef_c*Uxz + coef_d*Uyy +
42191                                                                          coef_e*Uyz + coef_f*Uzz ))
42192                                          )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
42193                     _energy_regul+=N;
42194                   }
42195                   if (is_backward) { // Constraint displacement vectors to stay in image
42196                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
42197                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
42198                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
42199                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
42200                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
42201                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
42202                   } else {
42203                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
42204                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
42205                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
42206                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
42207                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
42208                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
42209                   }
42210                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
42211                 }
42212                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
42213                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
42214                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
42215                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
42216                   }
42217               }
42218             }
42219           } else { // 2D version
42220             if (smoothness>=0) // Isotropic regularization
42221               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
42222                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
42223               cimg_forY(U,y) {
42224                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
42225                 cimg_for3X(U,x) {
42226                   const float
42227                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
42228                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
42229                   float delta_I = 0, _energy_regul = 0;
42230                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
42231                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
42232                   cimg_forC(U,c) {
42233                     const float
42234                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
42235                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
42236                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
42237                       Uyy = U(x,_n1y,c) + U(x,_p1y,c);
42238                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
42239                                                       smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
42240                     _energy_regul+=Ux*Ux + Uy*Uy;
42241                   }
42242                   if (is_backward) { // Constraint displacement vectors to stay in image
42243                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
42244                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
42245                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
42246                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
42247                   } else {
42248                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
42249                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
42250                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
42251                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
42252                   }
42253                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
42254                 }
42255                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
42256                     U(_x,_y,0) = V(_x,_y,0)/factor;
42257                     U(_x,_y,1) = V(_x,_y,1)/factor;
42258                   }
42259               } else { // Anisotropic regularization
42260               const float nsmoothness = -smoothness;
42261               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
42262                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
42263               cimg_forY(U,y) {
42264                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
42265                 cimg_for3X(U,x) {
42266                   const float
42267                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
42268                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
42269                   float delta_I = 0, _energy_regul = 0;
42270                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
42271                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
42272                   cimg_forC(U,c) {
42273                     const float
42274                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
42275                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
42276                       N2 = Ux*Ux + Uy*Uy,
42277                       N = std::sqrt(N2),
42278                       N3 = 1e-5f + N2*N,
42279                       coef_a = Uy*Uy/N3,
42280                       coef_b = -2*Ux*Uy/N3,
42281                       coef_c = Ux*Ux/N3,
42282                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
42283                       Uyy = U(x,_n1y,c) + U(x,_p1y,c),
42284                       Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
42285                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
42286                                                       nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
42287                       (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
42288                     _energy_regul+=N;
42289                   }
42290                   if (is_backward) { // Constraint displacement vectors to stay in image
42291                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
42292                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
42293                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
42294                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
42295                   } else {
42296                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
42297                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
42298                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
42299                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
42300                   }
42301                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
42302                 }
42303                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
42304                     U(_x,_y,0) = V(_x,_y,0)/factor;
42305                     U(_x,_y,1) = V(_x,_y,1)/factor;
42306                   }
42307               }
42308             }
42309           }
42310           const float d_energy = (_energy - energy)/(sw*sh*sd);
42311           if (d_energy<=0 && -d_energy<_precision) break;
42312           if (d_energy>0) dt*=0.5f;
42313           energy = _energy;
42314         }
42315       }
42316       return U;
42317     }
42318 
42319     //! Compute correspondence map between two images, using a patch-matching algorithm.
42320     /**
42321         \param patch_image The image containing the reference patches to match with the instance image.
42322         \param patch_width Width of the patch used for matching.
42323         \param patch_height Height of the patch used for matching.
42324         \param patch_depth Depth of the patch used for matching.
42325         \param nb_iterations Number of patch-match iterations.
42326         \param nb_randoms Number of randomization attempts (per pixel).
42327         \param patch_penalization Penalization factor in score related patch occurrences.
42328                if negative, also tells that identity result is not avoided.
42329         \param guide Image used as the initial correspondence estimate for the algorithm.
42330           'guide' may have a last channel with boolean values (0=false | other=true) that
42331           tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
42332         \param[out] matching_score Returned as the image of matching scores.
42333     **/
42334     template<typename t1, typename t2>
42335     CImg<T>& matchpatch(const CImg<T>& patch_image,
42336                         const unsigned int patch_width,
42337                         const unsigned int patch_height,
42338                         const unsigned int patch_depth,
42339                         const unsigned int nb_iterations,
42340                         const unsigned int nb_randoms,
42341                         const float patch_penalization,
42342                         const CImg<t1> &guide,
42343                         CImg<t2> &matching_score) {
42344       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
42345                             nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
42346     }
42347 
42348     //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
42349     template<typename t1, typename t2>
42350     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
42351                               const unsigned int patch_width,
42352                               const unsigned int patch_height,
42353                               const unsigned int patch_depth,
42354                               const unsigned int nb_iterations,
42355                               const unsigned int nb_randoms,
42356                               const float patch_penalization,
42357                               const CImg<t1> &guide,
42358                               CImg<t2> &matching_score) const {
42359       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
42360                          nb_iterations,nb_randoms,patch_penalization,
42361                          guide,true,matching_score);
42362     }
42363 
42364     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
42365     template<typename t>
42366     CImg<T>& matchpatch(const CImg<T>& patch_image,
42367                         const unsigned int patch_width,
42368                         const unsigned int patch_height,
42369                         const unsigned int patch_depth,
42370                         const unsigned int nb_iterations=5,
42371                         const unsigned int nb_randoms=5,
42372                         const float patch_penalization=0,
42373                         const CImg<t> &guide=CImg<t>::const_empty()) {
42374       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
42375                             nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this);
42376     }
42377 
42378     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
42379     template<typename t>
42380     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
42381                               const unsigned int patch_width,
42382                               const unsigned int patch_height,
42383                               const unsigned int patch_depth,
42384                               const unsigned int nb_iterations=5,
42385                               const unsigned int nb_randoms=5,
42386                               const float patch_penalization=0,
42387                               const CImg<t> &guide=CImg<t>::const_empty()) const {
42388       CImg<T> matching_score;
42389       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
42390                          nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score);
42391     }
42392 
42393     template<typename t1, typename t2>
42394     CImg<intT> _matchpatch(const CImg<T>& patch_image,
42395                            const unsigned int patch_width,
42396                            const unsigned int patch_height,
42397                            const unsigned int patch_depth,
42398                            const unsigned int nb_iterations,
42399                            const unsigned int nb_randoms,
42400                            const float patch_penalization,
42401                            const CImg<t1> &guide,
42402                            const bool is_matching_score,
42403                            CImg<t2> &matching_score) const {
42404       if (is_empty()) return CImg<intT>::const_empty();
42405       if (patch_image._spectrum!=_spectrum)
42406         throw CImgArgumentException(_cimg_instance
42407                                     "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
42408                                     "have different spectrums.",
42409                                     cimg_instance,
42410                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
42411                                     patch_image._data);
42412       if (patch_width>_width || patch_height>_height || patch_depth>_depth)
42413         throw CImgArgumentException(_cimg_instance
42414                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
42415                                     "of the instance image.",
42416                                     cimg_instance,patch_width,patch_height,patch_depth);
42417       if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
42418         throw CImgArgumentException(_cimg_instance
42419                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
42420                                     "of the patch image image (%u,%u,%u,%u,%p).",
42421                                     cimg_instance,patch_width,patch_height,patch_depth,
42422                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
42423                                     patch_image._data);
42424       const unsigned int
42425         _constraint = patch_image._depth>1?3:2,
42426         constraint = guide._spectrum>_constraint?_constraint:0;
42427 
42428       if (guide &&
42429           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
42430         throw CImgArgumentException(_cimg_instance
42431                                     "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
42432                                     "considering instance and patch image (%u,%u,%u,%u,%p).",
42433                                     cimg_instance,
42434                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
42435                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
42436                                     patch_image._data);
42437 
42438       CImg<intT> a_map(_width,_height,_depth,patch_image._depth>1?3:2);
42439       CImg<ucharT> is_updated(_width,_height,_depth,1,3);
42440       CImg<floatT> score(_width,_height,_depth), penalty;
42441       const float _patch_penalization = cimg::abs(patch_penalization);
42442       const bool allow_identity = patch_penalization>=0;
42443       if (_patch_penalization!=0)
42444         penalty.assign(patch_image._width,patch_image._height,patch_image._depth,1,0);
42445 
42446       const int
42447         psizew = (int)patch_width,  psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
42448         psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
42449         psized = (int)patch_depth,  psized1 = psized/2, psized2 = psized - psized1 - 1;
42450 
42451       // Interleave image buffers to speed up patch comparison (more cache-friendly).
42452       CImg<T> in_this = get_permute_axes("cxyz");
42453       in_this._width = _width*_spectrum;
42454       in_this._height = _height;
42455       in_this._depth = _depth;
42456       in_this._spectrum = 1;
42457       CImg<T> in_patch = patch_image.get_permute_axes("cxyz");
42458       in_patch._width = patch_image._width*patch_image._spectrum;
42459       in_patch._height = patch_image._height;
42460       in_patch._depth = patch_image._depth;
42461       in_patch._spectrum = 1;
42462 
42463       if (_depth>1 || patch_image._depth>1) { // 3D version
42464 
42465         // Initialize correspondence map.
42466         if (guide)
42467           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64))
42468             cimg_forXYZ(*this,x,y,z) { // User-defined initialization
42469             const int
42470               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42471               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42472               cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
42473               u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2),
42474               v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2),
42475               w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2);
42476             a_map(x,y,z,0) = u;
42477             a_map(x,y,z,1) = v;
42478             a_map(x,y,z,2) = w;
42479             score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42480                                        x - cx1,y - cy1,z - cz1,
42481                                        u - cx1,v - cy1,w - cz1,
42482                                        u,v,w,0,allow_identity,cimg::type<float>::inf());
42483           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
42484             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42485 #if cimg_use_openmp!=0
42486             rng+=omp_get_thread_num();
42487 #endif
42488             cimg_pragma_openmp(for cimg_openmp_collapse(2))
42489               cimg_forXYZ(*this,x,y,z) { // Random initialization
42490               const int
42491                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42492                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42493                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
42494                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
42495                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)),
42496                 w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng));
42497               a_map(x,y,z,0) = u;
42498               a_map(x,y,z,1) = v;
42499               a_map(x,y,z,2) = w;
42500               score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42501                                          x - cx1,y - cy1,z - cz1,
42502                                          u - cx1,v - cy1,w - cz1,
42503                                          u,v,w,0,allow_identity,cimg::type<float>::inf());
42504             }
42505             cimg::srand(rng);
42506           }
42507 
42508         // Start iteration loop.
42509         cimg_abort_init;
42510         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
42511           cimg_abort_test;
42512           const bool is_backward = iter&1, is_forward = !is_backward;
42513           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
42514 
42515           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
42516             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42517 
42518 #if cimg_use_openmp!=0
42519             rng+=omp_get_thread_num();
42520 #endif
42521             cimg_pragma_openmp(for cimg_openmp_collapse(2))
42522               cimg_forXYZ(*this,X,Y,Z) {
42523               const int
42524                 x = is_backward?width() - 1 - X:X,
42525                 y = is_backward?height() - 1 - Y:Y,
42526                 z = is_backward?depth() - 1 - Z:Z;
42527               if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
42528               const int
42529                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42530                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42531                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
42532                 xp = x - cx1,
42533                 yp = y - cy1,
42534                 zp = z - cz1;
42535 
42536               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;
42537               const float best_score0 = score(x,y,z);
42538               float best_score = best_score0, s;
42539 
42540               if (is_forward && x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor
42541                 u = a_map(x - 1,y,z,0);
42542                 v = a_map(x - 1,y,z,1);
42543                 w = a_map(x - 1,y,z,2);
42544                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
42545                     v>=cy1 && v<patch_image.height() - cy2 &&
42546                     w>=cz1 && w<patch_image.depth() - cz2) {
42547                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42548                                   xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,
42549                                   u,v,w,_patch_penalization,allow_identity,best_score);
42550                   if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; }
42551                 }
42552               }
42553               if (is_forward && y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor
42554                 u = a_map(x,y - 1,z,0);
42555                 v = a_map(x,y - 1,z,1);
42556                 w = a_map(x,y - 1,z,2);
42557                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42558                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
42559                     w>=cz1 && w<patch_image.depth() - cz2) {
42560                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42561                                   xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,
42562                                   u,v,w,_patch_penalization,allow_identity,best_score);
42563                   if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; }
42564                 }
42565               }
42566               if (is_forward && z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor
42567                 u = a_map(x,y,z - 1,0);
42568                 v = a_map(x,y,z - 1,1);
42569                 w = a_map(x,y,z - 1,2);
42570                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42571                     v>=cy1 && v<patch_image.height() - cy2 &&
42572                     w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
42573                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42574                                   xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,
42575                                   u,v,w,_patch_penalization,allow_identity,best_score);
42576                   if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; }
42577                 }
42578               }
42579               if (is_backward && x<width() - 1 && (is_updated(x + 1,y,z)&cmask)) { // Compare with right neighbor
42580                 u = a_map(x + 1,y,z,0);
42581                 v = a_map(x + 1,y,z,1);
42582                 w = a_map(x + 1,y,z,2);
42583                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
42584                     v>=cy1 && v<patch_image.height() - cy2 &&
42585                     w>=cz1 && w<patch_image.depth() - cz2) {
42586                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42587                                   xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,
42588                                   u,v,w,_patch_penalization,allow_identity,best_score);
42589                   if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; }
42590                 }
42591               }
42592               if (is_backward && y<height() - 1 && (is_updated(x,y + 1,z)&cmask)) { // Compare with bottom neighbor
42593                 u = a_map(x,y + 1,z,0);
42594                 v = a_map(x,y + 1,z,1);
42595                 w = a_map(x,y + 1,z,2);
42596                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42597                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
42598                     w>=cz1 && w<patch_image.depth() - cz2) {
42599                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42600                                   xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,
42601                                   u,v,w,_patch_penalization,allow_identity,best_score);
42602                   if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; }
42603                 }
42604               }
42605               if (is_backward && z<depth() - 1 && (is_updated(x,y,z + 1)&cmask)) { // Compare with forward neighbor
42606                 u = a_map(x,y,z + 1,0);
42607                 v = a_map(x,y,z + 1,1);
42608                 w = a_map(x,y,z + 1,2);
42609                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42610                     v>=cy1 && v<patch_image.height() - cy2 &&
42611                     w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
42612                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42613                                   xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,
42614                                   u,v,w,_patch_penalization,allow_identity,best_score);
42615                   if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; }
42616                 }
42617               }
42618 
42619               float
42620                 dw = (float)patch_image.width(),
42621                 dh = (float)patch_image.height(),
42622                 dd = (float)patch_image.depth();
42623               for (unsigned int i = 0; i<nb_randoms; ++i) {
42624                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
42625                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
42626                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
42627                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
42628                 w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd),
42629                                                 std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng));
42630                 if (u!=best_u || v!=best_v || w!=best_w) {
42631                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42632                                   xp,yp,zp,u - cx1,v - cy1,w - cz1,
42633                                   u,v,w,_patch_penalization,allow_identity,best_score);
42634                   if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; }
42635                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f);
42636                 }
42637               }
42638 
42639               if (best_score<best_score0) {
42640                 if (_patch_penalization!=0) {
42641                   float &p_penalty = penalty(a_map(x,y,z,0),a_map(x,y,z,1),a_map(x,y,z,2));
42642                   if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
42643                 }
42644                 a_map(x,y,z,0) = best_u;
42645                 a_map(x,y,z,1) = best_v;
42646                 a_map(x,y,z,2) = best_w;
42647                 score(x,y,z) = best_score;
42648                 is_updated(x,y,z) = 3;
42649               } else is_updated(x,y,z)&=~nmask;
42650               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v,best_w);
42651             }
42652             cimg::srand(rng);
42653           }
42654 
42655           // Update score according to new penalties.
42656           if (penalty)
42657             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
42658               cimg_forXYZ(score,x,y,z) {
42659               const float p_score = score(x,y,z);
42660               const int
42661                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),
42662                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
42663                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),
42664                 xp = x - cx1,
42665                 yp = y - cy1,
42666                 zp = z - cz1,
42667                 u = a_map(x,y,z,0),
42668                 v = a_map(x,y,z,1),
42669                 w = a_map(x,y,z,2);
42670               const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
42671                                                 xp,yp,zp,u - cx1,v - cy1,w - cz1,
42672                                                 u,v,w,_patch_penalization,allow_identity,cimg::type<float>::inf());
42673               if (n_score!=p_score) { score(x,y,z) = n_score; is_updated(x,y) = 3; }
42674             }
42675         }
42676 
42677       } else { // 2D version
42678 
42679         // Initialize correspondence map.
42680         if (guide)
42681           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,64))
42682             cimg_forXY(*this,x,y) { // User-defined initialization
42683             const int
42684               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42685               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42686               u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2),
42687               v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2);
42688             a_map(x,y,0) = u;
42689             a_map(x,y,1) = v;
42690             score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42691                                      x - cx1,y - cy1,u - cx1,v - cy1,
42692                                      u,v,0,allow_identity,cimg::type<float>::inf());
42693           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
42694             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42695 
42696 #if cimg_use_openmp!=0
42697             rng+=omp_get_thread_num();
42698 #endif
42699             cimg_pragma_openmp(for)
42700               cimg_forXY(*this,x,y) { // Random initialization
42701               const int
42702                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42703                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42704                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
42705                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng));
42706               a_map(x,y,0) = u;
42707               a_map(x,y,1) = v;
42708               score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42709                                        x - cx1,y - cy1,u - cx1,v - cy1,
42710                                        u,v,0,allow_identity,cimg::type<float>::inf());
42711             }
42712             cimg::srand(rng);
42713           }
42714 
42715         // Start iteration loop.
42716         cimg_abort_init;
42717         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
42718           cimg_abort_test;
42719           const bool is_backward = iter&1, is_forward = !is_backward;
42720           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
42721 
42722           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
42723             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
42724 
42725 #if cimg_use_openmp!=0
42726             rng+=omp_get_thread_num();
42727 #endif
42728             cimg_pragma_openmp(for)
42729               cimg_forXY(*this,X,Y) {
42730               const int
42731                 x = is_backward?width() - 1 - X:X,
42732                 y = is_backward?height() - 1 - Y:Y;
42733               if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
42734               const int
42735                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
42736                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
42737                 xp = x - cx1,
42738                 yp = y - cy1;
42739 
42740               int best_u = a_map(x,y,0), best_v = a_map(x,y,1), u, v;
42741               const float best_score0 = score(x,y);
42742               float best_score = best_score0, s;
42743 
42744               if (is_forward && x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor
42745                 u = a_map(x - 1,y,0);
42746                 v = a_map(x - 1,y,1);
42747                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
42748                     v>=cy1 && v<patch_image.height() - cy2) {
42749                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42750                                   xp,yp,u + 1 - cx1,v - cy1,
42751                                   u,v,_patch_penalization,allow_identity,best_score);
42752                   if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; }
42753                 }
42754               }
42755               if (is_forward && y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor
42756                 u = a_map(x,y - 1,0);
42757                 v = a_map(x,y - 1,1);
42758                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42759                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
42760                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42761                                   xp,yp,u - cx1,v + 1 - cy1,
42762                                   u,v,_patch_penalization,allow_identity,best_score);
42763                   if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; }
42764                 }
42765               }
42766               if (is_backward && x<width() - 1 && (is_updated(x + 1,y)&cmask)) { // Compare with right neighbor
42767                 u = a_map(x + 1,y,0);
42768                 v = a_map(x + 1,y,1);
42769                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
42770                     v>=cy1 && v<patch_image.height() - cy2) {
42771                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42772                                   xp,yp,u - 1 - cx1,v - cy1,
42773                                   u,v,_patch_penalization,allow_identity,best_score);
42774                   if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; }
42775                 }
42776               }
42777               if (is_backward && y<height() - 1 && (is_updated(x,y + 1)&cmask)) { // Compare with bottom neighbor
42778                 u = a_map(x,y + 1,0);
42779                 v = a_map(x,y + 1,1);
42780                 if (u>=cx1 && u<patch_image.width() - cx2 &&
42781                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
42782                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42783                                   xp,yp,u - cx1,v - 1 - cy1,
42784                                   u,v,_patch_penalization,allow_identity,best_score);
42785                   if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; }
42786                 }
42787               }
42788 
42789               float
42790                 dw = (float)patch_image.width(),
42791                 dh = (float)patch_image.height();
42792               for (unsigned int i = 0; i<nb_randoms; ++i) {
42793                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
42794                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
42795                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
42796                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
42797                 if (u!=best_u || v!=best_v) {
42798                   s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42799                                   xp,yp,u - cx1,v - cy1,
42800                                   u,v,_patch_penalization,allow_identity,best_score);
42801                   if (s<best_score) { best_u = u; best_v = v; best_score = s; }
42802                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f);
42803                 }
42804               }
42805 
42806               if (best_score<best_score0) {
42807                 if (_patch_penalization!=0) {
42808                   float &p_penalty = penalty(a_map(x,y,0),a_map(x,y,1));
42809                   if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
42810                 }
42811                 a_map(x,y,0) = best_u;
42812                 a_map(x,y,1) = best_v;
42813                 score(x,y) = best_score;
42814                 is_updated(x,y) = 3;
42815               } else is_updated(x,y)&=~nmask;
42816               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v);
42817             }
42818             cimg::srand(rng);
42819           }
42820 
42821           // Update score according to new penalties.
42822           if (penalty)
42823             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
42824               cimg_forXY(score,x,y) {
42825               const float p_score = score(x,y);
42826               const int
42827                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),
42828                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
42829                 xp = x - cx1,
42830                 yp = y - cy1,
42831                 u = a_map(x,y,0),
42832                 v = a_map(x,y,1);
42833               const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
42834                                                 xp,yp,u - cx1,v - cy1,
42835                                                 u,v,_patch_penalization,allow_identity,cimg::type<float>::inf());
42836               if (n_score!=p_score) { score(x,y) = n_score; is_updated(x,y) = 3; }
42837             }
42838         }
42839       }
42840 
42841       if (is_matching_score) score.move_to(matching_score);
42842       return a_map;
42843     }
42844 
42845     // Compute SSD between two patches in different images.
42846     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
42847                              const unsigned int psizew, const unsigned int psizeh,
42848                              const unsigned int psized, const unsigned int psizec,
42849                              const int x1, const int y1, const int z1,
42850                              const int x2, const int y2, const int z2,
42851                              const int xc, const int yc, const int zc,
42852                              const float patch_penalization,
42853                              const bool allow_identity,
42854                              const float max_score) { // 3D version
42855       if (!allow_identity && cimg::hypot((float)x1 - x2,(float)y1 - y2,(float)z1 - z2)<patch_penalization)
42856         return cimg::type<float>::inf();
42857       const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2);
42858       const unsigned int psizewc = psizew*psizec;
42859       const ulongT
42860         offx1 = (ulongT)img1._width - psizewc,
42861         offx2 = (ulongT)img2._width - psizewc,
42862         offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width,
42863         offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width;
42864       float ssd = 0;
42865       for (unsigned int k = 0; k<psized; ++k) {
42866         for (unsigned int j = 0; j<psizeh; ++j) {
42867           for (unsigned int i = 0; i<psizewc; ++i)
42868             ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
42869           if (ssd>max_score) return max_score;
42870           p1+=offx1; p2+=offx2;
42871         }
42872         p1+=offy1; p2+=offy2;
42873       }
42874       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
42875                                                patch_penalization*psizewc*psizeh*psized*penalty(xc,yc,zc)/100);
42876     }
42877 
42878     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
42879                              const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec,
42880                              const int x1, const int y1,
42881                              const int x2, const int y2,
42882                              const int xc, const int yc,
42883                              const float patch_penalization,
42884                              const bool allow_identity,
42885                              const float max_score) { // 2D version
42886       if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)<patch_penalization)
42887         return cimg::type<float>::inf();
42888       const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2);
42889       const unsigned int psizewc = psizew*psizec;
42890       const ulongT
42891         offx1 = (ulongT)img1._width - psizewc,
42892         offx2 = (ulongT)img2._width - psizewc;
42893       float ssd = 0;
42894       for (unsigned int j = 0; j<psizeh; ++j) {
42895         for (unsigned int i = 0; i<psizewc; ++i)
42896           ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
42897         if (ssd>max_score) return max_score;
42898         p1+=offx1; p2+=offx2;
42899       }
42900       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
42901                                                  patch_penalization*psizewc*psizeh*penalty(xc,yc)/100);
42902     }
42903 
42904     //! Compute Euclidean distance function to a specified value.
42905     /**
42906         \param value Reference value.
42907         \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
42908         \note
42909         The distance transform implementation has been submitted by A. Meijster, and implements
42910         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
42911                      "A general algorithm for computing distance transforms in linear time.",
42912                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
42913                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
42914          The submitted code has then been modified to fit CImg coding style and constraints.
42915     **/
42916     CImg<T>& distance(const T& value, const unsigned int metric=2) {
42917       if (is_empty()) return *this;
42918       if (cimg::type<Tint>::string()!=pixel_type()) // For datatype < int
42919         return CImg<Tint>(*this,false).distance((Tint)value,metric).
42920           cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
42921       bool is_value = false;
42922       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
42923       if (!is_value) return fill(cimg::type<T>::max());
42924       switch (metric) {
42925       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev
42926       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan
42927       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Squared Euclidean
42928       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Euclidean
42929       }
42930       return *this;
42931     }
42932 
42933     //! Compute distance to a specified value \newinstance.
42934     CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
42935       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
42936     }
42937 
42938     static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
42939       return (u*u - i*i + g[u] - g[i])/(2*(u - i));
42940     }
42941 
42942     static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
42943       return (x - i)*(x - i) + g[i];
42944     }
42945 
42946     static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
42947       return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
42948     }
42949 
42950     static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
42951       return (x<i?i - x:x - i) + g[i];
42952     }
42953 
42954     static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
42955       const longT h = (i + u)/2;
42956       if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
42957       return h<u - g[i]?h:u - g[i];
42958     }
42959 
42960     static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
42961       const longT d = x<i?i - x:x - i;
42962       return d<g[i]?g[i]:d;
42963     }
42964 
42965     static void _distance_scan(const unsigned int len,
42966                                const longT *const g,
42967                                longT (*const sep)(const longT, const longT, const longT *const),
42968                                longT (*const f)(const longT, const longT, const longT *const),
42969                                longT *const s,
42970                                longT *const t,
42971                                longT *const dt) {
42972       longT q = s[0] = t[0] = 0;
42973       for (int u = 1; u<(int)len; ++u) { // Forward scan
42974         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
42975         if (q<0) { q = 0; s[0] = u; }
42976         else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
42977       }
42978       for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan
42979     }
42980 
42981     CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
42982                             longT (*const f)(const longT, const longT, const longT *const)) {
42983  // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
42984 #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
42985 
42986       const ulongT wh = (ulongT)_width*_height;
42987 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42988       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
42989 #endif
42990       cimg_forC(*this,c) {
42991         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
42992         CImg<T> img = get_shared_channel(c);
42993 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
42994         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
42995                                                                    _height*_depth>=16)
42996                            firstprivate(g,dt,s,t))
42997 #endif
42998         cimg_forYZ(*this,y,z) { // Over X-direction
42999           cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
43000           _distance_scan(_width,g,sep,f,s,t,dt);
43001           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
43002         }
43003         if (_height>1) {
43004           g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
43005 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
43006           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
43007                              cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16)
43008                              firstprivate(g,dt,s,t))
43009 #endif
43010           cimg_forXZ(*this,x,z) { // Over Y-direction
43011             cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
43012             _distance_scan(_height,g,sep,f,s,t,dt);
43013             cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
43014           }
43015         }
43016         if (_depth>1) {
43017           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
43018 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
43019           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
43020                              cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16)
43021                              firstprivate(g,dt,s,t))
43022 #endif
43023           cimg_forXY(*this,x,y) { // Over Z-direction
43024             cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
43025             _distance_scan(_depth,g,sep,f,s,t,dt);
43026             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
43027           }
43028         }
43029       }
43030       return *this;
43031     }
43032 
43033     //! Compute chamfer distance to a specified value, with a custom metric.
43034     /**
43035        \param value Reference value.
43036        \param metric_mask Metric mask.
43037        \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
43038     **/
43039     template<typename t>
43040     CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
43041       if (is_empty()) return *this;
43042       bool is_value = false;
43043       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
43044       if (!is_value) return fill(cimg::type<T>::max());
43045       const ulongT wh = (ulongT)_width*_height;
43046       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
43047       cimg_forC(*this,c) {
43048         CImg<T> img = get_shared_channel(c);
43049         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
43050                            cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024))
43051         cimg_forXYZ(metric_mask,dx,dy,dz) {
43052           const t weight = metric_mask(dx,dy,dz);
43053           if (weight) {
43054             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan
43055               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
43056                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
43057                   const T dd = img(nx,ny,nz,0,wh) + weight;
43058                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
43059                 }
43060               }
43061             }
43062             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan
43063               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
43064                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
43065                   const T dd = img(nx,ny,nz,0,wh) + weight;
43066                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
43067                 }
43068               }
43069             }
43070           }
43071         }
43072       }
43073       return *this;
43074     }
43075 
43076     //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
43077     template<typename t>
43078     CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
43079       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
43080     }
43081 
43082     //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
43083     /**
43084        \param value Reference value.
43085        \param metric Field of distance potentials.
43086        \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
43087        \param[out] return_path An image containing the nodes of the minimal path.
43088      **/
43089     template<typename t, typename to>
43090     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
43091                                CImg<to>& return_path) {
43092       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
43093     }
43094 
43095     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
43096     template<typename t, typename to>
43097     CImg<typename cimg::superset<t,long>::type>
43098     get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
43099                           CImg<to>& return_path) const {
43100       if (is_empty()) return return_path.assign();
43101       if (!is_sameXYZ(metric))
43102         throw CImgArgumentException(_cimg_instance
43103                                     "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
43104                                     "have incompatible dimensions.",
43105                                     cimg_instance,
43106                                     metric._width,metric._height,metric._depth,metric._spectrum);
43107       typedef typename cimg::superset<t,long>::type td;  // Type used for computing cumulative distances
43108       CImg<td> result(_width,_height,_depth,_spectrum), Q;
43109       CImg<boolT> is_queued(_width,_height,_depth,1);
43110       if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
43111 
43112       cimg_forC(*this,c) {
43113         const CImg<T> img = get_shared_channel(c);
43114         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
43115         CImg<td> res = result.get_shared_channel(c);
43116         CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
43117         unsigned int sizeQ = 0;
43118 
43119         // Detect initial seeds.
43120         is_queued.fill(0);
43121         cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
43122           Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
43123           res(x,y,z) = 0;
43124           if (path) path(x,y,z) = (to)0;
43125         }
43126 
43127         // Start distance propagation.
43128         while (sizeQ) {
43129 
43130           // Get and remove point with minimal potential from the queue.
43131           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
43132           const td P = (td)-Q(0,0);
43133           Q._priority_queue_remove(sizeQ);
43134 
43135           // Update neighbors.
43136           td npot = 0;
43137           if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
43138             res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
43139           }
43140           if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
43141             res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
43142           }
43143           if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
43144             res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
43145           }
43146           if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
43147             res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
43148           }
43149           if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
43150             res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
43151           }
43152           if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
43153             res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
43154           }
43155 
43156           if (is_high_connectivity) {
43157             const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f);
43158 
43159             // Diagonal neighbors on slice z.
43160             if (x - 1>=0 && y - 1>=0 &&
43161                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
43162               res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
43163             }
43164             if (x + 1<width() && y - 1>=0 &&
43165                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
43166               res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
43167             }
43168             if (x - 1>=0 && y + 1<height() &&
43169                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
43170               res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
43171             }
43172             if (x + 1<width() && y + 1<height() &&
43173                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
43174               res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
43175             }
43176 
43177             if (z - 1>=0) { // Diagonal neighbors on slice z - 1
43178               if (x - 1>=0 &&
43179                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
43180                 res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
43181               }
43182               if (x + 1<width() &&
43183                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
43184                 res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
43185               }
43186               if (y - 1>=0 &&
43187                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
43188                 res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
43189               }
43190               if (y + 1<height() &&
43191                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
43192                 res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
43193               }
43194               if (x - 1>=0 && y - 1>=0 &&
43195                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
43196                                            x - 1,y - 1,z - 1)) {
43197                 res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
43198               }
43199               if (x + 1<width() && y - 1>=0 &&
43200                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
43201                                            x + 1,y - 1,z - 1)) {
43202                 res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
43203               }
43204               if (x - 1>=0 && y + 1<height() &&
43205                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
43206                                            x - 1,y + 1,z - 1)) {
43207                 res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
43208               }
43209               if (x + 1<width() && y + 1<height() &&
43210                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
43211                                            x + 1,y + 1,z - 1)) {
43212                 res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
43213               }
43214             }
43215 
43216             if (z + 1<depth()) { // Diagonal neighbors on slice z + 1
43217               if (x - 1>=0 &&
43218                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
43219                 res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
43220               }
43221               if (x + 1<width() &&
43222                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
43223                 res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
43224               }
43225               if (y - 1>=0 &&
43226                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
43227                 res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
43228               }
43229               if (y + 1<height() &&
43230                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
43231                 res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
43232               }
43233               if (x - 1>=0 && y - 1>=0 &&
43234                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
43235                                            x - 1,y - 1,z + 1)) {
43236                 res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
43237               }
43238               if (x + 1<width() && y - 1>=0 &&
43239                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
43240                                            x + 1,y - 1,z + 1)) {
43241                 res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
43242               }
43243               if (x - 1>=0 && y + 1<height() &&
43244                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
43245                                            x - 1,y + 1,z + 1)) {
43246                 res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
43247               }
43248               if (x + 1<width() && y + 1<height() &&
43249                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
43250                                            x + 1,y + 1,z + 1)) {
43251                 res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
43252               }
43253             }
43254           }
43255         }
43256       }
43257       return result;
43258     }
43259 
43260     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
43261     template<typename t>
43262     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
43263                                const bool is_high_connectivity=false) {
43264       return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
43265     }
43266 
43267     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
43268     template<typename t>
43269     CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
43270                                        const bool is_high_connectivity=false) const {
43271       CImg<T> return_path;
43272       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
43273     }
43274 
43275     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
43276     /**
43277        \param value Reference value.
43278        \param metric Field of distance potentials.
43279      **/
43280     template<typename t>
43281     CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
43282       return get_distance_eikonal(value,metric).move_to(*this);
43283     }
43284 
43285     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
43286     template<typename t>
43287     CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
43288       if (is_empty()) return *this;
43289       if (!is_sameXYZ(metric))
43290         throw CImgArgumentException(_cimg_instance
43291                                     "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
43292                                     "incompatible dimensions.",
43293                                     cimg_instance,
43294                                     metric._width,metric._height,metric._depth,metric._spectrum);
43295       CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
43296       CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen
43297 
43298       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
43299       cimg_forC(*this,c) {
43300         const CImg<T> img = get_shared_channel(c);
43301         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
43302         CImg<Tfloat> res = result.get_shared_channel(c);
43303         unsigned int sizeQ = 0;
43304         state.fill(-1);
43305 
43306         // Detect initial seeds.
43307         Tfloat *ptr1 = res._data; char *ptr2 = state._data;
43308         cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
43309 
43310         // Initialize seeds neighbors.
43311         ptr2 = state._data;
43312         cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
43313           if (x - 1>=0 && state(x - 1,y,z)==-1) {
43314             const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
43315             Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
43316           }
43317           if (x + 1<width() && state(x + 1,y,z)==-1) {
43318             const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
43319             Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
43320           }
43321           if (y - 1>=0 && state(x,y - 1,z)==-1) {
43322             const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
43323             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
43324           }
43325           if (y + 1<height() && state(x,y + 1,z)==-1) {
43326             const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
43327             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
43328           }
43329           if (z - 1>=0 && state(x,y,z - 1)==-1) {
43330             const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
43331             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
43332           }
43333           if (z + 1<depth() && state(x,y,z + 1)==-1) {
43334             const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
43335             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
43336           }
43337         }
43338 
43339         // Propagate front.
43340         while (sizeQ) {
43341           int x = -1, y = -1, z = -1;
43342           while (sizeQ && x<0) {
43343             x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
43344             Q._priority_queue_remove(sizeQ);
43345             if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
43346           }
43347           if (x>=0) {
43348             if (x - 1>=0 && state(x - 1,y,z)!=1) {
43349               const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
43350               if (dist<res(x - 1,y,z)) {
43351                 res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
43352               }
43353             }
43354             if (x + 1<width() && state(x + 1,y,z)!=1) {
43355               const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
43356               if (dist<res(x + 1,y,z)) {
43357                 res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
43358               }
43359             }
43360             if (y - 1>=0 && state(x,y - 1,z)!=1) {
43361               const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
43362               if (dist<res(x,y - 1,z)) {
43363                 res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
43364               }
43365             }
43366             if (y + 1<height() && state(x,y + 1,z)!=1) {
43367               const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
43368               if (dist<res(x,y + 1,z)) {
43369                 res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
43370               }
43371             }
43372             if (z - 1>=0 && state(x,y,z - 1)!=1) {
43373               const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
43374               if (dist<res(x,y,z - 1)) {
43375                 res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
43376               }
43377             }
43378             if (z + 1<depth() && state(x,y,z + 1)!=1) {
43379               const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
43380               if (dist<res(x,y,z + 1)) {
43381                 res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
43382               }
43383             }
43384           }
43385         }
43386       }
43387       return result;
43388     }
43389 
43390     // Locally solve eikonal equation.
43391     Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
43392                               const int x=0, const int y=0, const int z=0) const {
43393       const Tfloat M = (Tfloat)cimg::type<T>::max();
43394       T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
43395       Tfloat root = 0;
43396       if (_depth>1) { // 3D
43397         T
43398           T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
43399           T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
43400         if (T1>T2) cimg::swap(T1,T2);
43401         if (T2>T3) cimg::swap(T2,T3);
43402         if (T1>T2) cimg::swap(T1,T2);
43403         if (P<=0) return (Tfloat)T1;
43404         if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
43405           return std::max((Tfloat)T3,root);
43406         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
43407           return std::max((Tfloat)T2,root);
43408         return P + T1;
43409       } else if (_height>1) { // 2D
43410         T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
43411         if (T1>T2) cimg::swap(T1,T2);
43412         if (P<=0) return (Tfloat)T1;
43413         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
43414           return std::max((Tfloat)T2,root);
43415         return P + T1;
43416       } else { // 1D
43417         if (P<=0) return (Tfloat)T1;
43418         return P + T1;
43419       }
43420       return 0;
43421     }
43422 
43423     // Find max root of a 2nd-order polynomial.
43424     static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
43425       const Tfloat delta = b*b - 4*a*c;
43426       if (delta<0) return false;
43427       root = 0.5f*(-b + std::sqrt(delta))/a;
43428       return true;
43429     }
43430 
43431     // Insert new point in heap.
43432     template<typename t>
43433     void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
43434                                     const unsigned int x, const unsigned int y, const unsigned int z) {
43435       if (state(x,y,z)>0) return;
43436       state(x,y,z) = 0;
43437       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
43438       (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
43439       for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
43440         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
43441         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
43442       }
43443     }
43444 
43445     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
43446     /**
43447        \param nb_iterations Number of PDE iterations.
43448        \param band_size Size of the narrow band.
43449        \param time_step Time step of the PDE iterations.
43450     **/
43451     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
43452       if (is_empty()) return *this;
43453       CImg<Tfloat> velocity(*this,false);
43454       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
43455         Tfloat *ptrd = velocity._data, veloc_max = 0;
43456         if (_depth>1) { // 3D
43457           CImg_3x3x3(I,Tfloat);
43458           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
43459             const Tfloat
43460               gx = (Incc - Ipcc)/2,
43461               gy = (Icnc - Icpc)/2,
43462               gz = (Iccn - Iccp)/2,
43463               sgn = -cimg::sign(Iccc),
43464               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
43465               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
43466               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
43467               ng = 1e-5f + cimg::hypot(gx,gy,gz),
43468               ngx = gx/ng,
43469               ngy = gy/ng,
43470               ngz = gz/ng,
43471               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
43472             *(ptrd++) = veloc;
43473             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
43474           } else *(ptrd++) = 0;
43475         } else { // 2D version
43476           CImg_3x3(I,Tfloat);
43477           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
43478             const Tfloat
43479               gx = (Inc - Ipc)/2,
43480               gy = (Icn - Icp)/2,
43481               sgn = -cimg::sign(Icc),
43482               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
43483               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
43484               ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
43485               ngx = gx/ng,
43486               ngy = gy/ng,
43487               veloc = sgn*(ngx*ix + ngy*iy - 1);
43488             *(ptrd++) = veloc;
43489             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
43490           } else *(ptrd++) = 0;
43491         }
43492         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
43493       }
43494       return *this;
43495     }
43496 
43497     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
43498     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
43499                                       const float time_step=0.5f) const {
43500       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
43501     }
43502 
43503     //! Compute Haar multiscale wavelet transform.
43504     /**
43505        \param axis Axis considered for the transform.
43506        \param invert Set inverse of direct transform.
43507        \param nb_scales Number of scales used for the transform.
43508     **/
43509     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
43510       return get_haar(axis,invert,nb_scales).move_to(*this);
43511     }
43512 
43513     //! Compute Haar multiscale wavelet transform \newinstance.
43514     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
43515       if (is_empty() || !nb_scales) return +*this;
43516       CImg<Tfloat> res;
43517       const Tfloat sqrt2 = std::sqrt(2.f);
43518       if (nb_scales==1) {
43519         switch (cimg::lowercase(axis)) { // Single scale transform
43520         case 'x' : {
43521           const unsigned int w = _width/2;
43522           if (w) {
43523             if ((w%2) && w!=1)
43524               throw CImgInstanceException(_cimg_instance
43525                                           "haar(): Sub-image width %u is not even.",
43526                                           cimg_instance,
43527                                           w);
43528 
43529             res.assign(_width,_height,_depth,_spectrum);
43530             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
43531               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
43532                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
43533                 res(x2++,y,z,c) = (val0 - val1)/sqrt2;
43534                 res(x2++,y,z,c) = (val0 + val1)/sqrt2;
43535               }
43536             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
43537               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
43538                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
43539                 res(x,y,z,c) = (val0 + val1)/sqrt2;
43540                 res(xw,y,z,c) = (val1 - val0)/sqrt2;
43541               }
43542             }
43543           } else return *this;
43544         } break;
43545         case 'y' : {
43546           const unsigned int h = _height/2;
43547           if (h) {
43548             if ((h%2) && h!=1)
43549               throw CImgInstanceException(_cimg_instance
43550                                           "haar(): Sub-image height %u is not even.",
43551                                           cimg_instance,
43552                                           h);
43553 
43554             res.assign(_width,_height,_depth,_spectrum);
43555             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
43556               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
43557                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
43558                 res(x,y2++,z,c) = (val0 - val1)/sqrt2;
43559                 res(x,y2++,z,c) = (val0 + val1)/sqrt2;
43560               }
43561             } else cimg_forXZC(*this,x,z,c) {
43562               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
43563                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
43564                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
43565                 res(x,yh,z,c) = (val1 - val0)/sqrt2;
43566               }
43567             }
43568           } else return *this;
43569         } break;
43570         case 'z' : {
43571           const unsigned int d = _depth/2;
43572           if (d) {
43573             if ((d%2) && d!=1)
43574               throw CImgInstanceException(_cimg_instance
43575                                           "haar(): Sub-image depth %u is not even.",
43576                                           cimg_instance,
43577                                           d);
43578 
43579             res.assign(_width,_height,_depth,_spectrum);
43580             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
43581               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
43582                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
43583                 res(x,y,z2++,c) = (val0 - val1)/sqrt2;
43584                 res(x,y,z2++,c) = (val0 + val1)/sqrt2;
43585               }
43586             } else cimg_forXYC(*this,x,y,c) {
43587               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
43588                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
43589                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
43590                 res(x,y,zd,c) = (val1 - val0)/sqrt2;
43591               }
43592             }
43593           } else return *this;
43594         } break;
43595         default :
43596           throw CImgArgumentException(_cimg_instance
43597                                       "haar(): Invalid specified axis '%c' "
43598                                       "(should be { x | y | z }).",
43599                                       cimg_instance,
43600                                       axis);
43601         }
43602       } else { // Multi-scale version
43603         if (invert) {
43604           res.assign(*this,false);
43605           switch (cimg::lowercase(axis)) {
43606           case 'x' : {
43607             unsigned int w = _width;
43608             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
43609             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
43610           } break;
43611           case 'y' : {
43612             unsigned int h = _width;
43613             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
43614             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));
43615           } break;
43616           case 'z' : {
43617             unsigned int d = _depth;
43618             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
43619             for (d = d?d:1; d<=_depth; d*=2)
43620               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
43621           } break;
43622           default :
43623             throw CImgArgumentException(_cimg_instance
43624                                         "haar(): Invalid specified axis '%c' "
43625                                         "(should be { x | y | z }).",
43626                                         cimg_instance,
43627                                         axis);
43628           }
43629         } else { // Direct transform
43630           res = get_haar(axis,false,1);
43631           switch (cimg::lowercase(axis)) {
43632           case 'x' : {
43633             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
43634               res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
43635           } break;
43636           case 'y' : {
43637             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
43638               res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
43639           } break;
43640           case 'z' : {
43641             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
43642               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
43643           } break;
43644           default :
43645             throw CImgArgumentException(_cimg_instance
43646                                         "haar(): Invalid specified axis '%c' "
43647                                         "(should be { x | y | z }).",
43648                                         cimg_instance,
43649                                         axis);
43650           }
43651         }
43652       }
43653       return res;
43654     }
43655 
43656     //! Compute Haar multiscale wavelet transform \overloading.
43657     /**
43658        \param invert Set inverse of direct transform.
43659        \param nb_scales Number of scales used for the transform.
43660     **/
43661     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
43662       return get_haar(invert,nb_scales).move_to(*this);
43663     }
43664 
43665     //! Compute Haar multiscale wavelet transform \newinstance.
43666     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
43667       CImg<Tfloat> res;
43668       if (nb_scales==1) { // Single scale transform
43669         if (_width>1) get_haar('x',invert,1).move_to(res);
43670         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
43671         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
43672         if (res) return res;
43673       } else { // Multi-scale transform
43674         if (invert) { // Inverse transform
43675           res.assign(*this,false);
43676           if (_width>1) {
43677             if (_height>1) {
43678               if (_depth>1) {
43679                 unsigned int w = _width, h = _height, d = _depth;
43680                 for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
43681                 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)
43682                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
43683               } else {
43684                 unsigned int w = _width, h = _height;
43685                 for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
43686                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
43687                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
43688               }
43689             } else {
43690               if (_depth>1) {
43691                 unsigned int w = _width, d = _depth;
43692                 for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
43693                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
43694                   res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
43695               } else {
43696                 unsigned int w = _width;
43697                 for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
43698                 for (w = w?w:1; w<=_width; w*=2)
43699                   res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
43700               }
43701             }
43702           } else {
43703             if (_height>1) {
43704               if (_depth>1) {
43705                 unsigned int h = _height, d = _depth;
43706                 for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
43707                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
43708                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
43709               } else {
43710                 unsigned int h = _height;
43711                 for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
43712                 for (h = h?h:1; h<=_height; h*=2)
43713                   res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
43714               }
43715             } else {
43716               if (_depth>1) {
43717                 unsigned int d = _depth;
43718                 for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
43719                 for (d = d?d:1; d<=_depth; d*=2)
43720                   res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
43721               } else return *this;
43722             }
43723           }
43724         } else { // Direct transform
43725           res = get_haar(false,1);
43726           if (_width>1) {
43727             if (_height>1) {
43728               if (_depth>1)
43729                 for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
43730                      ++s, w/=2, h/=2, d/=2)
43731                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
43732               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
43733                      res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
43734             } else {
43735               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
43736                               res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
43737               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
43738                      res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
43739             }
43740           } else {
43741             if (_height>1) {
43742               if (_depth>1)
43743                 for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
43744                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
43745               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
43746                      res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
43747             } else {
43748               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
43749                               res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
43750               else return *this;
43751             }
43752           }
43753         }
43754         return res;
43755       }
43756       return *this;
43757     }
43758 
43759     //! Compute 1D Fast Fourier Transform, along a specified axis.
43760     /**
43761        \param axis Axis along which the FFT is computed.
43762        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43763     **/
43764     CImgList<Tfloat> get_FFT(const char axis, const bool is_inverse=false) const {
43765       CImgList<Tfloat> res(*this,CImg<Tfloat>());
43766       CImg<Tfloat>::FFT(res[0],res[1],axis,is_inverse);
43767       return res;
43768     }
43769 
43770     //! Compute n-D Fast Fourier Transform.
43771     /*
43772       \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43773     **/
43774     CImgList<Tfloat> get_FFT(const bool is_inverse=false) const {
43775       CImgList<Tfloat> res(*this,CImg<Tfloat>());
43776       CImg<Tfloat>::FFT(res[0],res[1],is_inverse);
43777       return res;
43778     }
43779 
43780     //! Compute 1D Fast Fourier Transform, along a specified axis.
43781     /**
43782        \param[in,out] real Real part of the pixel values.
43783        \param[in,out] imag Imaginary part of the pixel values.
43784        \param axis Axis along which the FFT is computed.
43785        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43786     **/
43787     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_inverse=false,
43788                     const unsigned int nb_threads=0) {
43789       if (!real)
43790         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
43791                                     pixel_type());
43792       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
43793       if (!real.is_sameXYZC(imag))
43794         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
43795                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
43796                                     pixel_type(),
43797                                     real._width,real._height,real._depth,real._spectrum,real._data,
43798                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
43799       const char _axis = cimg::lowercase(axis);
43800       if (_axis!='x' && _axis!='y' && _axis!='z')
43801         throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
43802                                     "(%u,%u,%u,%u) "
43803                                     "(should be { x | y | z }).",
43804                                     pixel_type(),axis,
43805                                     real._width,real._height,real._depth,real._spectrum);
43806       cimg::unused(nb_threads);
43807 #ifdef cimg_use_fftw3
43808       cimg::mutex(12);
43809 #ifndef cimg_use_fftw3_singlethread
43810       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
43811 #endif
43812       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
43813       if (!data_in)
43814         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
43815                                     "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
43816                                     pixel_type(),
43817                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth),
43818                                     real._width,real._height,real._depth,real._spectrum);
43819       double *const ptrf = (double*)data_in;
43820       fftw_plan data_plan =
43821         _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(),
43822                                       data_in,0,1,real.width(),
43823                                       data_in,0,1,real.width(),
43824                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43825         _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(),
43826                                       data_in,0,1,real.height(),
43827                                       data_in,0,1,real.height(),
43828                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43829         fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(),
43830                            data_in,0,1,real.depth(),
43831                            data_in,0,1,real.depth(),
43832                            is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
43833       cimg_forC(real,c) {
43834         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
43835         switch (_axis) {
43836         case 'x' :
43837           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43838             cimg_forXYZ(realc,x,y,z) {
43839             const ulongT
43840               i = realc.offset(x,y,z),
43841               j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height);
43842             ptrf[j] = (double)realc[i];
43843             ptrf[j + 1] = (double)imagc[i];
43844           }
43845           break;
43846         case 'y' :
43847           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43848             cimg_forXYZ(realc,x,y,z) {
43849             const ulongT
43850               i = realc.offset(x,y,z),
43851               j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height);
43852             ptrf[j] = (double)realc[i];
43853             ptrf[j + 1] = (double)imagc[i];
43854           }
43855           break;
43856         default :
43857           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43858             cimg_forXYZ(realc,x,y,z) {
43859             const ulongT
43860               i = realc.offset(x,y,z),
43861               j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth);
43862             ptrf[j] = (double)realc[i];
43863             ptrf[j + 1] = (double)imagc[i];
43864           }
43865         }
43866 
43867         fftw_execute(data_plan);
43868 
43869         const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0;
43870         switch (_axis) {
43871         case 'x' :
43872           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43873             cimg_forXYZ(realc,x,y,z) {
43874             const ulongT
43875               i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height),
43876               j = realc.offset(x,y,z);
43877             realc[j] = (T)(a*ptrf[i]);
43878             imagc[j] = (T)(a*ptrf[i + 1]);
43879           }
43880           break;
43881         case 'y' :
43882           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43883             cimg_forXYZ(realc,x,y,z) {
43884             const ulongT
43885               i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height),
43886               j = realc.offset(x,y,z);
43887             realc[j] = (T)(a*ptrf[i]);
43888             imagc[j] = (T)(a*ptrf[i + 1]);
43889           }
43890           break;
43891         default :
43892           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43893             cimg_forXYZ(realc,x,y,z) {
43894             const ulongT
43895               i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth),
43896               j = realc.offset(x,y,z);
43897             realc[j] = (T)(a*ptrf[i]);
43898             imagc[j] = (T)(a*ptrf[i + 1]);
43899           }
43900         }
43901       }
43902 
43903       fftw_destroy_plan(data_plan);
43904       fftw_free(data_in);
43905 #ifndef cimg_use_fftw3_singlethread
43906       fftw_cleanup_threads();
43907 #endif
43908       cimg::mutex(12,0);
43909 #else
43910       switch (_axis) {
43911       case 'x' : { // Fourier along X, using built-in functions
43912         const unsigned int N = real._width, N2 = N>>1;
43913         if (((N - 1)&N) && N!=1)
43914           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
43915                                       "have non 2^N dimension along the X-axis.",
43916                                       pixel_type(),
43917                                       real._width,real._height,real._depth,real._spectrum);
43918 
43919         for (unsigned int i = 0, j = 0; i<N2; ++i) {
43920           if (j>i) cimg_forYZC(real,y,z,c) {
43921               cimg::swap(real(i,y,z,c),real(j,y,z,c));
43922               cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
43923               if (j<N2) {
43924                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
43925                 cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
43926                 cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
43927               }
43928             }
43929           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
43930         }
43931         for (unsigned int delta = 2; delta<=N; delta<<=1) {
43932           const unsigned int delta2 = delta>>1;
43933           for (unsigned int i = 0; i<N; i+=delta) {
43934             float wr = 1, wi = 0;
43935             const float
43936               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
43937               ca = (float)std::cos(angle),
43938               sa = (float)std::sin(angle);
43939             for (unsigned int k = 0; k<delta2; ++k) {
43940               const unsigned int j = i + k, nj = j + delta2;
43941               cimg_forYZC(real,y,z,c) {
43942                 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);
43943                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
43944                 nir = (T)(ir - tmpr);
43945                 nii = (T)(ii - tmpi);
43946                 ir+=(T)tmpr;
43947                 ii+=(T)tmpi;
43948               }
43949               const float nwr = wr*ca-wi*sa;
43950               wi = wi*ca + wr*sa;
43951               wr = nwr;
43952             }
43953           }
43954         }
43955         if (is_inverse) { real/=N; imag/=N; }
43956       } break;
43957       case 'y' : { // Fourier along Y, using built-in functions
43958         const unsigned int N = real._height, N2 = N>>1;
43959         if (((N - 1)&N) && N!=1)
43960           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
43961                                       "have non 2^N dimension along the Y-axis.",
43962                                       pixel_type(),
43963                                       real._width,real._height,real._depth,real._spectrum);
43964 
43965         for (unsigned int i = 0, j = 0; i<N2; ++i) {
43966           if (j>i) cimg_forXZC(real,x,z,c) {
43967               cimg::swap(real(x,i,z,c),real(x,j,z,c));
43968               cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
43969               if (j<N2) {
43970                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
43971                 cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
43972                 cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
43973               }
43974             }
43975           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
43976         }
43977         for (unsigned int delta = 2; delta<=N; delta<<=1) {
43978           const unsigned int delta2 = (delta>>1);
43979           for (unsigned int i = 0; i<N; i+=delta) {
43980             float wr = 1, wi = 0;
43981             const float
43982               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
43983               ca = (float)std::cos(angle),
43984               sa = (float)std::sin(angle);
43985             for (unsigned int k = 0; k<delta2; ++k) {
43986               const unsigned int j = i + k, nj = j + delta2;
43987               cimg_forXZC(real,x,z,c) {
43988                 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);
43989                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
43990                 nir = (T)(ir - tmpr);
43991                 nii = (T)(ii - tmpi);
43992                 ir+=(T)tmpr;
43993                 ii+=(T)tmpi;
43994               }
43995               const float nwr = wr*ca-wi*sa;
43996               wi = wi*ca + wr*sa;
43997               wr = nwr;
43998             }
43999           }
44000         }
44001         if (is_inverse) { real/=N; imag/=N; }
44002       } break;
44003       default : { // Fourier along Z, using built-in functions
44004         const unsigned int N = real._depth, N2 = N>>1;
44005         if (((N - 1)&N) && N!=1)
44006           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
44007                                       "have non 2^N dimension along the Z-axis.",
44008                                       pixel_type(),
44009                                       real._width,real._height,real._depth,real._spectrum);
44010 
44011         for (unsigned int i = 0, j = 0; i<N2; ++i) {
44012           if (j>i) cimg_forXYC(real,x,y,c) {
44013               cimg::swap(real(x,y,i,c),real(x,y,j,c));
44014               cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
44015               if (j<N2) {
44016                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
44017                 cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
44018                 cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
44019               }
44020             }
44021           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
44022         }
44023         for (unsigned int delta = 2; delta<=N; delta<<=1) {
44024           const unsigned int delta2 = (delta>>1);
44025           for (unsigned int i = 0; i<N; i+=delta) {
44026             float wr = 1, wi = 0;
44027             const float
44028               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
44029               ca = (float)std::cos(angle),
44030               sa = (float)std::sin(angle);
44031             for (unsigned int k = 0; k<delta2; ++k) {
44032               const unsigned int j = i + k, nj = j + delta2;
44033               cimg_forXYC(real,x,y,c) {
44034                 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);
44035                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
44036                 nir = (T)(ir - tmpr);
44037                 nii = (T)(ii - tmpi);
44038                 ir+=(T)tmpr;
44039                 ii+=(T)tmpi;
44040               }
44041               const float nwr = wr*ca-wi*sa;
44042               wi = wi*ca + wr*sa;
44043               wr = nwr;
44044             }
44045           }
44046         }
44047         if (is_inverse) { real/=N; imag/=N; }
44048       } break;
44049       }
44050 #endif
44051     }
44052 
44053     //! Compute n-D Fast Fourier Transform.
44054     /**
44055        \param[in,out] real Real part of the pixel values.
44056        \param[in,out] imag Imaginary part of the pixel values.
44057        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
44058        \param nb_threads Number of parallel threads used for the computation.
44059          Use \c 0 to set this to the number of available cpus.
44060     **/
44061     static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_inverse=false,
44062                     const unsigned int nb_threads=0) {
44063       if (!real)
44064         throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
44065                                     pixel_type());
44066       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
44067       if (!real.is_sameXYZC(imag))
44068         throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
44069                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
44070                                     pixel_type(),
44071                                     real._width,real._height,real._depth,real._spectrum,real._data,
44072                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
44073       cimg::unused(nb_threads);
44074 #ifdef cimg_use_fftw3
44075       cimg::mutex(12);
44076 #ifndef cimg_use_fftw3_singlethread
44077       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
44078 #endif
44079       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
44080       if (!data_in)
44081         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
44082                                     "for computing FFT of image (%u,%u,%u,%u).",
44083                                     pixel_type(),
44084                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*
44085                                                         real._height*real._depth*real._spectrum),
44086                                     real._width,real._height,real._depth,real._spectrum);
44087       double *const ptrf = (double*)data_in;
44088       fftw_plan data_plan =
44089         real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in,
44090                                        is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
44091         real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in,
44092                                         is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
44093         fftw_plan_dft_1d(real._width,data_in,data_in,
44094                          is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
44095       cimg_forC(real,c) {
44096         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
44097         cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
44098           cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; }
44099         fftw_execute(data_plan);
44100         if (is_inverse) {
44101           const double a = 1.0/(real.width()*real.height()*real.depth());
44102           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
44103             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); }
44104         } else
44105           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
44106             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; }
44107       }
44108       fftw_destroy_plan(data_plan);
44109       fftw_free(data_in);
44110 #ifndef cimg_use_fftw3_singlethread
44111       fftw_cleanup_threads();
44112 #endif
44113       cimg::mutex(12,0);
44114 #else
44115       if (real._depth>1) FFT(real,imag,'z',is_inverse);
44116       if (real._height>1) FFT(real,imag,'y',is_inverse);
44117       if (real._width>1) FFT(real,imag,'x',is_inverse);
44118 #endif
44119     }
44120 
44121     //@}
44122     //-------------------------------------
44123     //
44124     //! \name 3D Objects Management
44125     //@{
44126     //-------------------------------------
44127 
44128     //! Rotate 3D object's vertices.
44129     /**
44130        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
44131        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
44132        \param z Z-coordinate of the rotation axis, or second quaternion coordinate.
44133        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
44134        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
44135     **/
44136     CImg<T>& rotate_object3d(const float x, const float y, const float z, const float w,
44137                              const bool is_quaternion=false) {
44138       return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this);
44139     }
44140 
44141     CImg<Tfloat> get_rotate_object3d(const float x, const float y, const float z, const float w,
44142                                      const bool is_quaternion=false) const {
44143       if (_height!=3 || _depth>1 || _spectrum>1)
44144         throw CImgInstanceException(_cimg_instance
44145                                     "rotate_object3d(): Instance is not a set of 3D vertices.",
44146                                     cimg_instance);
44147       return CImg<Tfloat>::rotation_matrix(x,y,z,w,is_quaternion)**this;
44148     }
44149 
44150     //! Shift 3D object's vertices.
44151     /**
44152        \param tx X-coordinate of the 3D displacement vector.
44153        \param ty Y-coordinate of the 3D displacement vector.
44154        \param tz Z-coordinate of the 3D displacement vector.
44155     **/
44156     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
44157       if (_height!=3 || _depth>1 || _spectrum>1)
44158         throw CImgInstanceException(_cimg_instance
44159                                     "shift_object3d(): Instance is not a set of 3D vertices.",
44160                                     cimg_instance);
44161 
44162       get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
44163       return *this;
44164     }
44165 
44166     //! Shift 3D object's vertices \newinstance.
44167     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
44168       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
44169     }
44170 
44171     //! Shift 3D object's vertices, so that it becomes centered.
44172     /**
44173        \note The object center is computed as its barycenter.
44174     **/
44175     CImg<T>& shift_object3d() {
44176       if (_height!=3 || _depth>1 || _spectrum>1)
44177         throw CImgInstanceException(_cimg_instance
44178                                     "shift_object3d(): Instance is not a set of 3D vertices.",
44179                                     cimg_instance);
44180 
44181       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
44182       float
44183         xm, xM = (float)xcoords.max_min(xm),
44184         ym, yM = (float)ycoords.max_min(ym),
44185         zm, zM = (float)zcoords.max_min(zm);
44186       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
44187       return *this;
44188     }
44189 
44190     //! Shift 3D object's vertices, so that it becomes centered \newinstance.
44191     CImg<Tfloat> get_shift_object3d() const {
44192       return CImg<Tfloat>(*this,false).shift_object3d();
44193     }
44194 
44195     //! Resize 3D object.
44196     /**
44197        \param sx Width of the 3D object's bounding box.
44198        \param sy Height of the 3D object's bounding box.
44199        \param sz Depth of the 3D object's bounding box.
44200     **/
44201     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
44202       if (_height!=3 || _depth>1 || _spectrum>1)
44203         throw CImgInstanceException(_cimg_instance
44204                                     "resize_object3d(): Instance is not a set of 3D vertices.",
44205                                     cimg_instance);
44206 
44207       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
44208       float
44209         xm, xM = (float)xcoords.max_min(xm),
44210         ym, yM = (float)ycoords.max_min(ym),
44211         zm, zM = (float)zcoords.max_min(zm);
44212       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
44213       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
44214       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
44215       return *this;
44216     }
44217 
44218     //! Resize 3D object \newinstance.
44219     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
44220       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
44221     }
44222 
44223     //! Resize 3D object to unit size.
44224     CImg<T> resize_object3d() {
44225       if (_height!=3 || _depth>1 || _spectrum>1)
44226         throw CImgInstanceException(_cimg_instance
44227                                     "resize_object3d(): Instance is not a set of 3D vertices.",
44228                                     cimg_instance);
44229 
44230       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
44231       float
44232         xm, xM = (float)xcoords.max_min(xm),
44233         ym, yM = (float)ycoords.max_min(ym),
44234         zm, zM = (float)zcoords.max_min(zm);
44235       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
44236       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
44237       return *this;
44238     }
44239 
44240     //! Resize 3D object to unit size \newinstance.
44241     CImg<Tfloat> get_resize_object3d() const {
44242       return CImg<Tfloat>(*this,false).resize_object3d();
44243     }
44244 
44245     //! Merge two 3D objects together.
44246     /**
44247        \param[in,out] primitives Primitives data of the current 3D object.
44248        \param obj_vertices Vertices data of the additional 3D object.
44249        \param obj_primitives Primitives data of the additional 3D object.
44250     **/
44251     template<typename tf, typename tp, typename tff>
44252     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
44253                              const CImgList<tff>& obj_primitives) {
44254       if (!obj_vertices || !obj_primitives) return *this;
44255       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
44256         throw CImgInstanceException(_cimg_instance
44257                                     "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
44258                                     "set of 3D vertices.",
44259                                     cimg_instance,
44260                                     obj_vertices._width,obj_vertices._height,
44261                                     obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
44262 
44263       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
44264       if (_height!=3 || _depth>1 || _spectrum>1)
44265         throw CImgInstanceException(_cimg_instance
44266                                     "append_object3d(): Instance is not a set of 3D vertices.",
44267                                     cimg_instance);
44268 
44269       const unsigned int P = _width;
44270       append(obj_vertices,'x');
44271       const unsigned int N = primitives._width;
44272       primitives.insert(obj_primitives);
44273       for (unsigned int i = N; i<primitives._width; ++i) {
44274         CImg<tf> &p = primitives[i];
44275         switch (p.size()) {
44276         case 1 : p[0]+=P; break; // Point
44277         case 5 : p[0]+=P; p[1]+=P; break; // Sphere
44278         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment
44279         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle
44280         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle
44281         }
44282       }
44283       return *this;
44284     }
44285 
44286     //! Texturize primitives of a 3D object.
44287     /**
44288        \param[in,out] primitives Primitives data of the 3D object.
44289        \param[in,out] colors Colors data of the 3D object.
44290        \param texture Texture image to map to 3D object.
44291        \param coords Texture-mapping coordinates.
44292     **/
44293     template<typename tp, typename tc, typename tt, typename tx>
44294     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
44295                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
44296       if (is_empty()) return *this;
44297       if (_height!=3)
44298         throw CImgInstanceException(_cimg_instance
44299                                     "texturize_object3d(): image instance is not a set of 3D points.",
44300                                     cimg_instance);
44301       if (coords && (coords._width!=_width || coords._height!=2))
44302         throw CImgArgumentException(_cimg_instance
44303                                     "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
44304                                     cimg_instance,
44305                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
44306       CImg<intT> _coords;
44307       if (!coords) { // If no texture coordinates specified, do a default XY-projection
44308         _coords.assign(_width,2);
44309         float
44310           xmin, xmax = (float)get_shared_row(0).max_min(xmin),
44311           ymin, ymax = (float)get_shared_row(1).max_min(ymin),
44312           dx = xmax>xmin?xmax-xmin:1,
44313           dy = ymax>ymin?ymax-ymin:1;
44314         cimg_forX(*this,p) {
44315           _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
44316           _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
44317         }
44318       } else _coords = coords;
44319 
44320       int texture_ind = -1;
44321       cimglist_for(primitives,l) {
44322         CImg<tp> &p = primitives[l];
44323         const unsigned int siz = p.size();
44324         switch (siz) {
44325         case 1 : { // Point
44326           const unsigned int i0 = (unsigned int)p[0];
44327           const int x0 = _coords(i0,0), y0 = _coords(i0,1);
44328           texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
44329                                 y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
44330         } break;
44331         case 2 : case 6 : { // Line
44332           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
44333           const int
44334             x0 = _coords(i0,0), y0 = _coords(i0,1),
44335             x1 = _coords(i1,0), y1 = _coords(i1,1);
44336           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
44337           else colors[l].assign(colors[texture_ind],true);
44338           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
44339         } break;
44340         case 3 : case 9 : { // Triangle
44341           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
44342           const int
44343             x0 = _coords(i0,0), y0 = _coords(i0,1),
44344             x1 = _coords(i1,0), y1 = _coords(i1,1),
44345             x2 = _coords(i2,0), y2 = _coords(i2,1);
44346           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
44347           else colors[l].assign(colors[texture_ind],true);
44348           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
44349         } break;
44350         case 4 : case 12 : { // Quadrangle
44351           const unsigned int
44352             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
44353           const int
44354             x0 = _coords(i0,0), y0 = _coords(i0,1),
44355             x1 = _coords(i1,0), y1 = _coords(i1,1),
44356             x2 = _coords(i2,0), y2 = _coords(i2,1),
44357             x3 = _coords(i3,0), y3 = _coords(i3,1);
44358           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
44359           else colors[l].assign(colors[texture_ind],true);
44360           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
44361         } break;
44362         }
44363       }
44364       return *this;
44365     }
44366 
44367     //! Generate a 3D elevation of the image instance.
44368     /**
44369        \param[out] primitives The returned list of the 3D object primitives
44370                               (template type \e tf should be at least \e unsigned \e int).
44371        \param[out] colors The returned list of the 3D object colors.
44372        \param elevation The input elevation map.
44373        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44374        \par Example
44375        \code
44376        const CImg<float> img("reference.jpg");
44377        CImgList<unsigned int> faces3d;
44378        CImgList<unsigned char> colors3d;
44379        const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
44380        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
44381        \endcode
44382        \image html ref_elevation3d.jpg
44383     **/
44384     template<typename tf, typename tc, typename te>
44385     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
44386       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
44387         throw CImgArgumentException(_cimg_instance
44388                                     "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
44389                                     "have incompatible dimensions.",
44390                                     cimg_instance,
44391                                     elevation._width,elevation._height,elevation._depth,
44392                                     elevation._spectrum,elevation._data);
44393       if (is_empty()) return *this;
44394       float m, M = (float)max_min(m);
44395       if (M==m) ++M;
44396       colors.assign();
44397       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
44398       for (unsigned int y = 0; y<size_y1; ++y)
44399         for (unsigned int x = 0; x<size_x1; ++x) {
44400           const unsigned char
44401             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
44402             g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
44403             b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
44404           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
44405         }
44406       const typename CImg<te>::_functor2d_int func(elevation);
44407       return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height);
44408     }
44409 
44410     //! Generate the 3D projection planes of the image instance.
44411     /**
44412        \param[out] primitives Primitives data of the returned 3D object.
44413        \param[out] colors Colors data of the returned 3D object.
44414        \param x0 X-coordinate of the projection point.
44415        \param y0 Y-coordinate of the projection point.
44416        \param z0 Z-coordinate of the projection point.
44417        \param normalize_colors Tells if the created textures have normalized colors.
44418     **/
44419     template<typename tf, typename tc>
44420     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
44421                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
44422                                    const bool normalize_colors=false) const {
44423       float m = 0, M = 0, delta = 1;
44424       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
44425       const unsigned int
44426         _x0 = (x0>=_width)?_width - 1:x0,
44427         _y0 = (y0>=_height)?_height - 1:y0,
44428         _z0 = (z0>=_depth)?_depth - 1:z0;
44429       CImg<tc> img_xy, img_xz, img_yz;
44430       if (normalize_colors) {
44431         ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
44432         ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
44433           move_to(img_xz);
44434         ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
44435           move_to(img_yz);
44436       } else {
44437         get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
44438         get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
44439         get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
44440       }
44441       CImg<floatT> points(12,3,1,1,
44442                           0,_width - 1,_width - 1,0,   0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
44443                           0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0,       0,_height - 1,_height - 1,0,
44444                           _z0,_z0,_z0,_z0,         0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
44445       primitives.assign();
44446       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).
44447         move_to(primitives);
44448       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).
44449         move_to(primitives);
44450       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).
44451         move_to(primitives);
44452       colors.assign();
44453       img_xy.move_to(colors);
44454       img_xz.move_to(colors);
44455       img_yz.move_to(colors);
44456       return points;
44457     }
44458 
44459     //! Generate a isoline of the image instance as a 3D object.
44460     /**
44461        \param[out] primitives The returned list of the 3D object primitives
44462                               (template type \e tf should be at least \e unsigned \e int).
44463        \param isovalue The returned list of the 3D object colors.
44464        \param size_x The number of subdivisions along the X-axis.
44465        \param size_y The number of subdisivions along the Y-axis.
44466        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44467        \par Example
44468        \code
44469        const CImg<float> img("reference.jpg");
44470        CImgList<unsigned int> faces3d;
44471        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
44472        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
44473        \endcode
44474        \image html ref_isoline3d.jpg
44475     **/
44476     template<typename tf>
44477     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
44478                                const int size_x=-100, const int size_y=-100) const {
44479       if (_spectrum>1)
44480         throw CImgInstanceException(_cimg_instance
44481                                     "get_isoline3d(): Instance is not a scalar image.",
44482                                     cimg_instance);
44483       if (_depth>1)
44484         throw CImgInstanceException(_cimg_instance
44485                                     "get_isoline3d(): Instance is not a 2D image.",
44486                                     cimg_instance);
44487       primitives.assign();
44488       if (is_empty()) return *this;
44489       CImg<floatT> vertices;
44490       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
44491         const _functor2d_int func(*this);
44492         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height());
44493       } else {
44494         const _functor2d_float func(*this);
44495         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y);
44496       }
44497       return vertices;
44498     }
44499 
44500     //! Compute isolines of a function, as a 3D object.
44501     /**
44502        \param[out] primitives Primitives data of the resulting 3D object.
44503        \param func Elevation functor. Must have <tt>operator()(x,y)</tt> defined.
44504        \param isovalue Isovalue to extract from function.
44505        \param x0 X-coordinate of the starting point.
44506        \param y0 Y-coordinate of the starting point.
44507        \param x1 X-coordinate of the ending point.
44508        \param y1 Y-coordinate of the ending point.
44509        \param size_x Resolution of the function along the X-axis.
44510        \param size_y Resolution of the function along the Y-axis.
44511        \note Use the marching squares algorithm for extracting the isolines.
44512      **/
44513     template<typename tf, typename tfunc>
44514     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
44515                                   const float x0, const float y0, const float x1, const float y1,
44516                                   const int size_x=256, const int size_y=256) {
44517       CImgList<floatT> vertices;
44518       primitives.assign();
44519       typename CImg<floatT>::_functor_isoline3d add_vertex(vertices);
44520       typename CImg<tf>::_functor_isoline3d add_segment(primitives);
44521       isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y);
44522       return vertices>'x';
44523     }
44524 
44525     //! Compute isolines of a function, as a 3D object.
44526     /**
44527        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
44528        \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment.
44529        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
44530        \param isovalue Isovalue to extract from function.
44531        \param x0 X-coordinate of the starting point.
44532        \param y0 Y-coordinate of the starting point.
44533        \param x1 X-coordinate of the ending point.
44534        \param y1 Y-coordinate of the ending point.
44535        \param size_x Resolution of the function along the X-axis.
44536        \param size_y Resolution of the function along the Y-axis.
44537        \note Use the marching squares algorithm for extracting the isolines.
44538      **/
44539     template<typename tv, typename tf, typename tfunc>
44540     static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue,
44541                           const float x0, const float y0, const float x1, const float y1,
44542                           const int size_x, const int size_y) {
44543       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
44544                                               0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
44545       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
44546                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
44547                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
44548                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
44549       const unsigned int
44550         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
44551         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
44552         nx = _nx?_nx:1,
44553         ny = _ny?_ny:1,
44554         nxm1 = nx - 1,
44555         nym1 = ny - 1;
44556 
44557       if (!nxm1 || !nym1) return;
44558       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
44559       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
44560       CImg<floatT> values1(nx), values2(nx);
44561       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
44562       int nb_vertices = 0;
44563 
44564       // Fill first line with values
44565       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
44566 
44567       // Run the marching squares algorithm
44568       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
44569         X = x0; nX = X + dx;
44570         indices2.fill(-1);
44571         values2(0) = (float)func(X,nY);
44572         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
44573 
44574           // Determine square configuration
44575           const float
44576             val0 = values1(xi),
44577             val1 = values1(nxi),
44578             val2 = values2(nxi) = (float)func(nX,nY),
44579             val3 = values2(xi);
44580           const unsigned int
44581             configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
44582             (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
44583             edge = edges[configuration];
44584 
44585           // Compute intersection vertices
44586           if (edge) {
44587             if ((edge&1) && indices1(xi,0)<0) {
44588               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
44589               indices1(xi,0) = nb_vertices++;
44590               add_vertex(Xi,Y,0.0f);
44591             }
44592             if ((edge&2) && indices1(nxi,1)<0) {
44593               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
44594               indices1(nxi,1) = nb_vertices++;
44595               add_vertex(nX,Yi,0.0f);
44596             }
44597             if ((edge&4) && indices2(xi,0)<0) {
44598               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
44599               indices2(xi,0) = nb_vertices++;
44600               add_vertex(Xi,nY,0.0f);
44601             }
44602             if ((edge&8) && indices1(xi,1)<0) {
44603               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
44604               indices1(xi,1) = nb_vertices++;
44605               add_vertex(X,Yi,0.0f);
44606             }
44607 
44608             // Create segments
44609             for (const int *segment = segments[configuration]; *segment!=-1; ) {
44610               const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
44611               const int
44612                 i0 = _isoline3d_index(p0,indices1,indices2,xi,nxi),
44613                 i1 = _isoline3d_index(p1,indices1,indices2,xi,nxi);
44614               add_segment(i0,i1);
44615             }
44616           }
44617         }
44618         values1.swap(values2);
44619         indices1.swap(indices2);
44620       }
44621     }
44622 
44623     //! Compute isolines of a function, as a 3D object \overloading.
44624     template<typename tf>
44625     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
44626                                   const float x0, const float y0, const float x1, const float y1,
44627                                   const int size_x=256, const int size_y=256) {
44628       const _functor2d_expr func(expression);
44629       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
44630     }
44631 
44632     template<typename t>
44633     static int _isoline3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
44634                                  const unsigned int x, const unsigned int nx) {
44635       switch (edge) {
44636       case 0 : return (int)indices1(x,0);
44637       case 1 : return (int)indices1(nx,1);
44638       case 2 : return (int)indices2(x,0);
44639       case 3 : return (int)indices1(x,1);
44640       }
44641       return 0;
44642     }
44643 
44644     //! Generate an isosurface of the image instance as a 3D object.
44645     /**
44646        \param[out] primitives The returned list of the 3D object primitives
44647                               (template type \e tf should be at least \e unsigned \e int).
44648        \param isovalue The returned list of the 3D object colors.
44649        \param size_x Number of subdivisions along the X-axis.
44650        \param size_y Number of subdisivions along the Y-axis.
44651        \param size_z Number of subdisivions along the Z-axis.
44652        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44653        \par Example
44654        \code
44655        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
44656        CImgList<unsigned int> faces3d;
44657        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
44658        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
44659        \endcode
44660        \image html ref_isosurface3d.jpg
44661     **/
44662     template<typename tf>
44663     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
44664                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
44665       if (_spectrum>1)
44666         throw CImgInstanceException(_cimg_instance
44667                                     "get_isosurface3d(): Instance is not a scalar image.",
44668                                     cimg_instance);
44669       primitives.assign();
44670       if (is_empty()) return *this;
44671       CImg<floatT> vertices;
44672       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
44673         const _functor3d_int func(*this);
44674         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
44675                                 width(),height(),depth());
44676       } else {
44677         const _functor3d_float func(*this);
44678         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
44679                                 size_x,size_y,size_z);
44680       }
44681       return vertices;
44682     }
44683 
44684     //! Compute isosurface of a function, as a 3D object.
44685     /**
44686        \param[out] primitives Primitives data of the resulting 3D object.
44687        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
44688        \param isovalue Isovalue to extract.
44689        \param x0 X-coordinate of the starting point.
44690        \param y0 Y-coordinate of the starting point.
44691        \param z0 Z-coordinate of the starting point.
44692        \param x1 X-coordinate of the ending point.
44693        \param y1 Y-coordinate of the ending point.
44694        \param z1 Z-coordinate of the ending point.
44695        \param size_x Resolution of the elevation function along the X-axis.
44696        \param size_y Resolution of the elevation function along the Y-axis.
44697        \param size_z Resolution of the elevation function along the Z-axis.
44698        \note Use the marching cubes algorithm for extracting the isosurface.
44699      **/
44700     template<typename tf, typename tfunc>
44701     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
44702                                      const float x0, const float y0, const float z0,
44703                                      const float x1, const float y1, const float z1,
44704                                      const int size_x=32, const int size_y=32, const int size_z=32) {
44705       CImgList<floatT> vertices;
44706       primitives.assign();
44707       typename CImg<floatT>::_functor_isosurface3d add_vertex(vertices);
44708       typename CImg<tf>::_functor_isosurface3d add_triangle(primitives);
44709       isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z);
44710       return vertices>'x';
44711     }
44712 
44713     //! Compute isosurface of a function, as a 3D object.
44714     /**
44715        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
44716        \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment.
44717        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
44718        \param isovalue Isovalue to extract.
44719        \param x0 X-coordinate of the starting point.
44720        \param y0 Y-coordinate of the starting point.
44721        \param z0 Z-coordinate of the starting point.
44722        \param x1 X-coordinate of the ending point.
44723        \param y1 Y-coordinate of the ending point.
44724        \param z1 Z-coordinate of the ending point.
44725        \param size_x Resolution of the elevation function along the X-axis.
44726        \param size_y Resolution of the elevation function along the Y-axis.
44727        \param size_z Resolution of the elevation function along the Z-axis.
44728        \note Use the marching cubes algorithm for extracting the isosurface.
44729      **/
44730     template<typename tv, typename tf, typename tfunc>
44731     static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue,
44732                              const float x0, const float y0, const float z0,
44733                              const float x1, const float y1, const float z1,
44734                              const int size_x, const int size_y, const int size_z) {
44735       static const unsigned int edges[256] = {
44736         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
44737         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
44738         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
44739         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
44740         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
44741         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
44742         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
44743         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
44744         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
44745         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
44746         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
44747         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
44748         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
44749         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
44750         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
44751         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
44752       };
44753 
44754       static const int triangles[256][16] = {
44755         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44756         { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44757         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44758         { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44759         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44760         { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44761         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44762         { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
44763         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44764         { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44765         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44766         { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
44767         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44768         { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
44769         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
44770         { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44771         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44772         { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44773         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44774         { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
44775         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44776         { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
44777         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
44778         { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
44779         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44780         { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
44781         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
44782         { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
44783         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
44784         { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
44785         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
44786         { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
44787         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44788         { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44789         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44790         { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
44791         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44792         { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
44793         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
44794         { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
44795         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44796         { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
44797         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
44798         { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
44799         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
44800         { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
44801         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
44802         { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
44803         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44804         { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
44805         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
44806         { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44807         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
44808         { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
44809         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
44810         { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
44811         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
44812         { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
44813         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
44814         { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
44815         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
44816         { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
44817         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
44818         { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44819         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44820         { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44821         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44822         { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
44823         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44824         { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
44825         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
44826         { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
44827         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44828         { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
44829         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
44830         { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
44831         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
44832         { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
44833         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
44834         { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
44835         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44836         { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
44837         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
44838         { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
44839         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
44840         { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
44841         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
44842         { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
44843         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
44844         { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
44845         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
44846         { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
44847         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
44848         { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
44849         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
44850         { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
44851         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44852         { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
44853         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
44854         { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
44855         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
44856         { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
44857         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44858         { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
44859         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
44860         { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
44861         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
44862         { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
44863         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
44864         { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
44865         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
44866         { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44867         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
44868         { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
44869         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
44870         { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
44871         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
44872         { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
44873         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
44874         { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44875         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
44876         { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
44877         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
44878         { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
44879         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
44880         { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44881         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
44882         { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44883         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44884         { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44885         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44886         { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
44887         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44888         { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
44889         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
44890         { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
44891         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44892         { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
44893         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
44894         { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
44895         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
44896         { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
44897         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
44898         { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
44899         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44900         { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
44901         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
44902         { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
44903         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
44904         { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
44905         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
44906         { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
44907         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
44908         { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44909         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
44910         { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
44911         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
44912         { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
44913         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
44914         { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44915         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44916         { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
44917         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
44918         { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
44919         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
44920         { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
44921         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
44922         { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
44923         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
44924         { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
44925         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
44926         { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
44927         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
44928         { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
44929         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
44930         { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
44931         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
44932         { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
44933         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
44934         { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
44935         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
44936         { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
44937         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
44938         { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
44939         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
44940         { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
44941         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
44942         { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44943         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
44944         { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
44945         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44946         { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44947         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44948         { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
44949         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
44950         { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
44951         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
44952         { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
44953         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
44954         { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
44955         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
44956         { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
44957         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
44958         { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
44959         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44960         { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
44961         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
44962         { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44963         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
44964         { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
44965         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
44966         { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
44967         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
44968         { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
44969         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
44970         { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44971         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
44972         { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
44973         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
44974         { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
44975         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
44976         { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44977         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
44978         { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44979         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
44980         { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
44981         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
44982         { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
44983         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
44984         { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
44985         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
44986         { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
44987         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
44988         { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
44989         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
44990         { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44991         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
44992         { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
44993         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44994         { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44995         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44996         { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
44997         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
44998         { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
44999         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
45000         { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
45001         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45002         { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45003         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
45004         { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45005         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
45006         { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45007         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45008         { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45009         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
45010         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
45011       };
45012 
45013       const unsigned int
45014         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
45015         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
45016         _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
45017         nx = _nx?_nx:1,
45018         ny = _ny?_ny:1,
45019         nz = _nz?_nz:1,
45020         nxm1 = nx - 1,
45021         nym1 = ny - 1,
45022         nzm1 = nz - 1;
45023       if (!nxm1 || !nym1 || !nzm1) return;
45024       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
45025       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
45026       CImg<floatT> values1(nx,ny), values2(nx,ny);
45027       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
45028       int nb_vertices = 0;
45029 
45030       // Fill the first plane with function values
45031       Y = y0;
45032       cimg_forY(values1,y) {
45033         X = x0;
45034         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
45035         Y+=dy;
45036       }
45037 
45038       // Run Marching Cubes algorithm
45039       Z = z0; nZ = Z + dz;
45040       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
45041         Y = y0; nY = Y + dy;
45042         indices2.fill(-1);
45043         X = x0; for (unsigned int xi = 0; xi<nx; ++xi) { values2(xi,0) = (float)func(X,Y,nZ); X += dx; }
45044 
45045         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
45046           X = x0; nX = X + dx;
45047           values2(0,nyi) = (float)func(X,nY,nZ);
45048 
45049           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
45050 
45051             // Determine cube configuration
45052             const float
45053               val0 = values1(xi,yi),
45054               val1 = values1(nxi,yi),
45055               val2 = values1(nxi,nyi),
45056               val3 = values1(xi,nyi),
45057               val4 = values2(xi,yi),
45058               val5 = values2(nxi,yi),
45059               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
45060               val7 = values2(xi,nyi);
45061 
45062             const unsigned int configuration =
45063               (val0<isovalue?1U:0U)  | (val1<isovalue?2U:0U)  | (val2<isovalue?4U:0U)  | (val3<isovalue?8U:0U) |
45064               (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
45065               edge = edges[configuration];
45066 
45067             // Compute intersection vertices
45068             if (edge) {
45069               if ((edge&1) && indices1(xi,yi,0)<0) {
45070                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
45071                 indices1(xi,yi,0) = nb_vertices++;
45072                 add_vertex(Xi,Y,Z);
45073               }
45074               if ((edge&2) && indices1(nxi,yi,1)<0) {
45075                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
45076                 indices1(nxi,yi,1) = nb_vertices++;
45077                 add_vertex(nX,Yi,Z);
45078               }
45079               if ((edge&4) && indices1(xi,nyi,0)<0) {
45080                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
45081                 indices1(xi,nyi,0) = nb_vertices++;
45082                 add_vertex(Xi,nY,Z);
45083               }
45084               if ((edge&8) && indices1(xi,yi,1)<0) {
45085                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
45086                 indices1(xi,yi,1) = nb_vertices++;
45087                 add_vertex(X,Yi,Z);
45088               }
45089               if ((edge&16) && indices2(xi,yi,0)<0) {
45090                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
45091                 indices2(xi,yi,0) = nb_vertices++;
45092                 add_vertex(Xi,Y,nZ);
45093               }
45094               if ((edge&32) && indices2(nxi,yi,1)<0) {
45095                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
45096                 indices2(nxi,yi,1) = nb_vertices++;
45097                 add_vertex(nX,Yi,nZ);
45098               }
45099               if ((edge&64) && indices2(xi,nyi,0)<0) {
45100                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
45101                 indices2(xi,nyi,0) = nb_vertices++;
45102                 add_vertex(Xi,nY,nZ);
45103               }
45104               if ((edge&128) && indices2(xi,yi,1)<0)  {
45105                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
45106                 indices2(xi,yi,1) = nb_vertices++;
45107                 add_vertex(X,Yi,nZ);
45108               }
45109               if ((edge&256) && indices1(xi,yi,2)<0) {
45110                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
45111                 indices1(xi,yi,2) = nb_vertices++;
45112                 add_vertex(X,Y,Zi);
45113               }
45114               if ((edge&512) && indices1(nxi,yi,2)<0)  {
45115                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
45116                 indices1(nxi,yi,2) = nb_vertices++;
45117                 add_vertex(nX,Y,Zi);
45118               }
45119               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
45120                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
45121                 indices1(nxi,nyi,2) = nb_vertices++;
45122                 add_vertex(nX,nY,Zi);
45123               }
45124               if ((edge&2048) && indices1(xi,nyi,2)<0) {
45125                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
45126                 indices1(xi,nyi,2) = nb_vertices++;
45127                 add_vertex(X,nY,Zi);
45128               }
45129 
45130               // Create triangles
45131               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
45132                 const unsigned int
45133                   p0 = (unsigned int)*(triangle++),
45134                   p1 = (unsigned int)*(triangle++),
45135                   p2 = (unsigned int)*(triangle++);
45136                 const int
45137                   i0 = _isosurface3d_index(p0,indices1,indices2,xi,yi,nxi,nyi),
45138                   i1 = _isosurface3d_index(p1,indices1,indices2,xi,yi,nxi,nyi),
45139                   i2 = _isosurface3d_index(p2,indices1,indices2,xi,yi,nxi,nyi);
45140                 add_triangle(i0,i2,i1);
45141               }
45142             }
45143           }
45144         }
45145         cimg::swap(values1,values2);
45146         cimg::swap(indices1,indices2);
45147       }
45148     }
45149 
45150     //! Compute isosurface of a function, as a 3D object \overloading.
45151     template<typename tf>
45152     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
45153                                      const float x0, const float y0, const float z0,
45154                                      const float x1, const float y1, const float z1,
45155                                      const int dx=32, const int dy=32, const int dz=32) {
45156       const _functor3d_expr func(expression);
45157       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
45158     }
45159 
45160     template<typename t>
45161     static int _isosurface3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
45162                                     const unsigned int x, const unsigned int y,
45163                                     const unsigned int nx, const unsigned int ny) {
45164       switch (edge) {
45165       case 0 : return indices1(x,y,0);
45166       case 1 : return indices1(nx,y,1);
45167       case 2 : return indices1(x,ny,0);
45168       case 3 : return indices1(x,y,1);
45169       case 4 : return indices2(x,y,0);
45170       case 5 : return indices2(nx,y,1);
45171       case 6 : return indices2(x,ny,0);
45172       case 7 : return indices2(x,y,1);
45173       case 8 : return indices1(x,y,2);
45174       case 9 : return indices1(nx,y,2);
45175       case 10 : return indices1(nx,ny,2);
45176       case 11 : return indices1(x,ny,2);
45177       }
45178       return 0;
45179     }
45180 
45181     // Define functors for accessing image values (used in previous functions).
45182     struct _functor2d_int {
45183       const CImg<T>& ref;
45184       _functor2d_int(const CImg<T>& pref):ref(pref) {}
45185       float operator()(const float x, const float y) const {
45186         return (float)ref((int)x,(int)y);
45187       }
45188     };
45189 
45190     struct _functor2d_float {
45191       const CImg<T>& ref;
45192       _functor2d_float(const CImg<T>& pref):ref(pref) {}
45193       float operator()(const float x, const float y) const {
45194         return (float)ref._linear_atXY(x,y);
45195       }
45196     };
45197 
45198     struct _functor2d_expr {
45199       _cimg_math_parser *mp;
45200       ~_functor2d_expr() { mp->end(); delete mp; }
45201       _functor2d_expr(const char *const expr):mp(0) {
45202         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
45203       }
45204       float operator()(const float x, const float y) const {
45205         return (float)(*mp)(x,y,0,0);
45206       }
45207     };
45208 
45209     struct _functor3d_int {
45210       const CImg<T>& ref;
45211       _functor3d_int(const CImg<T>& pref):ref(pref) {}
45212       float operator()(const float x, const float y, const float z) const {
45213         return (float)ref((int)x,(int)y,(int)z);
45214       }
45215     };
45216 
45217     struct _functor3d_float {
45218       const CImg<T>& ref;
45219       _functor3d_float(const CImg<T>& pref):ref(pref) {}
45220       float operator()(const float x, const float y, const float z) const {
45221         return (float)ref._linear_atXYZ(x,y,z);
45222       }
45223     };
45224 
45225     struct _functor3d_expr {
45226       _cimg_math_parser *mp;
45227       ~_functor3d_expr() { mp->end(); delete mp; }
45228       _functor3d_expr(const char *const expr):mp(0) {
45229         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
45230       }
45231       float operator()(const float x, const float y, const float z) const {
45232         return (float)(*mp)(x,y,z,0);
45233       }
45234     };
45235 
45236     struct _functor4d_int {
45237       const CImg<T>& ref;
45238       _functor4d_int(const CImg<T>& pref):ref(pref) {}
45239       float operator()(const float x, const float y, const float z, const unsigned int c) const {
45240         return (float)ref((int)x,(int)y,(int)z,c);
45241       }
45242     };
45243 
45244     struct _functor_isoline3d {
45245       CImgList<T>& list;
45246       _functor_isoline3d(CImgList<T>& _list):list(_list) {}
45247       template<typename t>
45248       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
45249       template<typename t>
45250       void operator()(const t i, const t j) { CImg<T>::vector((T)i,(T)j).move_to(list); }
45251     };
45252 
45253     struct _functor_isosurface3d {
45254       CImgList<T>& list;
45255       _functor_isosurface3d(CImgList<T>& _list):list(_list) {}
45256       template<typename t>
45257       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
45258     };
45259 
45260     //! Compute 3D elevation of a function as a 3D object.
45261     /**
45262        \param[out] primitives Primitives data of the resulting 3D object.
45263        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
45264        \param x0 X-coordinate of the starting point.
45265        \param y0 Y-coordinate of the starting point.
45266        \param x1 X-coordinate of the ending point.
45267        \param y1 Y-coordinate of the ending point.
45268        \param size_x Resolution of the function along the X-axis.
45269        \param size_y Resolution of the function along the Y-axis.
45270     **/
45271     template<typename tf, typename tfunc>
45272     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
45273                                     const float x0, const float y0, const float x1, const float y1,
45274                                     const int size_x=256, const int size_y=256) {
45275       const float
45276         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
45277         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
45278       const unsigned int
45279         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
45280         nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
45281         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
45282         nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
45283       if (nsize_x<2 || nsize_y<2)
45284         throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
45285                                     pixel_type(),
45286                                     nsize_x,nsize_y);
45287 
45288       CImg<floatT> vertices(nsize_x*nsize_y,3);
45289       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
45290       for (unsigned int y = 0; y<nsize_y; ++y) {
45291         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
45292         for (unsigned int x = 0; x<nsize_x; ++x) {
45293           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
45294           *(ptr_x++) = (float)x;
45295           *(ptr_y++) = (float)y;
45296           *(ptr_z++) = (float)func(X,Y);
45297         }
45298       }
45299       primitives.assign(nsize_x1*nsize_y1,1,4);
45300       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
45301         const unsigned int yw = y*nsize_x;
45302         for (unsigned int x = 0; x<nsize_x1; ++x) {
45303           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
45304           primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
45305         }
45306       }
45307       return vertices;
45308     }
45309 
45310     //! Compute 3D elevation of a function, as a 3D object \overloading.
45311     template<typename tf>
45312     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
45313                                     const float x0, const float y0, const float x1, const float y1,
45314                                     const int size_x=256, const int size_y=256) {
45315       const _functor2d_expr func(expression);
45316       return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
45317     }
45318 
45319     //! Generate a 3D box object.
45320     /**
45321        \param[out] primitives The returned list of the 3D object primitives
45322                               (template type \e tf should be at least \e unsigned \e int).
45323        \param size_x The width of the box (dimension along the X-axis).
45324        \param size_y The height of the box (dimension along the Y-axis).
45325        \param size_z The depth of the box (dimension along the Z-axis).
45326        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45327        \par Example
45328        \code
45329        CImgList<unsigned int> faces3d;
45330        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
45331        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
45332        \endcode
45333        \image html ref_box3d.jpg
45334     **/
45335     template<typename tf>
45336     static CImg<floatT> box3d(CImgList<tf>& primitives,
45337                               const float size_x=200, const float size_y=100, const float size_z=100) {
45338       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);
45339       return CImg<floatT>(8,3,1,1,
45340                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
45341                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
45342                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
45343     }
45344 
45345     //! Generate a 3D cone.
45346     /**
45347        \param[out] primitives The returned list of the 3D object primitives
45348                               (template type \e tf should be at least \e unsigned \e int).
45349        \param radius The radius of the cone basis.
45350        \param size_z The cone's height.
45351        \param subdivisions The number of basis angular subdivisions.
45352        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45353        \par Example
45354        \code
45355        CImgList<unsigned int> faces3d;
45356        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
45357        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
45358        \endcode
45359        \image html ref_cone3d.jpg
45360     **/
45361     template<typename tf>
45362     static CImg<floatT> cone3d(CImgList<tf>& primitives,
45363                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
45364       primitives.assign();
45365       if (!subdivisions) return CImg<floatT>();
45366       CImgList<floatT> vertices(2,1,3,1,1,
45367                                 0.,0.,size_z,
45368                                 0.,0.,0.);
45369       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
45370         const float a = (float)(angle*cimg::PI/180);
45371         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
45372       }
45373       const unsigned int nbr = vertices._width - 2;
45374       for (unsigned int p = 0; p<nbr; ++p) {
45375         const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
45376         CImg<tf>::vector(1,next,curr).move_to(primitives);
45377         CImg<tf>::vector(0,curr,next).move_to(primitives);
45378       }
45379       return vertices>'x';
45380     }
45381 
45382     //! Generate a 3D cylinder.
45383     /**
45384        \param[out] primitives The returned list of the 3D object primitives
45385                               (template type \e tf should be at least \e unsigned \e int).
45386        \param radius The radius of the cylinder basis.
45387        \param size_z The cylinder's height.
45388        \param subdivisions The number of basis angular subdivisions.
45389        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45390        \par Example
45391        \code
45392        CImgList<unsigned int> faces3d;
45393        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
45394        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
45395        \endcode
45396        \image html ref_cylinder3d.jpg
45397     **/
45398     template<typename tf>
45399     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
45400                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
45401       primitives.assign();
45402       if (!subdivisions) return CImg<floatT>();
45403       CImgList<floatT> vertices(2,1,3,1,1,
45404                                 0.,0.,0.,
45405                                 0.,0.,size_z);
45406       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
45407         const float a = (float)(angle*cimg::PI/180);
45408         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices);
45409         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
45410       }
45411       const unsigned int nbr = (vertices._width - 2)/2;
45412       for (unsigned int p = 0; p<nbr; ++p) {
45413         const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
45414         CImg<tf>::vector(0,next,curr).move_to(primitives);
45415         CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
45416         CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
45417       }
45418       return vertices>'x';
45419     }
45420 
45421     //! Generate a 3D torus.
45422     /**
45423        \param[out] primitives The returned list of the 3D object primitives
45424                               (template type \e tf should be at least \e unsigned \e int).
45425        \param radius1 The large radius.
45426        \param radius2 The small radius.
45427        \param subdivisions1 The number of angular subdivisions for the large radius.
45428        \param subdivisions2 The number of angular subdivisions for the small radius.
45429        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45430        \par Example
45431        \code
45432        CImgList<unsigned int> faces3d;
45433        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
45434        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
45435        \endcode
45436        \image html ref_torus3d.jpg
45437     **/
45438     template<typename tf>
45439     static CImg<floatT> torus3d(CImgList<tf>& primitives,
45440                                 const float radius1=100, const float radius2=30,
45441                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
45442       primitives.assign();
45443       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
45444       CImgList<floatT> vertices;
45445       for (unsigned int v = 0; v<subdivisions1; ++v) {
45446         const float
45447           beta = (float)(v*2*cimg::PI/subdivisions1),
45448           xc = radius1*(float)std::cos(beta),
45449           yc = radius1*(float)std::sin(beta);
45450         for (unsigned int u = 0; u<subdivisions2; ++u) {
45451           const float
45452             alpha = (float)(u*2*cimg::PI/subdivisions2),
45453             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
45454             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
45455             z = radius2*(float)std::sin(alpha);
45456           CImg<floatT>::vector(x,y,z).move_to(vertices);
45457         }
45458       }
45459       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
45460         const unsigned int nv = (vv + 1)%subdivisions1;
45461         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
45462           const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
45463           CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
45464         }
45465       }
45466       return vertices>'x';
45467     }
45468 
45469     //! Generate a 3D XY-plane.
45470     /**
45471        \param[out] primitives The returned list of the 3D object primitives
45472                               (template type \e tf should be at least \e unsigned \e int).
45473        \param size_x The width of the plane (dimension along the X-axis).
45474        \param size_y The height of the plane (dimensions along the Y-axis).
45475        \param subdivisions_x The number of planar subdivisions along the X-axis.
45476        \param subdivisions_y The number of planar subdivisions along the Y-axis.
45477        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45478        \par Example
45479        \code
45480        CImgList<unsigned int> faces3d;
45481        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
45482        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
45483        \endcode
45484        \image html ref_plane3d.jpg
45485     **/
45486     template<typename tf>
45487     static CImg<floatT> plane3d(CImgList<tf>& primitives,
45488                                 const float size_x=100, const float size_y=100,
45489                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
45490       primitives.assign();
45491       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
45492       CImgList<floatT> vertices;
45493       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
45494       const float fx = (float)size_x/w, fy = (float)size_y/h;
45495       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
45496         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
45497       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
45498         const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
45499         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
45500       }
45501       return vertices>'x';
45502     }
45503 
45504     //! Generate a 3D sphere.
45505     /**
45506        \param[out] primitives The returned list of the 3D object primitives
45507                               (template type \e tf should be at least \e unsigned \e int).
45508        \param radius The radius of the sphere (dimension along the X-axis).
45509        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
45510        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45511        \par Example
45512        \code
45513        CImgList<unsigned int> faces3d;
45514        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
45515        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
45516        \endcode
45517        \image html ref_sphere3d.jpg
45518     **/
45519     template<typename tf>
45520     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
45521                                  const float radius=50, const unsigned int subdivisions=3) {
45522 
45523       // Create initial icosahedron
45524       primitives.assign();
45525       const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a;
45526       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,
45527                                 -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a);
45528       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,
45529                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
45530                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
45531       // edge - length/2
45532       float he = (float)a;
45533 
45534       // Recurse subdivisions
45535       for (unsigned int i = 0; i<subdivisions; ++i) {
45536         const unsigned int L = primitives._width;
45537         he/=2;
45538         const float he2 = he*he;
45539         for (unsigned int l = 0; l<L; ++l) {
45540           const unsigned int
45541             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
45542           const float
45543             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
45544             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
45545             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
45546             tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
45547             nn0 = cimg::hypot(tnx0,tny0,tnz0),
45548             tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
45549             nn1 = cimg::hypot(tnx1,tny1,tnz1),
45550             tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
45551             nn2 = cimg::hypot(tnx2,tny2,tnz2),
45552             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
45553             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
45554             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
45555           int i0 = -1, i1 = -1, i2 = -1;
45556           cimglist_for(vertices,p) {
45557             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
45558             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
45559             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
45560             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
45561           }
45562           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
45563           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
45564           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
45565           primitives.remove(0);
45566           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
45567           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
45568           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
45569           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
45570         }
45571       }
45572       return (vertices>'x')*=radius;
45573     }
45574 
45575     //! Generate a 3D ellipsoid.
45576     /**
45577        \param[out] primitives The returned list of the 3D object primitives
45578                               (template type \e tf should be at least \e unsigned \e int).
45579        \param tensor The tensor which gives the shape and size of the ellipsoid.
45580        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
45581        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
45582        \par Example
45583        \code
45584        CImgList<unsigned int> faces3d;
45585        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
45586                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
45587        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
45588        \endcode
45589        \image html ref_ellipsoid3d.jpg
45590     **/
45591     template<typename tf, typename t>
45592     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
45593                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
45594       primitives.assign();
45595       if (!subdivisions) return CImg<floatT>();
45596       CImg<floatT> S, V;
45597       tensor.symmetric_eigen(S,V);
45598       const float orient =
45599         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
45600         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
45601         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
45602       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
45603       const float l0 = S[0], l1 = S[1], l2 = S[2];
45604       CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions);
45605       vertices.get_shared_row(0)*=l0;
45606       vertices.get_shared_row(1)*=l1;
45607       vertices.get_shared_row(2)*=l2;
45608       return V*vertices;
45609     }
45610 
45611     //! Convert 3D object into a CImg3d representation.
45612     /**
45613        \param primitives Primitives data of the 3D object.
45614        \param colors Colors data of the 3D object.
45615        \param opacities Opacities data of the 3D object.
45616        \param full_check Tells if full checking of the 3D object must be performed.
45617     **/
45618     template<typename tp, typename tc, typename to>
45619     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45620                               const CImgList<tc>& colors,
45621                               const to& opacities,
45622                               const bool full_check=true) {
45623       return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
45624     }
45625 
45626     //! Convert 3D object into a CImg3d representation \overloading.
45627     template<typename tp, typename tc>
45628     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45629                               const CImgList<tc>& colors,
45630                               const bool full_check=true) {
45631       return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
45632     }
45633 
45634     //! Convert 3D object into a CImg3d representation \overloading.
45635     template<typename tp>
45636     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
45637                               const bool full_check=true) {
45638       return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
45639     }
45640 
45641     //! Convert 3D object into a CImg3d representation \overloading.
45642     CImg<T>& object3dtoCImg3d(const bool full_check=true) {
45643       return get_object3dtoCImg3d(full_check).move_to(*this);
45644     }
45645 
45646     //! Convert 3D object into a CImg3d representation \newinstance.
45647     template<typename tp, typename tc, typename to>
45648     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45649                                       const CImgList<tc>& colors,
45650                                       const to& opacities,
45651                                       const bool full_check=true) const {
45652       CImg<charT> error_message(1024);
45653       if (!is_object3d(primitives,colors,opacities,full_check,error_message))
45654         throw CImgInstanceException(_cimg_instance
45655                                     "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).",
45656                                     cimg_instance,_width,primitives._width,error_message.data());
45657       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
45658       float *ptrd = res._data;
45659 
45660       // Put magick number.
45661       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
45662       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
45663 
45664       // Put number of vertices and primitives.
45665       *(ptrd++) = cimg::uint2float(_width);
45666       *(ptrd++) = cimg::uint2float(primitives._width);
45667 
45668       // Put vertex data.
45669       if (is_empty() || !primitives) return res;
45670       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
45671       cimg_forX(*this,p) {
45672         *(ptrd++) = (float)*(ptrx++);
45673         *(ptrd++) = (float)*(ptry++);
45674         *(ptrd++) = (float)*(ptrz++);
45675       }
45676 
45677       // Put primitive data.
45678       cimglist_for(primitives,p) {
45679         *(ptrd++) = (float)primitives[p].size();
45680         const tp *ptrp = primitives[p]._data;
45681         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
45682       }
45683 
45684       // Put color/texture data.
45685       const unsigned int csiz = std::min(colors._width,primitives._width);
45686       for (int c = 0; c<(int)csiz; ++c) {
45687         const CImg<tc>& color = colors[c];
45688         const tc *ptrc = color._data;
45689         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
45690         else {
45691           *(ptrd++) = -128.f;
45692           int shared_ind = -1;
45693           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
45694           if (shared_ind<0) {
45695             *(ptrd++) = (float)color._width;
45696             *(ptrd++) = (float)color._height;
45697             *(ptrd++) = (float)color._spectrum;
45698             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
45699           } else {
45700             *(ptrd++) = (float)shared_ind;
45701             *(ptrd++) = 0;
45702             *(ptrd++) = 0;
45703           }
45704         }
45705       }
45706       const int csiz2 = primitives.width() - colors.width();
45707       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; }
45708 
45709       // Put opacity data.
45710       ptrd = _object3dtoCImg3d(opacities,ptrd);
45711       const float *ptre = res.end();
45712       while (ptrd<ptre) *(ptrd++) = 1.f;
45713       return res;
45714     }
45715 
45716     template<typename to>
45717     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
45718       cimglist_for(opacities,o) {
45719         const CImg<to>& opacity = opacities[o];
45720         const to *ptro = opacity._data;
45721         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
45722         else {
45723           *(ptrd++) = -128.f;
45724           int shared_ind = -1;
45725           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
45726           if (shared_ind<0) {
45727             *(ptrd++) = (float)opacity._width;
45728             *(ptrd++) = (float)opacity._height;
45729             *(ptrd++) = (float)opacity._spectrum;
45730             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
45731           } else {
45732             *(ptrd++) = (float)shared_ind;
45733             *(ptrd++) = 0;
45734             *(ptrd++) = 0;
45735           }
45736         }
45737       }
45738       return ptrd;
45739     }
45740 
45741     template<typename to>
45742     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
45743       const to *ptro = opacities._data;
45744       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
45745       return ptrd;
45746     }
45747 
45748     template<typename tp, typename tc, typename to>
45749     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
45750                                         const CImgList<tc>& colors,
45751                                         const CImgList<to>& opacities) const {
45752       unsigned int siz = 8U + 3*_width;
45753       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
45754       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
45755         if (colors[c].is_shared()) siz+=4;
45756         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
45757       }
45758       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
45759       cimglist_for(opacities,o) {
45760         if (opacities[o].is_shared()) siz+=4;
45761         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
45762       }
45763       siz+=primitives._width - opacities._width;
45764       return siz;
45765     }
45766 
45767     template<typename tp, typename tc, typename to>
45768     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
45769                                         const CImgList<tc>& colors,
45770                                         const CImg<to>& opacities) const {
45771       unsigned int siz = 8U + 3*_width;
45772       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
45773       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
45774         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
45775       }
45776       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
45777       siz+=primitives.size();
45778       cimg::unused(opacities);
45779       return siz;
45780     }
45781 
45782     //! Convert 3D object into a CImg3d representation \overloading.
45783     template<typename tp, typename tc>
45784     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45785                                       const CImgList<tc>& colors,
45786                                       const bool full_check=true) const {
45787       CImgList<T> opacities;
45788       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45789     }
45790 
45791     //! Convert 3D object into a CImg3d representation \overloading.
45792     template<typename tp>
45793     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
45794                                       const bool full_check=true) const {
45795       CImgList<T> colors, opacities;
45796       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45797     }
45798 
45799     //! Convert 3D object into a CImg3d representation \overloading.
45800     CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
45801       CImgList<T> opacities, colors;
45802       CImgList<uintT> primitives(width(),1,1,1,1);
45803       cimglist_for(primitives,p) primitives(p,0) = p;
45804       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
45805     }
45806 
45807     //! Convert CImg3d representation into a 3D object.
45808     /**
45809        \param[out] primitives Primitives data of the 3D object.
45810        \param[out] colors Colors data of the 3D object.
45811        \param[out] opacities Opacities data of the 3D object.
45812        \param full_check Tells if full checking of the 3D object must be performed.
45813     **/
45814     template<typename tp, typename tc, typename to>
45815     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
45816                               CImgList<tc>& colors,
45817                               CImgList<to>& opacities,
45818                               const bool full_check=true) {
45819       return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
45820     }
45821 
45822     //! Convert CImg3d representation into a 3D object \newinstance.
45823     template<typename tp, typename tc, typename to>
45824     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
45825                                  CImgList<tc>& colors,
45826                                  CImgList<to>& opacities,
45827                                  const bool full_check=true) const {
45828       CImg<charT> error_message(1024);
45829       if (!is_CImg3d(full_check,error_message))
45830         throw CImgInstanceException(_cimg_instance
45831                                     "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
45832                                     cimg_instance,error_message.data());
45833       const T *ptrs = _data + 6;
45834       const unsigned int
45835         nb_points = cimg::float2uint((float)*(ptrs++)),
45836         nb_primitives = cimg::float2uint((float)*(ptrs++));
45837       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
45838       ptrs+=3*nb_points;
45839       primitives.assign(nb_primitives);
45840       cimglist_for(primitives,p) {
45841         const unsigned int nb_inds = (unsigned int)*(ptrs++);
45842         primitives[p].assign(1,nb_inds);
45843         tp *ptrp = primitives[p]._data;
45844         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
45845       }
45846       colors.assign(nb_primitives);
45847       cimglist_for(colors,c) {
45848         if (*ptrs==(T)-128) {
45849           ++ptrs;
45850           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
45851           if (!h && !s) colors[c].assign(colors[w],true);
45852           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
45853         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
45854       }
45855       opacities.assign(nb_primitives);
45856       cimglist_for(opacities,o) {
45857         if (*ptrs==(T)-128) {
45858           ++ptrs;
45859           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
45860           if (!h && !s) opacities[o].assign(opacities[w],true);
45861           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
45862         } else opacities[o].assign(1,1,1,1,*(ptrs++));
45863       }
45864       return points;
45865     }
45866 
45867     //@}
45868     //---------------------------
45869     //
45870     //! \name Drawing Functions
45871     //@{
45872     //---------------------------
45873 
45874 #define cimg_init_scanline(opacity) \
45875     static const T _sc_maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); \
45876     const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \
45877     const ulongT _sc_whd = (ulongT)_width*_height*_depth; \
45878     cimg::unused(_sc_maxval);
45879 
45880 #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
45881     _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval)
45882 
45883     // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
45884     // used only for internal purpose.
45885     // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
45886     template<typename tc>
45887     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
45888                             const tc *const color, const float opacity,
45889                             const float brightness,
45890                             const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) {
45891       const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
45892       if (dx>=0) {
45893         const tc *col = color;
45894         const ulongT off = whd - dx - 1;
45895         T *ptrd = data(nx0,y);
45896         if (opacity>=1) { // ** Opaque drawing **
45897           if (brightness==1) { // Brightness==1
45898             if (sizeof(T)!=1) cimg_forC(*this,c) {
45899                 const T val = (T)*(col++);
45900                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45901                 ptrd+=off;
45902               } else cimg_forC(*this,c) {
45903                 const T val = (T)*(col++);
45904                 std::memset(ptrd,(int)val,dx + 1);
45905                 ptrd+=whd;
45906               }
45907           } else if (brightness<1) { // Brightness<1
45908             if (sizeof(T)!=1) cimg_forC(*this,c) {
45909                 const T val = (T)(*(col++)*brightness);
45910                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45911                 ptrd+=off;
45912               } else cimg_forC(*this,c) {
45913                 const T val = (T)(*(col++)*brightness);
45914                 std::memset(ptrd,(int)val,dx + 1);
45915                 ptrd+=whd;
45916               }
45917           } else { // Brightness>1
45918             if (sizeof(T)!=1) cimg_forC(*this,c) {
45919                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
45920                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
45921                 ptrd+=off;
45922               } else cimg_forC(*this,c) {
45923                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
45924                 std::memset(ptrd,(int)val,dx + 1);
45925                 ptrd+=whd;
45926               }
45927           }
45928         } else { // ** Transparent drawing **
45929           if (brightness==1) { // Brightness==1
45930             cimg_forC(*this,c) {
45931               const Tfloat val = *(col++)*nopacity;
45932               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45933               ptrd+=off;
45934             }
45935           } else if (brightness<=1) { // Brightness<1
45936             cimg_forC(*this,c) {
45937               const Tfloat val = *(col++)*brightness*nopacity;
45938               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45939               ptrd+=off;
45940             }
45941           } else { // Brightness>1
45942             cimg_forC(*this,c) {
45943               const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity;
45944               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
45945               ptrd+=off;
45946             }
45947           }
45948         }
45949       }
45950       return *this;
45951     }
45952 
45953     //! Draw a 3D point.
45954     /**
45955        \param x0 X-coordinate of the point.
45956        \param y0 Y-coordinate of the point.
45957        \param z0 Z-coordinate of the point.
45958        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45959        \param opacity Drawing opacity.
45960        \note
45961        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
45962        \par Example:
45963        \code
45964        CImg<unsigned char> img(100,100,1,3,0);
45965        const unsigned char color[] = { 255,128,64 };
45966        img.draw_point(50,50,color);
45967        \endcode
45968     **/
45969     template<typename tc>
45970     CImg<T>& draw_point(const int x0, const int y0, const int z0,
45971                         const tc *const color, const float opacity=1) {
45972       if (is_empty()) return *this;
45973       if (!color)
45974         throw CImgArgumentException(_cimg_instance
45975                                     "draw_point(): Specified color is (null).",
45976                                     cimg_instance);
45977       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
45978         const ulongT whd = (ulongT)_width*_height*_depth;
45979         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
45980         T *ptrd = data(x0,y0,z0,0);
45981         const tc *col = color;
45982         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
45983         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
45984       }
45985       return *this;
45986     }
45987 
45988     //! Draw a 2D point \simplification.
45989     template<typename tc>
45990     CImg<T>& draw_point(const int x0, const int y0,
45991                         const tc *const color, const float opacity=1) {
45992       return draw_point(x0,y0,0,color,opacity);
45993     }
45994 
45995     // Draw a points cloud.
45996     /**
45997        \param points Image of vertices coordinates.
45998        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45999        \param opacity Drawing opacity.
46000     **/
46001     template<typename t, typename tc>
46002     CImg<T>& draw_point(const CImg<t>& points,
46003                         const tc *const color, const float opacity=1) {
46004       if (is_empty() || !points) return *this;
46005       switch (points._height) {
46006       case 0 : case 1 :
46007         throw CImgArgumentException(_cimg_instance
46008                                     "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
46009                                     cimg_instance,
46010                                     points._width,points._height,points._depth,points._spectrum,points._data);
46011       case 2 : {
46012         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
46013       } break;
46014       default : {
46015         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
46016       }
46017       }
46018       return *this;
46019     }
46020 
46021     //! Draw a 2D line.
46022     /**
46023        \param x0 X-coordinate of the starting line point.
46024        \param y0 Y-coordinate of the starting line point.
46025        \param x1 X-coordinate of the ending line point.
46026        \param y1 Y-coordinate of the ending line point.
46027        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46028        \param opacity Drawing opacity.
46029        \param pattern An integer whose bits describe the line pattern.
46030        \param init_hatch Tells if a reinitialization of the hash state must be done.
46031        \note
46032        - Line routine uses Bresenham's algorithm.
46033        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
46034        \par Example:
46035        \code
46036        CImg<unsigned char> img(100,100,1,3,0);
46037        const unsigned char color[] = { 255,128,64 };
46038         img.draw_line(40,40,80,70,color);
46039        \endcode
46040     **/
46041     template<typename tc>
46042     CImg<T>& draw_line(int x0, int y0,
46043                        int x1, int y1,
46044                        const tc *const color, const float opacity=1,
46045                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46046       if (is_empty() || !opacity || !pattern ||
46047           std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
46048           std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
46049 
46050       int
46051         w1 = width() - 1, h1 = height() - 1,
46052         dx01 = x1 - x0, dy01 = y1 - y0;
46053 
46054       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
46055       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
46056       if (pattern==~0U && y0>y1) {
46057         cimg::swap(x0,x1,y0,y1);
46058         dx01*=-1; dy01*=-1;
46059       }
46060 
46061       static unsigned int hatch = ~0U - (~0U>>1);
46062       if (init_hatch) hatch = ~0U - (~0U>>1);
46063       cimg_init_scanline(opacity);
46064       const int
46065         step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2,
46066         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
46067       dy01+=dy01?0:1;
46068 
46069       for (int y = cy0; y!=cy1; y+=step) {
46070         const int
46071           yy0 = y - y0,
46072           x = x0 + (dx01*yy0 + hdy01)/dy01;
46073         if (x>=0 && x<=w1 && pattern&hatch) {
46074           T *const ptrd = is_horizontal?data(y,x):data(x,y);
46075           cimg_forC(*this,c) {
46076             const T val = color[c];
46077             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46078           }
46079         }
46080         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
46081       }
46082       return *this;
46083     }
46084 
46085     //! Draw a 2D line, with z-buffering.
46086     /**
46087        \param zbuffer Zbuffer image.
46088        \param x0 X-coordinate of the starting point.
46089        \param y0 Y-coordinate of the starting point.
46090        \param z0 Z-coordinate of the starting point
46091        \param x1 X-coordinate of the ending point.
46092        \param y1 Y-coordinate of the ending point.
46093        \param z1 Z-coordinate of the ending point.
46094        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46095        \param opacity Drawing opacity.
46096        \param pattern An integer whose bits describe the line pattern.
46097        \param init_hatch Tells if a reinitialization of the hash state must be done.
46098     **/
46099     template<typename tz,typename tc>
46100     CImg<T>& draw_line(CImg<tz>& zbuffer,
46101                        int x0, int y0, const float z0,
46102                        int x1, int y1, const float z1,
46103                        const tc *const color, const float opacity=1,
46104                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46105       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
46106       if (!color)
46107         throw CImgArgumentException(_cimg_instance
46108                                     "draw_line(): Specified color is (null).",
46109                                     cimg_instance);
46110       if (!is_sameXY(zbuffer))
46111         throw CImgArgumentException(_cimg_instance
46112                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46113                                     "different dimensions.",
46114                                     cimg_instance,
46115                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46116 
46117       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
46118 
46119       float iz0 = 1/z0, iz1 = 1/z1;
46120       int
46121         w1 = width() - 1, h1 = height() - 1,
46122         dx01 = x1 - x0, dy01 = y1 - y0;
46123       float diz01 = iz1 - iz0;
46124 
46125       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
46126       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
46127       if (pattern==~0U && y0>y1) {
46128         cimg::swap(x0,x1,y0,y1,iz0,iz1);
46129         dx01*=-1; dy01*=-1; diz01*=-1;
46130       }
46131 
46132       static unsigned int hatch = ~0U - (~0U>>1);
46133       if (init_hatch) hatch = ~0U - (~0U>>1);
46134       cimg_init_scanline(opacity);
46135 
46136       const int
46137         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
46138         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
46139       dy01+=dy01?0:1;
46140 
46141       for (int y = cy0; y!=cy1; y+=step) {
46142         const int
46143           yy0 = y - y0,
46144           x = x0 + (dx01*yy0 + hdy01)/dy01;
46145         const float iz = iz0 + diz01*yy0/dy01;
46146         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
46147 
46148         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
46149           *ptrz = (tz)iz;
46150           T *const ptrd = is_horizontal?data(y,x):data(x,y);
46151           cimg_forC(*this,c) {
46152             const T val = color[c];
46153             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46154           }
46155         }
46156         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
46157       }
46158       return *this;
46159     }
46160 
46161     //! Draw a textured 2D line.
46162     /**
46163        \param x0 X-coordinate of the starting line point.
46164        \param y0 Y-coordinate of the starting line point.
46165        \param x1 X-coordinate of the ending line point.
46166        \param y1 Y-coordinate of the ending line point.
46167        \param texture Texture image defining the pixel colors.
46168        \param tx0 X-coordinate of the starting texture point.
46169        \param ty0 Y-coordinate of the starting texture point.
46170        \param tx1 X-coordinate of the ending texture point.
46171        \param ty1 Y-coordinate of the ending texture point.
46172        \param opacity Drawing opacity.
46173        \param pattern An integer whose bits describe the line pattern.
46174        \param init_hatch Tells if the hash variable must be reinitialized.
46175        \note
46176        - Line routine uses the well known Bresenham's algorithm.
46177        \par Example:
46178        \code
46179        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
46180        const unsigned char color[] = { 255,128,64 };
46181        img.draw_line(40,40,80,70,texture,0,0,255,255);
46182        \endcode
46183     **/
46184     template<typename tc>
46185     CImg<T>& draw_line(int x0, int y0,
46186                        int x1, int y1,
46187                        const CImg<tc>& texture,
46188                        int tx0, int ty0,
46189                        int tx1, int ty1,
46190                        const float opacity=1,
46191                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46192 
46193       if (is_empty() || !opacity || !pattern) return *this;
46194       if (texture._depth>1 || texture._spectrum<_spectrum)
46195         throw CImgArgumentException(_cimg_instance
46196                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
46197                                     cimg_instance,
46198                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46199       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
46200 
46201       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
46202 
46203       int w1 = width() - 1, h1 = height() - 1;
46204       longT
46205         dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0,
46206         dtx01 = (longT)tx1 - tx0, dty01 = (longT)ty1 - ty0;
46207 
46208       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
46209       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
46210       if (pattern==~0U && y0>y1) {
46211         cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
46212         dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1;
46213       }
46214 
46215       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46216       static unsigned int hatch = ~0U - (~0U>>1);
46217       if (init_hatch) hatch = ~0U - (~0U>>1);
46218       cimg_init_scanline(opacity);
46219 
46220       const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
46221       const longT
46222         hdy01 = dy01*cimg::sign(dx01)/2,
46223         hdy01tx = dy01*cimg::sign(dtx01)/2,
46224         hdy01ty = dy01*cimg::sign(dty01)/2;
46225 
46226       dy01+=dy01?0:1;
46227 
46228       for (int y = cy0; y!=cy1; y+=step) {
46229         const longT
46230           yy0 = (longT)y - y0,
46231           x = x0 + (dx01*yy0 + hdy01)/dy01,
46232           tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01,
46233           ty = ty0 + (dty01*yy0 + hdy01ty)/dy01;
46234         if (x>=0 && x<=w1 && pattern&hatch) {
46235           T *const ptrd = is_horizontal?data(y,x):data(x,y);
46236           const tc *const color = &texture._atXY(tx,ty);
46237           cimg_forC(*this,c) {
46238             const T val = color[c*twhd];
46239             ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46240           }
46241         }
46242         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
46243       }
46244       return *this;
46245     }
46246 
46247     //! Draw a textured 2D line, with perspective correction.
46248     /**
46249        \param x0 X-coordinate of the starting point.
46250        \param y0 Y-coordinate of the starting point.
46251        \param z0 Z-coordinate of the starting point
46252        \param x1 X-coordinate of the ending point.
46253        \param y1 Y-coordinate of the ending point.
46254        \param z1 Z-coordinate of the ending point.
46255        \param texture Texture image defining the pixel colors.
46256        \param tx0 X-coordinate of the starting texture point.
46257        \param ty0 Y-coordinate of the starting texture point.
46258        \param tx1 X-coordinate of the ending texture point.
46259        \param ty1 Y-coordinate of the ending texture point.
46260        \param opacity Drawing opacity.
46261        \param pattern An integer whose bits describe the line pattern.
46262        \param init_hatch Tells if the hash variable must be reinitialized.
46263     **/
46264     template<typename tc>
46265     CImg<T>& draw_line(int x0, int y0, const float z0,
46266                        int x1, int y1, const float z1,
46267                        const CImg<tc>& texture,
46268                        const int tx0, const int ty0,
46269                        const int tx1, const int ty1,
46270                        const float opacity=1,
46271                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46272       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
46273       if (texture._depth>1 || texture._spectrum<_spectrum)
46274         throw CImgArgumentException(_cimg_instance
46275                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
46276                                     cimg_instance,
46277                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46278       if (is_overlapped(texture))
46279         return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
46280 
46281       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
46282 
46283       float iz0 = 1/z0, iz1 = 1/z1;
46284       int w1 = width() - 1, h1 = height() - 1;
46285       longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
46286       float
46287         diz01 = iz1 - iz0,
46288         txz0 = tx0*iz0, txz1 = tx1*iz1,
46289         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
46290         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
46291 
46292       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
46293       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
46294       if (pattern==~0U && y0>y1) {
46295         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
46296         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
46297       }
46298 
46299       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46300       static unsigned int hatch = ~0U - (~0U>>1);
46301       if (init_hatch) hatch = ~0U - (~0U>>1);
46302       cimg_init_scanline(opacity);
46303 
46304       const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
46305       const longT hdy01 = dy01*cimg::sign(dx01)/2;
46306 
46307       dy01+=dy01?0:1;
46308 
46309       for (int y = cy0; y!=cy1; y+=step) {
46310         const longT
46311           yy0 = (longT)y - y0,
46312           x = x0 + (dx01*yy0 + hdy01)/dy01;
46313         const float
46314           iz = iz0 + diz01*yy0/dy01,
46315           txz = txz0 + dtxz01*yy0/dy01,
46316           tyz = tyz0 + dtyz01*yy0/dy01;
46317         if (x>=0 && x<=w1 && pattern&hatch) {
46318           const int
46319             tx = (int)cimg::round(txz/iz),
46320             ty = (int)cimg::round(tyz/iz);
46321           T *const ptrd = is_horizontal?data(y,x):data(x,y);
46322           const tc *const color = &texture._atXY(tx,ty);
46323           cimg_forC(*this,c) {
46324             const T val = color[c*twhd];
46325             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46326           }
46327         }
46328         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
46329       }
46330       return *this;
46331     }
46332 
46333     //! Draw a textured 2D line, with perspective correction and z-buffering.
46334     /**
46335        \param zbuffer Z-buffer image.
46336        \param x0 X-coordinate of the starting point.
46337        \param y0 Y-coordinate of the starting point.
46338        \param z0 Z-coordinate of the starting point
46339        \param x1 X-coordinate of the ending point.
46340        \param y1 Y-coordinate of the ending point.
46341        \param z1 Z-coordinate of the ending point.
46342        \param texture Texture image defining the pixel colors.
46343        \param tx0 X-coordinate of the starting texture point.
46344        \param ty0 Y-coordinate of the starting texture point.
46345        \param tx1 X-coordinate of the ending texture point.
46346        \param ty1 Y-coordinate of the ending texture point.
46347        \param opacity Drawing opacity.
46348        \param pattern An integer whose bits describe the line pattern.
46349        \param init_hatch Tells if the hash variable must be reinitialized.
46350     **/
46351     template<typename tz, typename tc>
46352     CImg<T>& draw_line(CImg<tz>& zbuffer,
46353                        int x0, int y0, const float z0,
46354                        int x1, int y1, const float z1,
46355                        const CImg<tc>& texture,
46356                        const int tx0, const int ty0,
46357                        const int tx1, const int ty1,
46358                        const float opacity=1,
46359                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46360       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
46361       if (!is_sameXY(zbuffer))
46362         throw CImgArgumentException(_cimg_instance
46363                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46364                                     "different dimensions.",
46365                                     cimg_instance,
46366                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46367       if (texture._depth>1 || texture._spectrum<_spectrum)
46368         throw CImgArgumentException(_cimg_instance
46369                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
46370                                     cimg_instance,
46371                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46372       if (is_overlapped(texture))
46373         return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
46374 
46375       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
46376 
46377       float iz0 = 1/z0, iz1 = 1/z1;
46378       int w1 = width() - 1, h1 = height() - 1;
46379       longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
46380       float
46381         diz01 = iz1 - iz0,
46382         txz0 = tx0*iz0, txz1 = tx1*iz1,
46383         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
46384         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
46385 
46386       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
46387       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
46388       if (pattern==~0U && y0>y1) {
46389         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
46390         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
46391       }
46392 
46393       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46394       static unsigned int hatch = ~0U - (~0U>>1);
46395       if (init_hatch) hatch = ~0U - (~0U>>1);
46396       cimg_init_scanline(opacity);
46397 
46398       const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
46399       const longT hdy01 = dy01*cimg::sign(dx01)/2;
46400 
46401       dy01+=dy01?0:1;
46402 
46403       for (int y = cy0; y!=cy1; y+=step) {
46404         const longT
46405           yy0 = (longT)y - y0,
46406           x = x0 + (dx01*yy0 + hdy01)/dy01;
46407         const float
46408           iz = iz0 + diz01*yy0/dy01,
46409           txz = txz0 + dtxz01*yy0/dy01,
46410           tyz = tyz0 + dtyz01*yy0/dy01;
46411         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
46412 
46413         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
46414           *ptrz = (tz)iz;
46415           const int
46416             tx = (int)cimg::round(txz/iz),
46417             ty = (int)cimg::round(tyz/iz);
46418           T *const ptrd = is_horizontal?data(y,x):data(x,y);
46419           const tc *const color = &texture._atXY(tx,ty);
46420           cimg_forC(*this,c) {
46421             const T val = color[c*twhd];
46422             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46423           }
46424         }
46425         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
46426       }
46427       return *this;
46428     }
46429 
46430     //! Draw a set of consecutive lines.
46431     /**
46432        \param points Coordinates of vertices, stored as a list of vectors.
46433        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46434        \param opacity Drawing opacity.
46435        \param pattern An integer whose bits describe the line pattern.
46436        \param init_hatch If set to true, init hatch motif.
46437        \note
46438        - This function uses several call to the single CImg::draw_line() procedure,
46439        depending on the vectors size in \p points.
46440     **/
46441     template<typename t, typename tc>
46442     CImg<T>& draw_line(const CImg<t>& points,
46443                        const tc *const color, const float opacity=1,
46444                        const unsigned int pattern=~0U, const bool init_hatch=true) {
46445       if (is_empty() || !points || points._width<2) return *this;
46446       bool ninit_hatch = init_hatch;
46447       switch (points._height) {
46448       case 0 : case 1 :
46449         throw CImgArgumentException(_cimg_instance
46450                                     "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
46451                                     cimg_instance,
46452                                     points._width,points._height,points._depth,points._spectrum,points._data);
46453 
46454       default : {
46455         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
46456         int ox = x0, oy = y0;
46457         for (unsigned int i = 1; i<points._width; ++i) {
46458           const int x = (int)points(i,0), y = (int)points(i,1);
46459           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
46460           ninit_hatch = false;
46461           ox = x; oy = y;
46462         }
46463       }
46464       }
46465       return *this;
46466     }
46467 
46468     //! Draw a 2D arrow.
46469     /**
46470        \param x0 X-coordinate of the starting arrow point (tail).
46471        \param y0 Y-coordinate of the starting arrow point (tail).
46472        \param x1 X-coordinate of the ending arrow point (head).
46473        \param y1 Y-coordinate of the ending arrow point (head).
46474        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46475        \param angle Aperture angle of the arrow head.
46476        \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
46477        \param opacity Drawing opacity.
46478        \param pattern An integer whose bits describe the line pattern.
46479     **/
46480     template<typename tc>
46481     CImg<T>& draw_arrow(const int x0, const int y0,
46482                         const int x1, const int y1,
46483                         const tc *const color, const float opacity=1,
46484                         const float angle=30, const float length=-10,
46485                         const unsigned int pattern=~0U) {
46486       if (is_empty()) return *this;
46487       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
46488         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f,
46489         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
46490       if (sq>0) {
46491         const float
46492             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
46493             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
46494         const int
46495           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
46496           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
46497           xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
46498         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
46499       } else draw_point(x0,y0,color,opacity);
46500       return *this;
46501     }
46502 
46503     //! Draw a 2D spline.
46504     /**
46505        \param x0 X-coordinate of the starting curve point
46506        \param y0 Y-coordinate of the starting curve point
46507        \param u0 X-coordinate of the starting velocity
46508        \param v0 Y-coordinate of the starting velocity
46509        \param x1 X-coordinate of the ending curve point
46510        \param y1 Y-coordinate of the ending curve point
46511        \param u1 X-coordinate of the ending velocity
46512        \param v1 Y-coordinate of the ending velocity
46513        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46514        \param precision Curve drawing precision.
46515        \param opacity Drawing opacity.
46516        \param pattern An integer whose bits describe the line pattern.
46517        \param init_hatch If \c true, init hatch motif.
46518        \note
46519        - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points
46520        and corresponding velocity vectors.
46521        - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the
46522        average number of pixels in each drawn segment.
46523        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
46524          (\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
46525          and (\p xa,\p ya), (\p xb,\p yb) are two
46526        \e control points.
46527        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
46528        the control points as
46529        \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).
46530        \par Example:
46531        \code
46532        CImg<unsigned char> img(100,100,1,3,0);
46533        const unsigned char color[] = { 255,255,255 };
46534        img.draw_spline(30,30,0,100,90,40,0,-100,color);
46535        \endcode
46536     **/
46537     template<typename tc>
46538     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
46539                          const int x1, const int y1, const float u1, const float v1,
46540                          const tc *const color, const float opacity=1,
46541                          const float precision=0.25, const unsigned int pattern=~0U,
46542                          const bool init_hatch=true) {
46543       if (is_empty()) return *this;
46544       if (!color)
46545         throw CImgArgumentException(_cimg_instance
46546                                     "draw_spline(): Specified color is (null).",
46547                                     cimg_instance);
46548       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
46549       bool ninit_hatch = init_hatch;
46550       const float
46551         ax = u0 + u1 + 2*(x0 - x1),
46552         bx = 3*(x1 - x0) - 2*u0 - u1,
46553         ay = v0 + v1 + 2*(y0 - y1),
46554         by = 3*(y1 - y0) - 2*v0 - v1,
46555         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
46556       int ox = x0, oy = y0;
46557       for (float t = 0; t<1; t+=_precision) {
46558         const float t2 = t*t, t3 = t2*t;
46559         const int
46560           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
46561           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
46562         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
46563         ninit_hatch = false;
46564         ox = nx; oy = ny;
46565       }
46566       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
46567     }
46568 
46569     //! Draw a textured 2D spline.
46570     /**
46571        \param x0 X-coordinate of the starting curve point
46572        \param y0 Y-coordinate of the starting curve point
46573        \param u0 X-coordinate of the starting velocity
46574        \param v0 Y-coordinate of the starting velocity
46575        \param x1 X-coordinate of the ending curve point
46576        \param y1 Y-coordinate of the ending curve point
46577        \param u1 X-coordinate of the ending velocity
46578        \param v1 Y-coordinate of the ending velocity
46579        \param texture Texture image defining line pixel colors.
46580        \param tx0 X-coordinate of the starting texture point.
46581        \param ty0 Y-coordinate of the starting texture point.
46582        \param tx1 X-coordinate of the ending texture point.
46583        \param ty1 Y-coordinate of the ending texture point.
46584        \param precision Curve drawing precision.
46585        \param opacity Drawing opacity.
46586        \param pattern An integer whose bits describe the line pattern.
46587        \param init_hatch if \c true, reinit hatch motif.
46588     **/
46589     template<typename t>
46590     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
46591                          const int x1, const int y1, const float u1, const float v1,
46592                          const CImg<t>& texture,
46593                          const int tx0, const int ty0, const int tx1, const int ty1,
46594                          const float opacity=1,
46595                          const float precision=4, const unsigned int pattern=~0U,
46596                          const bool init_hatch=true) {
46597       if (texture._depth>1 || texture._spectrum<_spectrum)
46598         throw CImgArgumentException(_cimg_instance
46599                                     "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
46600                                     cimg_instance,
46601                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46602       if (is_empty()) return *this;
46603       if (is_overlapped(texture))
46604         return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
46605       if (x0==x1 && y0==y1)
46606         return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
46607                                                       y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(),
46608                           opacity);
46609       bool ninit_hatch = init_hatch;
46610       const float
46611         ax = u0 + u1 + 2*(x0 - x1),
46612         bx = 3*(x1 - x0) - 2*u0 - u1,
46613         ay = v0 + v1 + 2*(y0 - y1),
46614         by = 3*(y1 - y0) - 2*v0 - v1,
46615         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
46616       int ox = x0, oy = y0, otx = tx0, oty = ty0;
46617       for (float t1 = 0; t1<1; t1+=_precision) {
46618         const float t2 = t1*t1, t3 = t2*t1;
46619         const int
46620           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
46621           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
46622           ntx = tx0 + (int)((tx1 - tx0)*t1),
46623           nty = ty0 + (int)((ty1 - ty0)*t1);
46624         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
46625         ninit_hatch = false;
46626         ox = nx; oy = ny; otx = ntx; oty = nty;
46627       }
46628       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
46629     }
46630 
46631     //! Draw a set of consecutive splines.
46632     /**
46633        \param points Vertices data.
46634        \param tangents Tangents data.
46635        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46636        \param opacity Drawing opacity.
46637        \param is_closed_set Tells if the drawn spline set is closed.
46638        \param precision Precision of the drawing.
46639        \param pattern An integer whose bits describe the line pattern.
46640        \param init_hatch If \c true, init hatch motif.
46641     **/
46642     template<typename tp, typename tt, typename tc>
46643     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
46644                          const tc *const color, const float opacity=1,
46645                          const bool is_closed_set=false, const float precision=4,
46646                          const unsigned int pattern=~0U, const bool init_hatch=true) {
46647       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
46648       bool ninit_hatch = init_hatch;
46649       switch (points._height) {
46650       case 0 : case 1 :
46651         throw CImgArgumentException(_cimg_instance
46652                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
46653                                     cimg_instance,
46654                                     points._width,points._height,points._depth,points._spectrum,points._data);
46655 
46656       default : {
46657         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
46658         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
46659         int ox = x0, oy = y0;
46660         float ou = u0, ov = v0;
46661         for (unsigned int i = 1; i<points._width; ++i) {
46662           const int x = (int)points(i,0), y = (int)points(i,1);
46663           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
46664           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
46665           ninit_hatch = false;
46666           ox = x; oy = y; ou = u; ov = v;
46667         }
46668         if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
46669       }
46670       }
46671       return *this;
46672     }
46673 
46674     //! Draw a set of consecutive splines \overloading.
46675     /**
46676        Similar to previous function, with the point tangents automatically estimated from the given points set.
46677     **/
46678     template<typename tp, typename tc>
46679     CImg<T>& draw_spline(const CImg<tp>& points,
46680                          const tc *const color, const float opacity=1,
46681                          const bool is_closed_set=false, const float precision=4,
46682                          const unsigned int pattern=~0U, const bool init_hatch=true) {
46683       if (is_empty() || !points || points._width<2) return *this;
46684       CImg<Tfloat> tangents;
46685       switch (points._height) {
46686       case 0 : case 1 :
46687         throw CImgArgumentException(_cimg_instance
46688                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
46689                                     cimg_instance,
46690                                     points._width,points._height,points._depth,points._spectrum,points._data);
46691       case 2 : {
46692         tangents.assign(points._width,points._height);
46693         cimg_forX(points,p) {
46694           const unsigned int
46695             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
46696             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
46697           const float
46698             x = (float)points(p,0),
46699             y = (float)points(p,1),
46700             x0 = (float)points(p0,0),
46701             y0 = (float)points(p0,1),
46702             x1 = (float)points(p1,0),
46703             y1 = (float)points(p1,1),
46704             u0 = x - x0,
46705             v0 = y - y0,
46706             n0 = 1e-8f + cimg::hypot(u0,v0),
46707             u1 = x1 - x,
46708             v1 = y1 - y,
46709             n1 = 1e-8f + cimg::hypot(u1,v1),
46710             u = u0/n0 + u1/n1,
46711             v = v0/n0 + v1/n1,
46712             n = 1e-8f + cimg::hypot(u,v),
46713             fact = 0.5f*(n0 + n1);
46714           tangents(p,0) = (Tfloat)(fact*u/n);
46715           tangents(p,1) = (Tfloat)(fact*v/n);
46716         }
46717       } break;
46718       default : {
46719         tangents.assign(points._width,points._height);
46720         cimg_forX(points,p) {
46721           const unsigned int
46722             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
46723             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
46724           const float
46725             x = (float)points(p,0),
46726             y = (float)points(p,1),
46727             z = (float)points(p,2),
46728             x0 = (float)points(p0,0),
46729             y0 = (float)points(p0,1),
46730             z0 = (float)points(p0,2),
46731             x1 = (float)points(p1,0),
46732             y1 = (float)points(p1,1),
46733             z1 = (float)points(p1,2),
46734             u0 = x - x0,
46735             v0 = y - y0,
46736             w0 = z - z0,
46737             n0 = 1e-8f + cimg::hypot(u0,v0,w0),
46738             u1 = x1 - x,
46739             v1 = y1 - y,
46740             w1 = z1 - z,
46741             n1 = 1e-8f + cimg::hypot(u1,v1,w1),
46742             u = u0/n0 + u1/n1,
46743             v = v0/n0 + v1/n1,
46744             w = w0/n0 + w1/n1,
46745             n = 1e-8f + cimg::hypot(u,v,w),
46746             fact = 0.5f*(n0 + n1);
46747           tangents(p,0) = (Tfloat)(fact*u/n);
46748           tangents(p,1) = (Tfloat)(fact*v/n);
46749           tangents(p,2) = (Tfloat)(fact*w/n);
46750         }
46751       }
46752       }
46753       return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
46754     }
46755 
46756     // [internal] Draw a filled triangle.
46757     template<typename tc>
46758     CImg<T>& _draw_triangle(int x0, int y0,
46759                             int x1, int y1,
46760                             int x2, int y2,
46761                             const tc *const color, const float opacity,
46762                             const float brightness) {
46763       if (y0>y1) cimg::swap(x0,x1,y0,y1);
46764       if (y0>y2) cimg::swap(x0,x2,y0,y2);
46765       if (y1>y2) cimg::swap(x1,x2,y1,y2);
46766       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46767 
46768       const int
46769         h1 = height() - 1,
46770         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
46771       const longT
46772         dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
46773         dy01 = std::max((longT)1,(longT)y1 - y0),
46774         dy02 = std::max((longT)1,(longT)y2 - y0),
46775         dy12 = std::max((longT)1,(longT)y2 - y1),
46776         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46777       const float cbs = cimg::cut(brightness,0,2);
46778       cimg_init_scanline(opacity);
46779 
46780       for (int y = cy0; y<=cy2; ++y) {
46781         const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
46782         longT
46783           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46784           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46785         if (xm>xM) cimg::swap(xm,xM);
46786         cimg_draw_scanline(xm,xM,y,color,opacity,cbs);
46787       }
46788       return *this;
46789     }
46790 
46791     //! Draw a filled 2D triangle.
46792     /**
46793        \param x0 X-coordinate of the first vertex.
46794        \param y0 Y-coordinate of the first vertex.
46795        \param x1 X-coordinate of the second vertex.
46796        \param y1 Y-coordinate of the second vertex.
46797        \param x2 X-coordinate of the third vertex.
46798        \param y2 Y-coordinate of the third vertex.
46799        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46800        \param opacity Drawing opacity.
46801      **/
46802     template<typename tc>
46803     CImg<T>& draw_triangle(const int x0, const int y0,
46804                            const int x1, const int y1,
46805                            const int x2, const int y2,
46806                            const tc *const color, const float opacity=1) {
46807       if (is_empty()) return *this;
46808       if (!color)
46809         throw CImgArgumentException(_cimg_instance
46810                                     "draw_triangle(): Specified color is (null).",
46811                                     cimg_instance);
46812       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
46813       return *this;
46814     }
46815 
46816     //! Draw a outlined 2D triangle.
46817     /**
46818        \param x0 X-coordinate of the first vertex.
46819        \param y0 Y-coordinate of the first vertex.
46820        \param x1 X-coordinate of the second vertex.
46821        \param y1 Y-coordinate of the second vertex.
46822        \param x2 X-coordinate of the third vertex.
46823        \param y2 Y-coordinate of the third vertex.
46824        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46825        \param opacity Drawing opacity.
46826        \param pattern An integer whose bits describe the outline pattern.
46827      **/
46828     template<typename tc>
46829     CImg<T>& draw_triangle(const int x0, const int y0,
46830                            const int x1, const int y1,
46831                            const int x2, const int y2,
46832                            const tc *const color, const float opacity,
46833                            const unsigned int pattern) {
46834       if (is_empty()) return *this;
46835       if (!color)
46836         throw CImgArgumentException(_cimg_instance
46837                                     "draw_triangle(): Specified color is (null).",
46838                                     cimg_instance);
46839       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
46840         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
46841         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
46842       return *this;
46843     }
46844 
46845     //! Draw a filled 2D triangle, with z-buffering.
46846     /**
46847        \param zbuffer Z-buffer image.
46848        \param x0 X-coordinate of the first vertex.
46849        \param y0 Y-coordinate of the first vertex.
46850        \param z0 Z-coordinate of the first vertex.
46851        \param x1 X-coordinate of the second vertex.
46852        \param y1 Y-coordinate of the second vertex.
46853        \param z1 Z-coordinate of the second vertex.
46854        \param x2 X-coordinate of the third vertex.
46855        \param y2 Y-coordinate of the third vertex.
46856        \param z2 Z-coordinate of the third vertex.
46857        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
46858        \param opacity Drawing opacity.
46859        \param brightness Brightness factor.
46860     **/
46861     template<typename tz, typename tc>
46862     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46863                            int x0, int y0, const float z0,
46864                            int x1, int y1, const float z1,
46865                            int x2, int y2, const float z2,
46866                            const tc *const color, const float opacity=1,
46867                            const float brightness=1) {
46868       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46869       if (!color)
46870         throw CImgArgumentException(_cimg_instance
46871                                     "draw_triangle(): Specified color is (null).",
46872                                     cimg_instance);
46873       if (!is_sameXY(zbuffer))
46874         throw CImgArgumentException(_cimg_instance
46875                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46876                                     "different dimensions.",
46877                                     cimg_instance,
46878                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46879 
46880       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46881       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1);
46882       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2);
46883       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2);
46884       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46885 
46886       const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
46887       const longT
46888         dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
46889         dy01 = std::max((longT)1,(longT)y1 - y0),
46890         dy02 = std::max((longT)1,(longT)y2 - y0),
46891         dy12 = std::max((longT)1,(longT)y2 - y1),
46892         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46893       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
46894 
46895       const float cbs = cimg::cut(brightness,0,2);
46896       cimg_init_scanline(opacity);
46897 
46898       for (int y = cy0; y<=cy2; ++y) {
46899         const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
46900         longT
46901           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46902           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46903         float
46904           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46905           izM = iz0 + diz02*yy0/dy02;
46906         if (xm>xM) cimg::swap(xm,xM,izm,izM);
46907         if (xM>=0 && xm<=w1) {
46908           const int
46909             cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
46910             cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
46911           T *ptrd = data(cxm,y);
46912           tz *ptrz = zbuffer.data(cxm,y);
46913           const longT dxmM = std::max((longT)1,xM - xm);
46914           const float dizmM = izM - izm;
46915 
46916           for (int x = cxm; x<=cxM; ++x) {
46917             const longT xxm = x - xm;
46918             const float iz = izm + dizmM*xxm/dxmM;
46919             if (iz>=*ptrz) {
46920               *ptrz = (tz)iz;
46921               cimg_forC(*this,c) {
46922                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
46923                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46924               }
46925             }
46926             ++ptrd; ++ptrz;
46927           }
46928         }
46929       }
46930       return *this;
46931     }
46932 
46933     //! Draw a Gouraud-shaded 2D triangle.
46934     /**
46935        \param x0 X-coordinate of the first vertex in the image instance.
46936        \param y0 Y-coordinate of the first vertex in the image instance.
46937        \param x1 X-coordinate of the second vertex in the image instance.
46938        \param y1 Y-coordinate of the second vertex in the image instance.
46939        \param x2 X-coordinate of the third vertex in the image instance.
46940        \param y2 Y-coordinate of the third vertex in the image instance.
46941        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
46942        \param bs0 Brightness factor of the first vertex (in [0,2]).
46943        \param bs1 brightness factor of the second vertex (in [0,2]).
46944        \param bs2 brightness factor of the third vertex (in [0,2]).
46945        \param opacity Drawing opacity.
46946     **/
46947     template<typename tc>
46948     CImg<T>& draw_triangle(int x0, int y0,
46949                            int x1, int y1,
46950                            int x2, int y2,
46951                            const tc *const color,
46952                            float bs0,
46953                            float bs1,
46954                            float bs2,
46955                            const float opacity=1) {
46956       if (is_empty()) return *this;
46957       if (!color)
46958         throw CImgArgumentException(_cimg_instance
46959                                     "draw_triangle(): Specified color is (null).",
46960                                     cimg_instance);
46961 
46962       if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1);
46963       if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2);
46964       if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2);
46965       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46966 
46967       const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
46968       const longT
46969         dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
46970         dy01 = std::max((longT)1,(longT)y1 - y0),
46971         dy02 = std::max((longT)1,(longT)y2 - y0),
46972         dy12 = std::max((longT)1,(longT)y2 - y1),
46973         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46974       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46975 
46976       cimg_init_scanline(opacity);
46977 
46978       for (int y = cy0; y<=cy2; ++y) {
46979         const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
46980         longT
46981           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46982           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46983         float
46984           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46985           bsM = bs0 + dbs02*yy0/dy02;
46986         if (xm>xM) cimg::swap(xm,xM,bsm,bsM);
46987         if (xM>=0 && xm<=w1) {
46988           const int
46989             cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
46990             cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
46991           T *ptrd = data(cxm,y);
46992           const longT dxmM = std::max((longT)1,xM - xm);
46993           const float dbsmM = bsM - bsm;
46994 
46995           for (int x = cxm; x<=cxM; ++x) {
46996             const longT xxm = (longT)x - xm;
46997             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46998             cimg_forC(*this,c) {
46999               const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
47000               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47001             }
47002             ++ptrd;
47003           }
47004         }
47005       }
47006       return *this;
47007     }
47008 
47009     //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading.
47010     template<typename tz, typename tc>
47011     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47012                            int x0, int y0, const float z0,
47013                            int x1, int y1, const float z1,
47014                            int x2, int y2, const float z2,
47015                            const tc *const color,
47016                            float bs0,
47017                            float bs1,
47018                            float bs2,
47019                            float opacity=1) {
47020       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47021       if (!color)
47022         throw CImgArgumentException(_cimg_instance
47023                                     "draw_triangle(): Specified color is (null).",
47024                                     cimg_instance);
47025       if (!is_sameXY(zbuffer))
47026         throw CImgArgumentException(_cimg_instance
47027                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47028                                     "different dimensions.",
47029                                     cimg_instance,
47030                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47031 
47032       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47033       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1);
47034       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2);
47035       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2);
47036       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47037 
47038       const int
47039         w1 = width() - 1, h1 = height() - 1,
47040         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47041         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47042         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47043         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47044       const float
47045         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47046         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47047 
47048       cimg_init_scanline(opacity);
47049 
47050       for (int y = cy0; y<=cy2; ++y) {
47051         const int yy0 = y - y0, yy1 = y - y1;
47052         int
47053           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47054           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47055         float
47056           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47057           izM = iz0 + diz02*yy0/dy02,
47058           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47059           bsM = bs0 + dbs02*yy0/dy02;
47060         if (xm>xM) cimg::swap(xm,xM,izm,izM,bsm,bsM);
47061         if (xM>=0 && xm<=w1) {
47062           const int
47063             cxm = cimg::cut(xm,0,w1),
47064             cxM = cimg::cut(xM,0,w1);
47065           T *ptrd = data(cxm,y);
47066           tz *ptrz = zbuffer.data(cxm,y);
47067           const int dxmM = std::max(1,xM - xm);
47068           const float dizmM = izM - izm, dbsmM = bsM - bsm;
47069 
47070           for (int x = cxm; x<=cxM; ++x) {
47071             const int xxm = x - xm;
47072             const float iz = izm + dizmM*xxm/dxmM;
47073             if (iz>=*ptrz) {
47074               *ptrz = (tz)iz;
47075               const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47076               cimg_forC(*this,c) {
47077                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
47078                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47079               }
47080             }
47081             ++ptrd; ++ptrz;
47082           }
47083         }
47084       }
47085       return *this;
47086     }
47087 
47088     //! Draw a color-interpolated 2D triangle.
47089     /**
47090        \param x0 X-coordinate of the first vertex in the image instance.
47091        \param y0 Y-coordinate of the first vertex in the image instance.
47092        \param x1 X-coordinate of the second vertex in the image instance.
47093        \param y1 Y-coordinate of the second vertex in the image instance.
47094        \param x2 X-coordinate of the third vertex in the image instance.
47095        \param y2 Y-coordinate of the third vertex in the image instance.
47096        \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
47097        \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex.
47098        \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
47099        \param opacity Drawing opacity.
47100      **/
47101     template<typename tc>
47102     CImg<T>& draw_triangle(int x0, int y0,
47103                            int x1, int y1,
47104                            int x2, int y2,
47105                            const tc *color0,
47106                            const tc *color1,
47107                            const tc *color2,
47108                            const float opacity=1) {
47109       typedef typename cimg::superset<tc,int>::type stc;
47110       if (is_empty()) return *this;
47111       if (!color0 || !color1 || !color2)
47112         throw CImgArgumentException(_cimg_instance
47113                                     "draw_triangle(): One of the specified color is (null).",
47114                                     cimg_instance);
47115 
47116       if (y0>y1) cimg::swap(x0,x1,y0,y1,color0,color1);
47117       if (y0>y2) cimg::swap(x0,x2,y0,y2,color0,color2);
47118       if (y1>y2) cimg::swap(x1,x2,y1,y2,color1,color2);
47119       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47120 
47121       const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
47122       const longT
47123         dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
47124         dy01 = std::max((longT)1,(longT)y1 - y0),
47125         dy02 = std::max((longT)1,(longT)y2 - y0),
47126         dy12 = std::max((longT)1,(longT)y2 - y1),
47127         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47128       cimg_init_scanline(opacity);
47129 
47130       cimg_forC(*this,c) {
47131         const stc dcolor01 = color1[c] - color0[c], dcolor02 = color2[c] - color0[c], dcolor12 = color2[c] - color1[c];
47132 
47133         for (int y = cy0; y<=cy2; ++y) {
47134           const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
47135           longT
47136             xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47137             xM = x0 + (dx02*yy0 + hdy02)/dy02;
47138           stc
47139             colorm = y<y1?(color0[c] + dcolor01*yy0/dy01):(color1[c] + dcolor12*yy1/dy12),
47140             colorM = color0[c] + dcolor02*yy0/dy02;
47141           if (xm>xM) cimg::swap(xm,xM,colorm,colorM);
47142           if (xM>=0 && xm<=w1) {
47143             const int
47144               cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
47145               cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
47146             T *ptrd = data(cxm,y);
47147             const longT dxmM = std::max((longT)1,xM - xm);
47148             const stc dcolormM = colorM - colorm;
47149 
47150             for (int x = cxm; x<=cxM; ++x) {
47151               const longT xxm = (longT)x - xm;
47152               const stc col = colorm + dcolormM*xxm/dxmM;
47153               ptrd[c*_sc_whd] = (T)(opacity>=1?col:col*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47154               ++ptrd;
47155             }
47156           }
47157         }
47158       }
47159       return *this;
47160     }
47161 
47162     //! Draw a textured 2D triangle.
47163     /**
47164        \param x0 X-coordinate of the first vertex in the image instance.
47165        \param y0 Y-coordinate of the first vertex in the image instance.
47166        \param x1 X-coordinate of the second vertex in the image instance.
47167        \param y1 Y-coordinate of the second vertex in the image instance.
47168        \param x2 X-coordinate of the third vertex in the image instance.
47169        \param y2 Y-coordinate of the third vertex in the image instance.
47170        \param texture Texture image used to fill the triangle.
47171        \param tx0 X-coordinate of the first vertex in the texture image.
47172        \param ty0 Y-coordinate of the first vertex in the texture image.
47173        \param tx1 X-coordinate of the second vertex in the texture image.
47174        \param ty1 Y-coordinate of the second vertex in the texture image.
47175        \param tx2 X-coordinate of the third vertex in the texture image.
47176        \param ty2 Y-coordinate of the third vertex in the texture image.
47177        \param opacity Drawing opacity.
47178        \param brightness Brightness factor of the drawing (in [0,2]).
47179     **/
47180     template<typename tc>
47181     CImg<T>& draw_triangle(int x0, int y0,
47182                            int x1, int y1,
47183                            int x2, int y2,
47184                            const CImg<tc>& texture,
47185                            int tx0, int ty0,
47186                            int tx1, int ty1,
47187                            int tx2, int ty2,
47188                            const float opacity=1,
47189                            const float brightness=1) {
47190       if (is_empty()) return *this;
47191       if (texture._depth>1 || texture._spectrum<_spectrum)
47192         throw CImgArgumentException(_cimg_instance
47193                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47194                                     cimg_instance,
47195                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47196       if (is_overlapped(texture))
47197         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
47198 
47199       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
47200       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2);
47201       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2);
47202       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47203 
47204       const int
47205         w1 = width() - 1, h1 = height() - 1,
47206         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47207         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47208         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47209         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47210         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
47211         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
47212         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
47213         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
47214       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47215       const float cbs = cimg::cut(brightness,0,2);
47216       cimg_init_scanline(opacity);
47217 
47218       for (int y = cy0; y<=cy2; ++y) {
47219         const int yy0 = y - y0, yy1 = y - y1;
47220         int
47221           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47222           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47223           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
47224           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
47225           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
47226           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
47227         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM);
47228         if (xM>=0 && xm<=w1) {
47229           const int
47230             cxm = cimg::cut(xm,0,w1),
47231             cxM = cimg::cut(xM,0,w1);
47232           T *ptrd = data(cxm,y);
47233           const int
47234             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47235             dtxmM = txM - txm, dtymM = tyM - tym;
47236 
47237           for (int x = cxm; x<=cxM; ++x) {
47238             const int
47239               xxm = x - xm,
47240               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
47241               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
47242             const tc *const color = &texture._atXY(tx,ty);
47243             cimg_forC(*this,c) {
47244               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
47245               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47246             }
47247             ++ptrd;
47248           }
47249         }
47250       }
47251       return *this;
47252     }
47253 
47254     //! Draw a 2D textured triangle, with perspective correction.
47255     template<typename tc>
47256     CImg<T>& draw_triangle(int x0, int y0, const float z0,
47257                            int x1, int y1, const float z1,
47258                            int x2, int y2, const float z2,
47259                            const CImg<tc>& texture,
47260                            int tx0, int ty0,
47261                            int tx1, int ty1,
47262                            int tx2, int ty2,
47263                            const float opacity=1,
47264                            const float brightness=1) {
47265       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47266       if (texture._depth>1 || texture._spectrum<_spectrum)
47267         throw CImgArgumentException(_cimg_instance
47268                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47269                                     cimg_instance,
47270                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47271       if (is_overlapped(texture))
47272         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
47273 
47274       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47275       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
47276       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
47277       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
47278       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47279 
47280       const int
47281         w1 = width() - 1, h1 = height() - 1,
47282         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47283         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47284         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47285         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47286       const float
47287         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47288         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47289         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47290         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47291         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
47292 
47293       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47294       const float cbs = cimg::cut(brightness,0,2);
47295       cimg_init_scanline(opacity);
47296 
47297       for (int y = cy0; y<=cy2; ++y) {
47298         const int yy0 = y - y0, yy1 = y - y1;
47299         int
47300           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47301           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47302         float
47303           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47304           izM = iz0 + diz02*yy0/dy02,
47305           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47306           txzM = txz0 + dtxz02*yy0/dy02,
47307           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47308           tyzM = tyz0 + dtyz02*yy0/dy02;
47309         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
47310         if (xM>=0 && xm<=w1) {
47311           const int
47312             cxm = cimg::cut(xm,0,w1),
47313             cxM = cimg::cut(xM,0,w1);
47314           T *ptrd = data(cxm,y);
47315           const int dxmM = std::max(1,xM - xm);
47316           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
47317 
47318           for (int x = cxm; x<=cxM; ++x) {
47319             const int xxm = x - xm;
47320             const float
47321               iz = izm + dizmM*xxm/dxmM,
47322               txz = txzm + dtxzmM*xxm/dxmM,
47323               tyz = tyzm + dtyzmM*xxm/dxmM;
47324             const int
47325               tx = (int)cimg::round(txz/iz),
47326               ty = (int)cimg::round(tyz/iz);
47327             const tc *const color = &texture._atXY(tx,ty);
47328             cimg_forC(*this,c) {
47329               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
47330               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47331             }
47332             ++ptrd;
47333           }
47334         }
47335       }
47336       return *this;
47337     }
47338 
47339     //! Draw a textured 2D triangle, with perspective correction and z-buffering.
47340     template<typename tz, typename tc>
47341     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47342                            int x0, int y0, const float z0,
47343                            int x1, int y1, const float z1,
47344                            int x2, int y2, const float z2,
47345                            const CImg<tc>& texture,
47346                            int tx0, int ty0,
47347                            int tx1, int ty1,
47348                            int tx2, int ty2,
47349                            const float opacity=1,
47350                            const float brightness=1) {
47351       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47352       if (!is_sameXY(zbuffer))
47353         throw CImgArgumentException(_cimg_instance
47354                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47355                                     "different dimensions.",
47356                                     cimg_instance,
47357                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47358 
47359       if (texture._depth>1 || texture._spectrum<_spectrum)
47360         throw CImgArgumentException(_cimg_instance
47361                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47362                                     cimg_instance,
47363                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47364       if (is_overlapped(texture))
47365         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
47366 
47367       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47368       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
47369       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
47370       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
47371       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47372 
47373       const int
47374         w1 = width() - 1, h1 = height() - 1,
47375         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47376         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47377         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47378         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47379       const float
47380         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47381         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47382         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47383         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47384         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
47385 
47386       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47387       const float cbs = cimg::cut(brightness,0,2);
47388       cimg_init_scanline(opacity);
47389 
47390       for (int y = cy0; y<=cy2; ++y) {
47391         const int yy0 = y - y0, yy1 = y - y1;
47392         int
47393           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47394           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47395         float
47396           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47397           izM = iz0 + diz02*yy0/dy02,
47398           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47399           txzM = txz0 + dtxz02*yy0/dy02,
47400           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47401           tyzM = tyz0 + dtyz02*yy0/dy02;
47402         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
47403         if (xM>=0 && xm<=w1) {
47404           const int
47405             cxm = cimg::cut(xm,0,w1),
47406             cxM = cimg::cut(xM,0,w1);
47407           T *ptrd = data(cxm,y);
47408           tz *ptrz = zbuffer.data(cxm,y);
47409           const int dxmM = std::max(1,xM - xm);
47410           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
47411 
47412           for (int x = cxm; x<=cxM; ++x) {
47413             const int xxm = x - xm;
47414             const float iz = izm + dizmM*xxm/dxmM;
47415             if (iz>=*ptrz) {
47416               *ptrz = (tz)iz;
47417               const float
47418                 txz = txzm + dtxzmM*xxm/dxmM,
47419                 tyz = tyzm + dtyzmM*xxm/dxmM;
47420               const int
47421                 tx = (int)cimg::round(txz/iz),
47422                 ty = (int)cimg::round(tyz/iz);
47423               const tc *const color = &texture._atXY(tx,ty);
47424               cimg_forC(*this,c) {
47425                 const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
47426                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47427               }
47428             }
47429             ++ptrd; ++ptrz;
47430           }
47431         }
47432       }
47433       return *this;
47434     }
47435 
47436     //! Draw a Phong-shaded 2D triangle.
47437     /**
47438        \param x0 X-coordinate of the first vertex in the image instance.
47439        \param y0 Y-coordinate of the first vertex in the image instance.
47440        \param x1 X-coordinate of the second vertex in the image instance.
47441        \param y1 Y-coordinate of the second vertex in the image instance.
47442        \param x2 X-coordinate of the third vertex in the image instance.
47443        \param y2 Y-coordinate of the third vertex in the image instance.
47444        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47445        \param light Light image.
47446        \param lx0 X-coordinate of the first vertex in the light image.
47447        \param ly0 Y-coordinate of the first vertex in the light image.
47448        \param lx1 X-coordinate of the second vertex in the light image.
47449        \param ly1 Y-coordinate of the second vertex in the light image.
47450        \param lx2 X-coordinate of the third vertex in the light image.
47451        \param ly2 Y-coordinate of the third vertex in the light image.
47452        \param opacity Drawing opacity.
47453     **/
47454     template<typename tc, typename tl>
47455     CImg<T>& draw_triangle(int x0, int y0,
47456                            int x1, int y1,
47457                            int x2, int y2,
47458                            const tc *const color,
47459                            const CImg<tl>& light,
47460                            int lx0, int ly0,
47461                            int lx1, int ly1,
47462                            int lx2, int ly2,
47463                            const float opacity=1) {
47464       if (is_empty()) return *this;
47465       if (!color)
47466         throw CImgArgumentException(_cimg_instance
47467                                     "draw_triangle(): Specified color is (null).",
47468                                     cimg_instance);
47469       if (light._depth>1 || light._spectrum<_spectrum)
47470         throw CImgArgumentException(_cimg_instance
47471                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47472                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47473 
47474       if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1);
47475       if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2);
47476       if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2);
47477       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47478 
47479       const int
47480         w1 = width() - 1, h1 = height() - 1,
47481         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47482         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47483         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47484         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47485         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
47486         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
47487         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
47488         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
47489 
47490       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
47491       cimg_init_scanline(opacity);
47492 
47493       for (int y = cy0; y<=cy2; ++y) {
47494         const int yy0 = y - y0, yy1 = y - y1;
47495         int
47496           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47497           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47498           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
47499           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
47500           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
47501           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
47502         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM);
47503         if (xM>=0 && xm<=w1) {
47504           const int
47505             cxm = cimg::cut(xm,0,w1),
47506             cxM = cimg::cut(xM,0,w1);
47507           T *ptrd = data(cxm,y);
47508           const int
47509             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47510             dlxmM = lxM - lxm, dlymM = lyM - lym;
47511 
47512           for (int x = cxm; x<=cxM; ++x) {
47513             const int
47514               xxm = x - xm,
47515               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
47516               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
47517             const tl *const lig = &light._atXY(lx,ly);
47518             cimg_forC(*this,c) {
47519               const tc col = color[c];
47520               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47521               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47522               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47523             }
47524             ++ptrd;
47525           }
47526         }
47527       }
47528       return *this;
47529     }
47530 
47531     //! Draw a Phong-shaded 2D triangle, with z-buffering.
47532     template<typename tz, typename tc, typename tl>
47533     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47534                            int x0, int y0, const float z0,
47535                            int x1, int y1, const float z1,
47536                            int x2, int y2, const float z2,
47537                            const tc *const color,
47538                            const CImg<tl>& light,
47539                            int lx0, int ly0,
47540                            int lx1, int ly1,
47541                            int lx2, int ly2,
47542                            const float opacity=1) {
47543       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47544       if (!color)
47545         throw CImgArgumentException(_cimg_instance
47546                                     "draw_triangle(): Specified color is (null).",
47547                                     cimg_instance);
47548       if (light._depth>1 || light._spectrum<_spectrum)
47549         throw CImgArgumentException(_cimg_instance
47550                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47551                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47552       if (!is_sameXY(zbuffer))
47553         throw CImgArgumentException(_cimg_instance
47554                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47555                                     "different dimensions.",
47556                                     cimg_instance,
47557                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47558       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
47559                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47560 
47561       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47562       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
47563       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
47564       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
47565       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47566 
47567       const int
47568         w1 = width() - 1, h1 = height() - 1,
47569         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47570         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47571         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47572         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47573         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
47574         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
47575         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
47576         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
47577       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
47578 
47579       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
47580       cimg_init_scanline(opacity);
47581 
47582       for (int y = cy0; y<=cy2; ++y) {
47583         const int yy0 = y - y0, yy1 = y - y1;
47584         int
47585           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47586           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47587           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
47588           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
47589           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
47590           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
47591         float
47592           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47593           izM = iz0 + diz02*yy0/dy02;
47594 
47595         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
47596         if (xM>=0 && xm<=w1) {
47597           const int
47598             cxm = cimg::cut(xm,0,w1),
47599             cxM = cimg::cut(xM,0,w1);
47600           T *ptrd = data(cxm,y);
47601           tz *ptrz = zbuffer.data(cxm,y);
47602           const int
47603             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47604             dlxmM = lxM - lxm, dlymM = lyM - lym;
47605           const float dizmM = izM - izm;
47606 
47607           for (int x = cxm; x<=cxM; ++x) {
47608             const int xxm = x - xm;
47609             const float iz = izm + dizmM*xxm/dxmM;
47610             if (iz>=*ptrz) {
47611               *ptrz = (tz)iz;
47612               const int
47613                 lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
47614                 ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
47615               const tl *const lig = &light._atXY(lx,ly);
47616               cimg_forC(*this,c) {
47617                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47618                 const tc col = color[c];
47619                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47620                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47621               }
47622             }
47623             ++ptrd; ++ptrz;
47624           }
47625         }
47626       }
47627       return *this;
47628     }
47629 
47630     //! Draw a textured Gouraud-shaded 2D triangle.
47631     /**
47632        \param x0 X-coordinate of the first vertex in the image instance.
47633        \param y0 Y-coordinate of the first vertex in the image instance.
47634        \param x1 X-coordinate of the second vertex in the image instance.
47635        \param y1 Y-coordinate of the second vertex in the image instance.
47636        \param x2 X-coordinate of the third vertex in the image instance.
47637        \param y2 Y-coordinate of the third vertex in the image instance.
47638        \param texture Texture image used to fill the triangle.
47639        \param tx0 X-coordinate of the first vertex in the texture image.
47640        \param ty0 Y-coordinate of the first vertex in the texture image.
47641        \param tx1 X-coordinate of the second vertex in the texture image.
47642        \param ty1 Y-coordinate of the second vertex in the texture image.
47643        \param tx2 X-coordinate of the third vertex in the texture image.
47644        \param ty2 Y-coordinate of the third vertex in the texture image.
47645        \param bs0 Brightness factor of the first vertex.
47646        \param bs1 Brightness factor of the second vertex.
47647        \param bs2 Brightness factor of the third vertex.
47648        \param opacity Drawing opacity.
47649     **/
47650     template<typename tc>
47651     CImg<T>& draw_triangle(int x0, int y0,
47652                            int x1, int y1,
47653                            int x2, int y2,
47654                            const CImg<tc>& texture,
47655                            int tx0, int ty0,
47656                            int tx1, int ty1,
47657                            int tx2, int ty2,
47658                            float bs0,
47659                            float bs1,
47660                            float bs2,
47661                            const float opacity=1) {
47662       if (is_empty()) return *this;
47663       if (texture._depth>1 || texture._spectrum<_spectrum)
47664         throw CImgArgumentException(_cimg_instance
47665                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47666                                     cimg_instance,
47667                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47668       if (is_overlapped(texture))
47669         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
47670                              bs0,bs1,bs2,opacity);
47671 
47672       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1);
47673       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2);
47674       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2);
47675       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47676 
47677       const int
47678         w1 = width() - 1, h1 = height() - 1,
47679         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47680         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47681         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47682         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47683         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
47684         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
47685         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
47686         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
47687       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47688 
47689       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47690       cimg_init_scanline(opacity);
47691 
47692       for (int y = cy0; y<=cy2; ++y) {
47693         const int yy0 = y - y0, yy1 = y - y1;
47694         int
47695           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47696           xM = x0 + (dx02*yy0 + hdy02)/dy02,
47697           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
47698           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
47699           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
47700           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
47701         float
47702           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47703           bsM = bs0 + dbs02*yy0/dy02;
47704         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM);
47705         if (xM>=0 && xm<=w1) {
47706           const int
47707             cxm = cimg::cut(xm,0,w1),
47708             cxM = cimg::cut(xM,0,w1);
47709           T *ptrd = data(cxm,y);
47710           const int
47711             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
47712             dtxmM = txM - txm, dtymM = tyM - tym;
47713           const float dbsmM = bsM - bsm;
47714 
47715           for (int x = cxm; x<=cxM; ++x) {
47716             const int
47717               xxm = x - xm,
47718               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
47719               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
47720             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47721             const tc *const color = &texture._atXY(tx,ty);
47722             cimg_forC(*this,c) {
47723               const tc col = color[c*twhd];
47724               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47725               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47726             }
47727             ++ptrd;
47728           }
47729         }
47730       }
47731       return *this;
47732     }
47733 
47734     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading.
47735     template<typename tc>
47736     CImg<T>& draw_triangle(int x0, int y0, const float z0,
47737                            int x1, int y1, const float z1,
47738                            int x2, int y2, const float z2,
47739                            const CImg<tc>& texture,
47740                            int tx0, int ty0,
47741                            int tx1, int ty1,
47742                            int tx2, int ty2,
47743                            float bs0,
47744                            float bs1,
47745                            float bs2,
47746                            const float opacity=1) {
47747       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47748       if (texture._depth>1 || texture._spectrum<_spectrum)
47749         throw CImgArgumentException(_cimg_instance
47750                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47751                                     cimg_instance,
47752                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47753       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
47754                                                        bs0,bs1,bs2,opacity);
47755 
47756       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47757       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
47758       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
47759       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
47760       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47761 
47762       const int
47763         w1 = width() - 1, h1 = height() - 1,
47764         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47765         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47766         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47767         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47768       const float
47769         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47770         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47771         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47772         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47773         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47774         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47775 
47776       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47777       cimg_init_scanline(opacity);
47778 
47779       for (int y = cy0; y<=cy2; ++y) {
47780         const int yy0 = y - y0, yy1 = y - y1;
47781         int
47782           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47783           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47784         float
47785           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47786           izM = iz0 + diz02*yy0/dy02,
47787           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47788           txzM = txz0 + dtxz02*yy0/dy02,
47789           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47790           tyzM = tyz0 + dtyz02*yy0/dy02,
47791           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47792           bsM = bs0 + dbs02*yy0/dy02;
47793         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
47794         if (xM>=0 && xm<=w1) {
47795           const int
47796             cxm = cimg::cut(xm,0,w1),
47797             cxM = cimg::cut(xM,0,w1);
47798           T *ptrd = data(cxm,y);
47799           const int dxmM = std::max(1,xM - xm);
47800           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
47801 
47802           for (int x = cxm; x<=cxM; ++x) {
47803             const int xxm = x - xm;
47804             const float
47805               iz = izm + dizmM*xxm/dxmM,
47806               txz = txzm + dtxzmM*xxm/dxmM,
47807               tyz = tyzm + dtyzmM*xxm/dxmM,
47808               cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47809             const int
47810               tx = (int)cimg::round(txz/iz),
47811               ty = (int)cimg::round(tyz/iz);
47812             const tc *const color = &texture._atXY(tx,ty);
47813             cimg_forC(*this,c) {
47814               const tc col = color[c*twhd];
47815               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47816               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47817             }
47818             ++ptrd;
47819           }
47820         }
47821       }
47822       return *this;
47823     }
47824 
47825     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading.
47826     template<typename tz, typename tc>
47827     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47828                            int x0, int y0, const float z0,
47829                            int x1, int y1, const float z1,
47830                            int x2, int y2, const float z2,
47831                            const CImg<tc>& texture,
47832                            int tx0, int ty0,
47833                            int tx1, int ty1,
47834                            int tx2, int ty2,
47835                            float bs0,
47836                            float bs1,
47837                            float bs2,
47838                            const float opacity=1) {
47839       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47840       if (!is_sameXY(zbuffer))
47841         throw CImgArgumentException(_cimg_instance
47842                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47843                                     "different dimensions.",
47844                                     cimg_instance,
47845                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47846       if (texture._depth>1 || texture._spectrum<_spectrum)
47847         throw CImgArgumentException(_cimg_instance
47848                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47849                                     cimg_instance,
47850                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47851       if (is_overlapped(texture))
47852         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity);
47853 
47854       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47855       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
47856       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
47857       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
47858       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47859 
47860       const int
47861         w1 = width() - 1, h1 = height() - 1,
47862         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47863         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47864         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47865         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47866       const float
47867         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47868         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47869         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47870         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47871         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47872         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
47873 
47874       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
47875       cimg_init_scanline(opacity);
47876 
47877       for (int y = cy0; y<=cy2; ++y) {
47878         const int yy0 = y - y0, yy1 = y - y1;
47879         int
47880           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47881           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47882         float
47883           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47884           izM = iz0 + diz02*yy0/dy02,
47885           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47886           txzM = txz0 + dtxz02*yy0/dy02,
47887           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47888           tyzM = tyz0 + dtyz02*yy0/dy02,
47889           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
47890           bsM = bs0 + dbs02*yy0/dy02;
47891         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
47892         if (xM>=0 && xm<=w1) {
47893           const int
47894             cxm = cimg::cut(xm,0,w1),
47895             cxM = cimg::cut(xM,0,w1);
47896           T *ptrd = data(cxm,y);
47897           tz *ptrz = zbuffer.data(cxm,y);
47898           const int dxmM = std::max(1,xM - xm);
47899           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
47900 
47901           for (int x = cxm; x<=cxM; ++x) {
47902             const int xxm = x - xm;
47903             const float iz = izm + dizmM*xxm/dxmM;
47904             if (iz>=*ptrz) {
47905               *ptrz = (tz)iz;
47906               const float
47907                 txz = txzm + dtxzmM*xxm/dxmM,
47908                 tyz = tyzm + dtyzmM*xxm/dxmM,
47909                 cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
47910               const int
47911                 tx = (int)cimg::round(txz/iz),
47912                 ty = (int)cimg::round(tyz/iz);
47913               const tc *const color = &texture._atXY(tx,ty);
47914               cimg_forC(*this,c) {
47915                 const tc col = color[c*twhd];
47916                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47917                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47918               }
47919             }
47920             ++ptrd; ++ptrz;
47921           }
47922         }
47923       }
47924       return *this;
47925     }
47926 
47927     //! Draw a textured Phong-shaded 2D triangle.
47928     /**
47929        \param x0 X-coordinate of the first vertex in the image instance.
47930        \param y0 Y-coordinate of the first vertex in the image instance.
47931        \param x1 X-coordinate of the second vertex in the image instance.
47932        \param y1 Y-coordinate of the second vertex in the image instance.
47933        \param x2 X-coordinate of the third vertex in the image instance.
47934        \param y2 Y-coordinate of the third vertex in the image instance.
47935        \param texture Texture image used to fill the triangle.
47936        \param tx0 X-coordinate of the first vertex in the texture image.
47937        \param ty0 Y-coordinate of the first vertex in the texture image.
47938        \param tx1 X-coordinate of the second vertex in the texture image.
47939        \param ty1 Y-coordinate of the second vertex in the texture image.
47940        \param tx2 X-coordinate of the third vertex in the texture image.
47941        \param ty2 Y-coordinate of the third vertex in the texture image.
47942        \param light Light image.
47943        \param lx0 X-coordinate of the first vertex in the light image.
47944        \param ly0 Y-coordinate of the first vertex in the light image.
47945        \param lx1 X-coordinate of the second vertex in the light image.
47946        \param ly1 Y-coordinate of the second vertex in the light image.
47947        \param lx2 X-coordinate of the third vertex in the light image.
47948        \param ly2 Y-coordinate of the third vertex in the light image.
47949        \param opacity Drawing opacity.
47950     **/
47951     template<typename tc, typename tl>
47952     CImg<T>& draw_triangle(int x0, int y0,
47953                            int x1, int y1,
47954                            int x2, int y2,
47955                            const CImg<tc>& texture,
47956                            int tx0, int ty0,
47957                            int tx1, int ty1,
47958                            int tx2, int ty2,
47959                            const CImg<tl>& light,
47960                            int lx0, int ly0,
47961                            int lx1, int ly1,
47962                            int lx2, int ly2,
47963                            const float opacity=1) {
47964       if (is_empty()) return *this;
47965       if (texture._depth>1 || texture._spectrum<_spectrum)
47966         throw CImgArgumentException(_cimg_instance
47967                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47968                                     cimg_instance,
47969                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47970       if (light._depth>1 || light._spectrum<_spectrum)
47971         throw CImgArgumentException(_cimg_instance
47972                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47973                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47974       if (is_overlapped(texture))
47975         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47976       if (is_overlapped(light))
47977         return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47978 
47979       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
47980       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
47981       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
47982       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47983 
47984       const int
47985         w1 = width() - 1, h1 = height() - 1,
47986         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47987         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47988         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47989         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
47990         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
47991         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
47992         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
47993         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2,
47994         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
47995         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
47996         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
47997         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
47998 
47999       const ulongT
48000         twhd = (ulongT)texture._width*texture._height*texture._depth,
48001         lwhd = (ulongT)light._width*light._height*light._depth;
48002       cimg_init_scanline(opacity);
48003 
48004       for (int y = cy0; y<=cy2; ++y) {
48005         const int yy0 = y - y0, yy1 = y - y1;
48006         int
48007           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
48008           xM = x0 + (dx02*yy0 + hdy02)/dy02,
48009           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
48010           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
48011           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
48012           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02,
48013           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
48014           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
48015           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
48016           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
48017         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM);
48018         if (xM>=0 && xm<=w1) {
48019           const int
48020             cxm = cimg::cut(xm,0,w1),
48021             cxM = cimg::cut(xM,0,w1);
48022           T *ptrd = data(cxm,y);
48023           const int
48024             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
48025             dtxmM = txM - txm, dtymM = tyM - tym,
48026             dlxmM = lxM - lxm, dlymM = lyM - lym;
48027 
48028           for (int x = cxm; x<=cxM; ++x) {
48029             const int
48030               xxm = x - xm,
48031               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
48032               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM,
48033               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
48034               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
48035             const tc *const color = &texture._atXY(tx,ty);
48036             const tl *const lig = &light._atXY(lx,ly);
48037             cimg_forC(*this,c) {
48038               const tc col = color[c*twhd];
48039               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
48040               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
48041               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
48042             }
48043             ++ptrd;
48044           }
48045         }
48046       }
48047       return *this;
48048     }
48049 
48050     //! Draw a textured Phong-shaded 2D triangle, with perspective correction.
48051     template<typename tc, typename tl>
48052     CImg<T>& draw_triangle(int x0, int y0, const float z0,
48053                            int x1, int y1, const float z1,
48054                            int x2, int y2, const float z2,
48055                            const CImg<tc>& texture,
48056                            int tx0, int ty0,
48057                            int tx1, int ty1,
48058                            int tx2, int ty2,
48059                            const CImg<tl>& light,
48060                            int lx0, int ly0,
48061                            int lx1, int ly1,
48062                            int lx2, int ly2,
48063                            const float opacity=1) {
48064       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
48065       if (texture._depth>1 || texture._spectrum<_spectrum)
48066         throw CImgArgumentException(_cimg_instance
48067                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
48068                                     cimg_instance,
48069                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
48070       if (light._depth>1 || light._spectrum<_spectrum)
48071         throw CImgArgumentException(_cimg_instance
48072                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
48073                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
48074       if (is_overlapped(texture))
48075         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
48076                              light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
48077       if (is_overlapped(light))
48078         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
48079                              +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
48080 
48081       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
48082       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
48083       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
48084       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
48085       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
48086 
48087       const int
48088         w1 = width() - 1, h1 = height() - 1,
48089         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
48090         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
48091         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
48092         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
48093       const float
48094         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
48095         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
48096         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
48097         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
48098         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
48099         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
48100         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
48101         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
48102         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
48103 
48104       const ulongT
48105         twhd = (ulongT)texture._width*texture._height*texture._depth,
48106         lwhd = (ulongT)light._width*light._height*light._depth;
48107       cimg_init_scanline(opacity);
48108 
48109       for (int y = cy0; y<=cy2; ++y) {
48110         const int yy0 = y - y0, yy1 = y - y1;
48111         int
48112           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
48113           xM = x0 + (dx02*yy0 + hdy02)/dy02;
48114         float
48115           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
48116           izM = iz0 + diz02*yy0/dy02,
48117           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
48118           txzM = txz0 + dtxz02*yy0/dy02,
48119           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
48120           tyzM = tyz0 + dtyz02*yy0/dy02,
48121           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
48122           lxzM = lxz0 + dlxz02*yy0/dy02,
48123           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
48124           lyzM = lyz0 + dlyz02*yy0/dy02;
48125         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
48126         if (xM>=0 && xm<=w1) {
48127           const int
48128             cxm = cimg::cut(xm,0,w1),
48129             cxM = cimg::cut(xM,0,w1);
48130           T *ptrd = data(cxm,y);
48131           const int dxmM = std::max(1,xM - xm);
48132           const float
48133             dizmM = izM - izm,
48134             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
48135             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
48136 
48137           for (int x = cxm; x<=cxM; ++x) {
48138             const int xxm = x - xm;
48139             const float
48140               iz = izm + dizmM*xxm/dxmM,
48141               txz = txzm + dtxzmM*xxm/dxmM,
48142               tyz = tyzm + dtyzmM*xxm/dxmM,
48143               lxz = lxzm + dlxzmM*xxm/dxmM,
48144               lyz = lyzm + dlyzmM*xxm/dxmM;
48145             const int
48146               tx = (int)cimg::round(txz/iz),
48147               ty = (int)cimg::round(tyz/iz),
48148               lx = (int)cimg::round(lxz/iz),
48149               ly = (int)cimg::round(lyz/iz);
48150             const tc *const color = &texture._atXY(tx,ty);
48151             const tl *const lig = &light._atXY(lx,ly);
48152             cimg_forC(*this,c) {
48153               const tc col = color[c*twhd];
48154               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
48155               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
48156               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
48157             }
48158             ++ptrd;
48159           }
48160         }
48161       }
48162       return *this;
48163     }
48164 
48165     //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering.
48166     template<typename tz, typename tc, typename tl>
48167     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
48168                            int x0, int y0, const float z0,
48169                            int x1, int y1, const float z1,
48170                            int x2, int y2, const float z2,
48171                            const CImg<tc>& texture,
48172                            int tx0, int ty0,
48173                            int tx1, int ty1,
48174                            int tx2, int ty2,
48175                            const CImg<tl>& light,
48176                            int lx0, int ly0,
48177                            int lx1, int ly1,
48178                            int lx2, int ly2,
48179                            const float opacity=1) {
48180       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
48181       if (!is_sameXY(zbuffer))
48182         throw CImgArgumentException(_cimg_instance
48183                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
48184                                     "different dimensions.",
48185                                     cimg_instance,
48186                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
48187       if (texture._depth>1 || texture._spectrum<_spectrum)
48188         throw CImgArgumentException(_cimg_instance
48189                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
48190                                     cimg_instance,
48191                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
48192       if (light._depth>1 || light._spectrum<_spectrum)
48193         throw CImgArgumentException(_cimg_instance
48194                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
48195                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
48196       if (is_overlapped(texture))
48197         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
48198                              +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
48199       if (is_overlapped(light))
48200         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
48201                              texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
48202 
48203       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
48204       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
48205       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
48206       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
48207       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
48208 
48209       const int
48210         w1 = width() - 1, h1 = height() - 1,
48211         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
48212         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
48213         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
48214         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
48215       const float
48216         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
48217         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
48218         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
48219         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
48220         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
48221         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
48222         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
48223         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
48224         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
48225 
48226       const ulongT
48227         twhd = (ulongT)texture._width*texture._height*texture._depth,
48228         lwhd = (ulongT)light._width*light._height*light._depth;
48229       cimg_init_scanline(opacity);
48230 
48231       for (int y = cy0; y<=cy2; ++y) {
48232         const int yy0 = y - y0, yy1 = y - y1;
48233         int
48234           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
48235           xM = x0 + (dx02*yy0 + hdy02)/dy02;
48236         float
48237           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
48238           izM = iz0 + diz02*yy0/dy02,
48239           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
48240           txzM = txz0 + dtxz02*yy0/dy02,
48241           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
48242           tyzM = tyz0 + dtyz02*yy0/dy02,
48243           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
48244           lxzM = lxz0 + dlxz02*yy0/dy02,
48245           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
48246           lyzM = lyz0 + dlyz02*yy0/dy02;
48247         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
48248         if (xM>=0 && xm<=w1) {
48249           const int
48250             cxm = cimg::cut(xm,0,w1),
48251             cxM = cimg::cut(xM,0,w1);
48252           T *ptrd = data(cxm,y);
48253           tz *ptrz = zbuffer.data(cxm,y);
48254           const int dxmM = std::max(1,xM - xm);
48255           const float
48256             dizmM = izM - izm,
48257             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
48258             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
48259 
48260           for (int x = cxm; x<=cxM; ++x) {
48261             const int xxm = x - xm;
48262             const float iz = izm + dizmM*xxm/dxmM;
48263             if (iz>=*ptrz) {
48264               *ptrz = (tz)iz;
48265               const float
48266                 txz = txzm + dtxzmM*xxm/dxmM,
48267                 tyz = tyzm + dtyzmM*xxm/dxmM,
48268                 lxz = lxzm + dlxzmM*xxm/dxmM,
48269                 lyz = lyzm + dlyzmM*xxm/dxmM;
48270               const int
48271                 tx = (int)cimg::round(txz/iz),
48272                 ty = (int)cimg::round(tyz/iz),
48273                 lx = (int)cimg::round(lxz/iz),
48274                 ly = (int)cimg::round(lyz/iz);
48275               const tc *const color = &texture._atXY(tx,ty);
48276               const tl *const lig = &light._atXY(lx,ly);
48277               cimg_forC(*this,c) {
48278                 const tc col = color[c*twhd];
48279                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
48280                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
48281                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
48282               }
48283             }
48284             ++ptrd; ++ptrz;
48285           }
48286         }
48287       }
48288       return *this;
48289     }
48290 
48291     //! Draw a filled 4D rectangle.
48292     /**
48293        \param x0 X-coordinate of the upper-left rectangle corner.
48294        \param y0 Y-coordinate of the upper-left rectangle corner.
48295        \param z0 Z-coordinate of the upper-left rectangle corner.
48296        \param c0 C-coordinate of the upper-left rectangle corner.
48297        \param x1 X-coordinate of the lower-right rectangle corner.
48298        \param y1 Y-coordinate of the lower-right rectangle corner.
48299        \param z1 Z-coordinate of the lower-right rectangle corner.
48300        \param c1 C-coordinate of the lower-right rectangle corner.
48301        \param val Scalar value used to fill the rectangle area.
48302        \param opacity Drawing opacity.
48303     **/
48304     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
48305                             const int x1, const int y1, const int z1, const int c1,
48306                             const T val, const float opacity=1) {
48307       if (is_empty()) return *this;
48308       const int
48309         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
48310         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
48311         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
48312         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
48313       const int
48314         lx = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
48315         ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
48316         lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
48317         lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
48318       const ulongT
48319         offX = (ulongT)_width - lx,
48320         offY = (ulongT)_width*(_height - ly),
48321         offZ = (ulongT)_width*_height*(_depth - lz);
48322       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48323       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
48324       if (lx>0 && ly>0 && lz>0 && lc>0)
48325         for (int v = 0; v<lc; ++v) {
48326           for (int z = 0; z<lz; ++z) {
48327             for (int y = 0; y<ly; ++y) {
48328               if (opacity>=1) {
48329                 if (sizeof(T)!=1) { for (int x = 0; x<lx; ++x) *(ptrd++) = val; ptrd+=offX; }
48330                 else { std::memset(ptrd,(int)val,lx); ptrd+=_width; }
48331               } else { for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
48332             }
48333             ptrd+=offY;
48334           }
48335           ptrd+=offZ;
48336         }
48337       return *this;
48338     }
48339 
48340     //! Draw a filled 3D rectangle.
48341     /**
48342        \param x0 X-coordinate of the upper-left rectangle corner.
48343        \param y0 Y-coordinate of the upper-left rectangle corner.
48344        \param z0 Z-coordinate of the upper-left rectangle corner.
48345        \param x1 X-coordinate of the lower-right rectangle corner.
48346        \param y1 Y-coordinate of the lower-right rectangle corner.
48347        \param z1 Z-coordinate of the lower-right rectangle corner.
48348        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
48349        \param opacity Drawing opacity.
48350     **/
48351     template<typename tc>
48352     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
48353                             const int x1, const int y1, const int z1,
48354                             const tc *const color, const float opacity=1) {
48355       if (is_empty()) return *this;
48356       if (!color)
48357         throw CImgArgumentException(_cimg_instance
48358                                     "draw_rectangle(): Specified color is (null).",
48359                                     cimg_instance);
48360       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
48361       return *this;
48362     }
48363 
48364     //! Draw a filled 2D rectangle.
48365     /**
48366        \param x0 X-coordinate of the upper-left rectangle corner.
48367        \param y0 Y-coordinate of the upper-left rectangle corner.
48368        \param x1 X-coordinate of the lower-right rectangle corner.
48369        \param y1 Y-coordinate of the lower-right rectangle corner.
48370        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
48371        \param opacity Drawing opacity.
48372     **/
48373     template<typename tc>
48374     CImg<T>& draw_rectangle(const int x0, const int y0,
48375                             const int x1, const int y1,
48376                             const tc *const color, const float opacity=1) {
48377       return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
48378     }
48379 
48380     //! Draw a outlined 2D rectangle \overloading.
48381     template<typename tc>
48382     CImg<T>& draw_rectangle(const int x0, const int y0,
48383                             const int x1, const int y1,
48384                             const tc *const color, const float opacity,
48385                             const unsigned int pattern) {
48386       if (is_empty()) return *this;
48387       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
48388       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
48389       const int
48390         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
48391         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
48392       if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
48393                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
48394       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
48395         draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
48396         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
48397         draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
48398     }
48399 
48400     //! Draw a filled 2D polygon.
48401     /**
48402        \param points Set of polygon vertices.
48403        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
48404        \param opacity Drawing opacity.
48405      **/
48406     template<typename tp, typename tc>
48407     CImg<T>& draw_polygon(const CImg<tp>& points,
48408                           const tc *const color, const float opacity=1) {
48409       if (is_empty() || !points) return *this;
48410       if (!color)
48411         throw CImgArgumentException(_cimg_instance
48412                                     "draw_polygon(): Specified color is (null).",
48413                                     cimg_instance);
48414       if (points.height()!=2)
48415         throw CImgArgumentException(_cimg_instance
48416                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
48417                                     cimg_instance,
48418                                     points._width,points._height,points._depth,points._spectrum);
48419       if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity);
48420       if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
48421                                              cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity);
48422       if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
48423                                                  cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),
48424                                                  cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity);
48425       cimg_init_scanline(opacity);
48426       int
48427         xmin = 0, ymin = 0,
48428         xmax = points.get_shared_row(0).max_min(xmin),
48429         ymax = points.get_shared_row(1).max_min(ymin);
48430       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
48431       if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
48432 
48433       ymin = std::max(0,ymin);
48434       ymax = std::min(height() - 1,ymax);
48435       CImg<intT> Xs(points._width,ymax - ymin + 1);
48436       CImg<uintT> count(Xs._height,1,1,1,0);
48437       unsigned int n = 0, nn = 1;
48438       bool go_on = true;
48439 
48440       while (go_on) {
48441         unsigned int an = (nn + 1)%points._width;
48442         const int
48443           x0 = cimg::uiround(points(n,0)),
48444           y0 = cimg::uiround(points(n,1));
48445         if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
48446         const int
48447           x1 = cimg::uiround(points(nn,0)),
48448           y1 = cimg::uiround(points(nn,1));
48449         unsigned int tn = an;
48450         while (points(tn,1)==y1) (tn+=1)%=points._width;
48451 
48452         if (y0!=y1) {
48453           const int
48454             y2 = cimg::uiround(points(tn,1)),
48455             x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
48456             step = cimg::sign(y01),
48457             tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2,
48458             tend = tmax - (step==cimg::sign(y12));
48459           unsigned int y = (unsigned int)y0 - ymin;
48460           for (int t = 0; t<=tend; ++t, y+=step)
48461             if (y<Xs._height) Xs(count[y]++,y) = x0 + (t*x01 + htmax)/tmax;
48462         }
48463         go_on = nn>n;
48464         n = nn;
48465         nn = an;
48466       }
48467 
48468       cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512))
48469       cimg_forY(Xs,y) {
48470         const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
48471         int px = width();
48472         for (unsigned int k = 0; k<Xsy._width; k+=2) {
48473           int x0 = Xsy[k];
48474           const int x1 = Xsy[k + 1];
48475           x0+=x0==px;
48476           cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
48477           px = x1;
48478         }
48479       }
48480       return *this;
48481     }
48482 
48483     //! Draw a outlined 2D or 3D polygon \overloading.
48484     template<typename t, typename tc>
48485     CImg<T>& draw_polygon(const CImg<t>& points,
48486                           const tc *const color, const float opacity, const unsigned int pattern) {
48487       if (is_empty() || !points) return *this;
48488       if (!color)
48489         throw CImgArgumentException(_cimg_instance
48490                                     "draw_polygon(): Specified color is (null).",
48491                                     cimg_instance);
48492       if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
48493       if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
48494                                              (int)points(1,0),(int)points(1,1),color,opacity,pattern);
48495       bool ninit_hatch = true;
48496       switch (points._height) {
48497       case 0 : case 1 :
48498         throw CImgArgumentException(_cimg_instance
48499                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
48500                                     cimg_instance,
48501                                     points._width,points._height,points._depth,points._spectrum);
48502       default : {
48503         CImg<intT> npoints(points._width,2);
48504         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
48505         unsigned int nb_points = 1;
48506         for (unsigned int p = 1; p<points._width; ++p) {
48507           const int nx = (int)points(p,0), ny = (int)points(p,1);
48508           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
48509         }
48510         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
48511         int ox = x0, oy = y0;
48512         for (unsigned int i = 1; i<nb_points; ++i) {
48513           const int _x = (int)npoints(i,0), _y = (int)npoints(i,1);
48514           draw_line(ox,oy,_x,_y,color,opacity,pattern,ninit_hatch);
48515           ninit_hatch = false;
48516           ox = _x; oy = _y;
48517         }
48518         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
48519       }
48520       }
48521       return *this;
48522     }
48523 
48524     //! Draw a filled 2D ellipse.
48525     /**
48526        \param x0 X-coordinate of the ellipse center.
48527        \param y0 Y-coordinate of the ellipse center.
48528        \param r1 First radius of the ellipse.
48529        \param r2 Second radius of the ellipse.
48530        \param angle Angle of the first radius.
48531        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48532        \param opacity Drawing opacity.
48533     **/
48534     template<typename tc>
48535     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
48536                           const tc *const color, const float opacity=1) {
48537       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true);
48538     }
48539 
48540     //! Draw a filled 2D ellipse \overloading.
48541     /**
48542        \param x0 X-coordinate of the ellipse center.
48543        \param y0 Y-coordinate of the ellipse center.
48544        \param tensor Diffusion tensor describing the ellipse.
48545        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48546        \param opacity Drawing opacity.
48547     **/
48548     template<typename t, typename tc>
48549     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
48550                           const tc *const color, const float opacity=1) {
48551       CImgList<t> eig = tensor.get_symmetric_eigen();
48552       const CImg<t> &val = eig[0], &vec = eig[1];
48553       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
48554                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
48555                           color,opacity);
48556     }
48557 
48558     //! Draw an outlined 2D ellipse.
48559     /**
48560        \param x0 X-coordinate of the ellipse center.
48561        \param y0 Y-coordinate of the ellipse center.
48562        \param r1 First radius of the ellipse.
48563        \param r2 Second radius of the ellipse.
48564        \param angle Angle of the first radius.
48565        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48566        \param opacity Drawing opacity.
48567        \param pattern An integer whose bits describe the outline pattern.
48568     **/
48569     template<typename tc>
48570     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
48571                           const tc *const color, const float opacity, const unsigned int pattern) {
48572       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false);
48573       return *this;
48574     }
48575 
48576     //! Draw an outlined 2D ellipse \overloading.
48577     /**
48578        \param x0 X-coordinate of the ellipse center.
48579        \param y0 Y-coordinate of the ellipse center.
48580        \param tensor Diffusion tensor describing the ellipse.
48581        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48582        \param opacity Drawing opacity.
48583        \param pattern An integer whose bits describe the outline pattern.
48584     **/
48585     template<typename t, typename tc>
48586     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
48587                           const tc *const color, const float opacity,
48588                           const unsigned int pattern) {
48589       CImgList<t> eig = tensor.get_symmetric_eigen();
48590       const CImg<t> &val = eig[0], &vec = eig[1];
48591       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
48592                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
48593                           color,opacity,pattern);
48594     }
48595 
48596     template<typename tc>
48597     CImg<T>& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle,
48598                            const tc *const color, const float opacity,
48599                            const unsigned int pattern, const bool is_filled) {
48600       if (is_empty() || (!is_filled && !pattern)) return *this;
48601       const float radiusM = std::max(radius1,radius2);
48602       if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this;
48603       if (!color)
48604         throw CImgArgumentException(_cimg_instance
48605                                     "draw_ellipse(): Specified color is (null).",
48606                                     cimg_instance);
48607       const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2);
48608       if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity);
48609       if (iradius1==iradius2) {
48610         if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity);
48611         else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern);
48612       }
48613       const float ang = (float)(angle*cimg::PI/180);
48614 
48615       if (!is_filled) { // Outlined
48616         const float ca = std::cos(ang), sa = std::sin(ang);
48617         CImg<int> points((unsigned int)cimg::round(6*radiusM),2);
48618         cimg_forX(points,k) {
48619           const float
48620             _ang = (float)(2*cimg::PI*k/points._width),
48621             X = (float)(radius1*std::cos(_ang)),
48622             Y = (float)(radius2*std::sin(_ang));
48623           points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa));
48624           points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca));
48625         }
48626         draw_polygon(points,color,opacity,pattern);
48627       } else { // Filled
48628         cimg_init_scanline(opacity);
48629         const float
48630           ca = std::cos(ang),
48631           sa = -std::sin(ang),
48632           ca2 = ca*ca,
48633           sa2 = sa*sa,
48634           casa = ca*sa,
48635           i1 = 1/cimg::sqr(radius1),
48636           i2 = 1/cimg::sqr(radius2),
48637           t1 = i1*ca2 + i2*sa2,
48638           t2 = (i2 - i1)*casa,
48639           t3 = i2*ca2 + i1*sa2,
48640           t12 = t1*2;
48641         const int
48642           _ymin = (int)std::floor(y0 - radiusM),
48643           _ymax = (int)std::ceil(y0 + radiusM),
48644           ymin = _ymin<0?0:_ymin,
48645           ymax = _ymax>=height()?height() - 1:_ymax;
48646         for (int y = ymin; y<=ymax; ++y) {
48647           const float
48648             Y = y - y0 + 0.5f,
48649             B = 2*t2*Y,
48650             C = t3*Y*Y - 1,
48651             D = B*B - 4*t1*C;
48652           if (D>=0) {
48653             const float sD = std::sqrt(D);
48654             const int
48655               xmin = (int)(x0 + cimg::round((-B - sD)/t12)),
48656               xmax = (int)(x0 + cimg::round((-B + sD)/t12));
48657             cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
48658           }
48659         }
48660       }
48661       return *this;
48662     }
48663 
48664     //! Draw a filled 2D circle.
48665     /**
48666        \param x0 X-coordinate of the circle center.
48667        \param y0 Y-coordinate of the circle center.
48668        \param radius  Circle radius.
48669        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48670        \param opacity Drawing opacity.
48671        \note
48672        - Circle version of the Bresenham's algorithm is used.
48673     **/
48674     template<typename tc>
48675     CImg<T>& draw_circle(const int x0, const int y0, int radius,
48676                          const tc *const color, const float opacity=1) {
48677       if (is_empty()) return *this;
48678       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
48679       if (!color)
48680         throw CImgArgumentException(_cimg_instance
48681                                     "draw_circle(): Specified color is (null).",
48682                                     cimg_instance);
48683       if (!radius) return draw_point(x0,y0,color,opacity);
48684       cimg_init_scanline(opacity);
48685       if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
48686       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
48687         if (f>=0) {
48688           const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
48689           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
48690           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
48691           f+=(ddFy+=2); --y;
48692         }
48693         const bool no_diag = y!=(x++);
48694         ++(f+=(ddFx+=2));
48695         const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
48696         if (no_diag) {
48697           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
48698           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
48699         }
48700       }
48701       return *this;
48702     }
48703 
48704     //! Draw an outlined 2D circle.
48705     /**
48706        \param x0 X-coordinate of the circle center.
48707        \param y0 Y-coordinate of the circle center.
48708        \param radius Circle radius.
48709        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48710        \param opacity Drawing opacity.
48711        \param pattern An integer whose bits describe the outline pattern.
48712     **/
48713     template<typename tc>
48714     CImg<T>& draw_circle(const int x0, const int y0, int radius,
48715                          const tc *const color, const float opacity,
48716                          const unsigned int pattern) {
48717       if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern);
48718       if (is_empty()) return *this;
48719       if (!color)
48720         throw CImgArgumentException(_cimg_instance
48721                                     "draw_circle(): Specified color is (null).",
48722                                     cimg_instance);
48723       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
48724       if (!radius) return draw_point(x0,y0,color,opacity);
48725 
48726       draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
48727         draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
48728       if (radius==1) return *this;
48729       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
48730         if (f>=0) { f+=(ddFy+=2); --y; }
48731         ++x; ++(f+=(ddFx+=2));
48732         if (x!=y + 1) {
48733           const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
48734             x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
48735           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
48736             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
48737           if (x!=y)
48738             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
48739               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
48740         }
48741       }
48742       return *this;
48743     }
48744 
48745     //! Draw an image.
48746     /**
48747        \param sprite Sprite image.
48748        \param x0 X-coordinate of the sprite position.
48749        \param y0 Y-coordinate of the sprite position.
48750        \param z0 Z-coordinate of the sprite position.
48751        \param c0 C-coordinate of the sprite position.
48752        \param opacity Drawing opacity.
48753     **/
48754     template<typename t>
48755     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48756                         const CImg<t>& sprite, const float opacity=1) {
48757       if (is_empty() || !sprite) return *this;
48758       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
48759       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
48760         return assign(sprite,false);
48761       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48762       const int
48763         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48764         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48765         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48766         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48767         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48768         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48769 
48770       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48771       if (lx>0 && ly>0 && lz>0 && lc>0) {
48772         for (int c = 0; c<lc; ++c)
48773           for (int z = 0; z<lz; ++z)
48774             for (int y = 0; y<ly; ++y) {
48775               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48776               const t *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48777               if (opacity>=1) for (int x = 0; x<lx; ++x) *(ptrd++) = (T)*(ptrs++);
48778               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
48779             }
48780       }
48781       return *this;
48782     }
48783 
48784     //! Draw an image \specialization.
48785     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48786                         const CImg<T>& sprite, const float opacity=1) {
48787       if (is_empty() || !sprite) return *this;
48788       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
48789       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
48790         return assign(sprite,false);
48791       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48792       const int
48793         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48794         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48795         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48796         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48797         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48798         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48799       const ulongT slx = lx*sizeof(T);
48800 
48801       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48802       if (lx>0 && ly>0 && lz>0 && lc>0) {
48803         for (int c = 0; c<lc; ++c)
48804           for (int z = 0; z<lz; ++z)
48805             for (int y = 0; y<ly; ++y) {
48806               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48807               const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48808               if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
48809               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
48810             }
48811       }
48812       return *this;
48813     }
48814 
48815     //! Draw an image \overloading.
48816     template<typename t>
48817     CImg<T>& draw_image(const int x0, const int y0, const int z0,
48818                         const CImg<t>& sprite, const float opacity=1) {
48819       return draw_image(x0,y0,z0,0,sprite,opacity);
48820     }
48821 
48822     //! Draw an image \overloading.
48823     template<typename t>
48824     CImg<T>& draw_image(const int x0, const int y0,
48825                         const CImg<t>& sprite, const float opacity=1) {
48826       return draw_image(x0,y0,0,sprite,opacity);
48827     }
48828 
48829     //! Draw an image \overloading.
48830     template<typename t>
48831     CImg<T>& draw_image(const int x0,
48832                         const CImg<t>& sprite, const float opacity=1) {
48833       return draw_image(x0,0,sprite,opacity);
48834     }
48835 
48836     //! Draw an image \overloading.
48837     template<typename t>
48838     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
48839       return draw_image(0,sprite,opacity);
48840     }
48841 
48842     //! Draw a masked image.
48843     /**
48844        \param sprite Sprite image.
48845        \param mask Mask image.
48846        \param x0 X-coordinate of the sprite position in the image instance.
48847        \param y0 Y-coordinate of the sprite position in the image instance.
48848        \param z0 Z-coordinate of the sprite position in the image instance.
48849        \param c0 C-coordinate of the sprite position in the image instance.
48850        \param mask_max_value Maximum pixel value of the mask image \c mask.
48851        \param opacity Drawing opacity.
48852        \note
48853        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
48854        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
48855     **/
48856     template<typename ti, typename tm>
48857     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
48858                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48859                         const float mask_max_value=1) {
48860       if (is_empty() || !sprite || !mask) return *this;
48861       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
48862       if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
48863       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
48864         throw CImgArgumentException(_cimg_instance
48865                                     "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
48866                                     "incompatible dimensions.",
48867                                     cimg_instance,
48868                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
48869                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
48870 
48871       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
48872       const int
48873         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
48874         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
48875         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
48876         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
48877         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
48878         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
48879       const ulongT msize = mask.size();
48880 
48881       if (lx>0 && ly>0 && lz>0 && lc>0) {
48882         for (int c = 0; c<lc; ++c)
48883           for (int z = 0; z<lz; ++z)
48884             for (int y = 0; y<ly; ++y) {
48885               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
48886               const ti *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
48887               const tm *ptrm = mask._data + (mask.offset(sx0,sy0 + y,sz0 + z,sc0 + c)%msize);
48888               for (int x = 0; x<lx; ++x) {
48889                 const float mopacity = (float)(*(ptrm++)*opacity),
48890                   nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f);
48891                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
48892                 ++ptrd;
48893               }
48894             }
48895       }
48896       return *this;
48897     }
48898 
48899     //! Draw a masked image \overloading.
48900     template<typename ti, typename tm>
48901     CImg<T>& draw_image(const int x0, const int y0, const int z0,
48902                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48903                         const float mask_max_value=1) {
48904       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
48905     }
48906 
48907     //! Draw a image \overloading.
48908     template<typename ti, typename tm>
48909     CImg<T>& draw_image(const int x0, const int y0,
48910                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48911                         const float mask_max_value=1) {
48912       return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
48913     }
48914 
48915     //! Draw a image \overloading.
48916     template<typename ti, typename tm>
48917     CImg<T>& draw_image(const int x0,
48918                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48919                         const float mask_max_value=1) {
48920       return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
48921     }
48922 
48923     //! Draw an image.
48924     template<typename ti, typename tm>
48925     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
48926                         const float mask_max_value=1) {
48927       return draw_image(0,sprite,mask,opacity,mask_max_value);
48928     }
48929 
48930     //! Draw a text string.
48931     /**
48932        \param x0 X-coordinate of the text in the image instance.
48933        \param y0 Y-coordinate of the text in the image instance.
48934        \param text Format of the text ('printf'-style format string).
48935        \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
48936        \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
48937        \param opacity Drawing opacity.
48938        \param font Font used for drawing text.
48939     **/
48940     template<typename tc1, typename tc2, typename t>
48941     CImg<T>& draw_text(const int x0, const int y0,
48942                        const char *const text,
48943                        const tc1 *const foreground_color, const tc2 *const background_color,
48944                        const float opacity, const CImgList<t>& font, ...) {
48945       if (!font) return *this;
48946       CImg<charT> tmp(2048);
48947       std::va_list ap; va_start(ap,font);
48948       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48949       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
48950     }
48951 
48952     //! Draw a text string \overloading.
48953     /**
48954        \note A transparent background is used for the text.
48955     **/
48956     template<typename tc, typename t>
48957     CImg<T>& draw_text(const int x0, const int y0,
48958                        const char *const text,
48959                        const tc *const foreground_color, const int,
48960                        const float opacity, const CImgList<t>& font, ...) {
48961       if (!font) return *this;
48962       CImg<charT> tmp(2048);
48963       std::va_list ap; va_start(ap,font);
48964       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48965       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
48966     }
48967 
48968     //! Draw a text string \overloading.
48969     /**
48970        \note A transparent foreground is used for the text.
48971     **/
48972     template<typename tc, typename t>
48973     CImg<T>& draw_text(const int x0, const int y0,
48974                        const char *const text,
48975                        const int, const tc *const background_color,
48976                        const float opacity, const CImgList<t>& font, ...) {
48977       if (!font) return *this;
48978       CImg<charT> tmp(2048);
48979       std::va_list ap; va_start(ap,font);
48980       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48981       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
48982     }
48983 
48984     //! Draw a text string \overloading.
48985     /**
48986        \param x0 X-coordinate of the text in the image instance.
48987        \param y0 Y-coordinate of the text in the image instance.
48988        \param text Format of the text ('printf'-style format string).
48989        \param foreground_color Array of spectrum() values of type \c T,
48990          defining the foreground color (0 means 'transparent').
48991        \param background_color Array of spectrum() values of type \c T,
48992          defining the background color (0 means 'transparent').
48993        \param opacity Drawing opacity.
48994        \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise).
48995     **/
48996     template<typename tc1, typename tc2>
48997     CImg<T>& draw_text(const int x0, const int y0,
48998                        const char *const text,
48999                        const tc1 *const foreground_color, const tc2 *const background_color,
49000                        const float opacity=1, const unsigned int font_height=13, ...) {
49001       if (!font_height) return *this;
49002       CImg<charT> tmp(2048);
49003       std::va_list ap; va_start(ap,font_height);
49004       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
49005       const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
49006       _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
49007       return *this;
49008     }
49009 
49010     //! Draw a text string \overloading.
49011     template<typename tc>
49012     CImg<T>& draw_text(const int x0, const int y0,
49013                        const char *const text,
49014                        const tc *const foreground_color, const int background_color=0,
49015                        const float opacity=1, const unsigned int font_height=13, ...) {
49016       if (!font_height) return *this;
49017       cimg::unused(background_color);
49018       CImg<charT> tmp(2048);
49019       std::va_list ap; va_start(ap,font_height);
49020       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
49021       return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
49022     }
49023 
49024     //! Draw a text string \overloading.
49025     template<typename tc>
49026     CImg<T>& draw_text(const int x0, const int y0,
49027                        const char *const text,
49028                        const int, const tc *const background_color,
49029                        const float opacity=1, const unsigned int font_height=13, ...) {
49030       if (!font_height) return *this;
49031       CImg<charT> tmp(2048);
49032       std::va_list ap; va_start(ap,font_height);
49033       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
49034       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
49035     }
49036 
49037     template<typename tc1, typename tc2, typename t>
49038     CImg<T>& _draw_text(const int x0, const int y0,
49039                         const char *const text,
49040                         const tc1 *const foreground_color, const tc2 *const background_color,
49041                         const float opacity, const CImgList<t>& font,
49042                         const bool is_native_font) {
49043       if (!text) return *this;
49044       if (!font)
49045         throw CImgArgumentException(_cimg_instance
49046                                     "draw_text(): Empty specified font.",
49047                                     cimg_instance);
49048 
49049       const unsigned int text_length = (unsigned int)std::strlen(text);
49050       const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
49051       unsigned char o_ch, ch = 0;
49052       int x, y, w;
49053       CImg<intT> left_paddings(text_length,1,1,1,0);
49054       const CImg<t> empty = CImg<t>::empty();
49055 
49056       if (is_empty() || is_native_font) {
49057         // Pre-compute necessary size of the image as well as left paddings of each character.
49058         x = y = w = 0;
49059         o_ch = 0;
49060         for (unsigned int i = 0; i<text_length; ++i) {
49061           ch = (unsigned char)text[i];
49062           switch (ch) {
49063           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
49064           case '\t' : x+=4*font[(int)' ']._width; break;
49065           case ' ' : x+=font[(int)' ']._width; break;
49066           default : if (ch<font._width) {
49067               int left_padding = 0;
49068               if (is_native_font && font[0]._height<128) {
49069                 // Determine left padding from various rules.
49070                 if (ch==':' || ch=='!' || ch=='.' || ch==';')
49071                   left_padding = 2*padding_x;
49072                 else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
49073                   left_padding = 4*padding_x;
49074                 else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
49075                           ((ch>='0' && ch<='9') ||
49076                            (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
49077                            (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
49078                          o_ch=='.' || o_ch=='\'' || ch=='\'')
49079                   left_padding = padding_x;
49080                 else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
49081                   const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
49082                   if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
49083                     const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
49084                     if (o_mask._height>13) {
49085                       const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
49086                       left_padding = -10;
49087                       cimg_forY(mask,k) {
49088                         const int
49089                           lpad = o_mask(w1,k)>=8?0:
49090                                  o_mask._width<=2 || o_mask(w2,k)>=8?-1:
49091                                  o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
49092                           rpad = mask(0,k)>=8?0:
49093                                  mask._width<=2 || mask(1,k)>=8?-1:
49094                                  mask._width<=3 || mask(2,k)>=8?-2:-3;
49095                         left_padding = std::max(left_padding,lpad + rpad);
49096                       }
49097                     }
49098                   }
49099                 }
49100                 left_paddings[i] = left_padding;
49101               }
49102               x+=left_padding + font[ch]._width + padding_x;
49103               o_ch = ch;
49104             }
49105           }
49106         }
49107         if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
49108         if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
49109       }
49110 
49111       // Draw font characters on image.
49112       x = x0; y = y0;
49113       for (unsigned int i = 0; i<text_length; ++i) {
49114         ch = (unsigned char)text[i];
49115         switch (ch) {
49116         case '\n' : y+=font[0]._height; x = x0; break;
49117         case '\t' :
49118         case ' ' : {
49119           const unsigned int lw = (ch=='\t'?4:1)*font[(int)' ']._width, lh = font[(int)' ']._height;
49120           if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
49121           x+=lw;
49122         } break;
49123         default : if (ch<font._width) {
49124             CImg<T> letter = font[ch];
49125             if (letter) {
49126               const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
49127               const int posx = x + left_paddings[i] + padding_x;
49128               if (is_native_font && _spectrum>letter._spectrum)
49129                 letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false);
49130               const unsigned int cmin = std::min(_spectrum,letter._spectrum);
49131               if (foreground_color)
49132                 for (unsigned int c = 0; c<cmin; ++c)
49133                   if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
49134               if (mask) { // Letter has mask
49135                 if (background_color)
49136                   for (unsigned int c = 0; c<cmin; ++c)
49137                     draw_rectangle(x,y,0,c,posx + letter._width - 1,y + letter._height - 1,0,c,
49138                                    background_color[c],opacity);
49139                 draw_image(posx,y,letter,font[ch + 256],opacity,255.f);
49140               } else draw_image(posx,y,letter,opacity); // Letter has no mask
49141               x = posx + letter._width;
49142             }
49143           }
49144         }
49145       }
49146       return *this;
49147     }
49148 
49149     // [internal] Version used to display text in interactive viewers.
49150     CImg<T>& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) {
49151       CImg<charT> tmp(2048);
49152       std::va_list ap;
49153       va_start(ap,is_down);
49154       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
49155       CImg<ucharT> a_label, a_labelmask;
49156       const unsigned char a_labelcolor = 255;
49157       unsigned int ofs = font_size, fs = ofs;
49158       do { // Determine best font size
49159         a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data);
49160         if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) {
49161           font_size = fs; break;
49162         } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) {
49163           ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f));
49164         } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) {
49165           ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f));
49166         } else { font_size = fs; break; }
49167       } while (true);
49168       a_label.normalize(0,255);
49169       a_label+=(255 - a_label.get_dilate(3)).normalize(0,80);
49170       a_label.resize(-100,-100,1,3,1);
49171       return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f);
49172     }
49173 
49174     //! Draw a 2D vector field.
49175     /**
49176        \param flow Image of 2D vectors used as input data.
49177        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49178        \param opacity Drawing opacity.
49179        \param sampling Length (in pixels) between each arrow.
49180        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
49181        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
49182        \param pattern Used pattern to draw lines.
49183        \note Clipping is supported.
49184     **/
49185     template<typename t1, typename t2>
49186     CImg<T>& draw_quiver(const CImg<t1>& flow,
49187                          const t2 *const color, const float opacity=1,
49188                          const unsigned int sampling=25, const float factor=-20,
49189                          const bool is_arrow=true, const unsigned int pattern=~0U) {
49190       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
49191     }
49192 
49193     //! Draw a 2D vector field, using a field of colors.
49194     /**
49195        \param flow Image of 2D vectors used as input data.
49196        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
49197        \param opacity Opacity of the drawing.
49198        \param sampling Length (in pixels) between each arrow.
49199        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
49200        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
49201        \param pattern Used pattern to draw lines.
49202        \note Clipping is supported.
49203     **/
49204     template<typename t1, typename t2>
49205     CImg<T>& draw_quiver(const CImg<t1>& flow,
49206                          const CImg<t2>& color, const float opacity=1,
49207                          const unsigned int sampling=25, const float factor=-20,
49208                          const bool is_arrow=true, const unsigned int pattern=~0U) {
49209       if (is_empty()) return *this;
49210       if (!flow || flow._spectrum!=2)
49211         throw CImgArgumentException(_cimg_instance
49212                                     "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
49213                                     cimg_instance,
49214                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
49215       if (sampling<=0)
49216         throw CImgArgumentException(_cimg_instance
49217                                     "draw_quiver(): Invalid sampling value %g "
49218                                     "(should be >0)",
49219                                     cimg_instance,
49220                                     sampling);
49221       const bool colorfield = (color._width==flow._width && color._height==flow._height &&
49222                                color._depth==1 && color._spectrum==_spectrum);
49223       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
49224       float vmax,fact;
49225       if (factor<=0) {
49226         float m, M = (float)flow.get_norm(2).max_min(m);
49227         vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
49228         if (!vmax) vmax = 1;
49229         fact = -factor;
49230       } else { fact = factor; vmax = 1; }
49231 
49232       for (unsigned int y = sampling/2; y<_height; y+=sampling)
49233         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
49234           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
49235           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
49236           if (is_arrow) {
49237             const int xx = (int)(x + u), yy = (int)(y + v);
49238             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern);
49239             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern);
49240           } else {
49241             if (colorfield)
49242               draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
49243                         color.get_vector_at(X,Y)._data,opacity,pattern);
49244             else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
49245                            color._data,opacity,pattern);
49246           }
49247         }
49248       return *this;
49249     }
49250 
49251     //! Draw a labeled horizontal axis.
49252     /**
49253        \param values_x Values along the horizontal axis.
49254        \param y Y-coordinate of the horizontal axis in the image instance.
49255        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49256        \param opacity Drawing opacity.
49257        \param pattern Drawing pattern.
49258        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
49259        \param allow_zero Enable/disable the drawing of label '0' if found.
49260     **/
49261     template<typename t, typename tc>
49262     CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
49263                        const tc *const color, const float opacity=1,
49264                        const unsigned int pattern=~0U, const unsigned int font_height=13,
49265                        const bool allow_zero=true, const float round_x=0) {
49266       if (is_empty()) return *this;
49267       const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
49268       const int siz = (int)values_x.size() - 1;
49269       CImg<charT> txt(32);
49270       CImg<T> a_label;
49271       if (siz<=0) { // Degenerated case
49272         draw_line(0,y,_width - 1,y,color,opacity,pattern);
49273         if (!siz) {
49274           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x);
49275           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
49276           const int
49277             _xt = (width() - a_label.width())/2,
49278             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
49279           draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
49280           if (allow_zero || *txt!='0' || txt[1]!=0)
49281             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
49282         }
49283       } else { // Regular case
49284         if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
49285         else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
49286         cimg_foroff(values_x,x) {
49287           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)values_x(x),round_x):(double)values_x(x));
49288           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
49289           const int
49290             xi = (int)(x*(_width - 1)/siz),
49291             _xt = xi - a_label.width()/2,
49292             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
49293           draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
49294           if (allow_zero || *txt!='0' || txt[1]!=0)
49295             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
49296         }
49297       }
49298       return *this;
49299     }
49300 
49301     //! Draw a labeled vertical axis.
49302     /**
49303        \param x X-coordinate of the vertical axis in the image instance.
49304        \param values_y Values along the Y-axis.
49305        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49306        \param opacity Drawing opacity.
49307        \param pattern Drawing pattern.
49308        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
49309        \param allow_zero Enable/disable the drawing of label '0' if found.
49310     **/
49311     template<typename t, typename tc>
49312     CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
49313                        const tc *const color, const float opacity=1,
49314                        const unsigned int pattern=~0U, const unsigned int font_height=13,
49315                        const bool allow_zero=true, const float round_y=0) {
49316       if (is_empty()) return *this;
49317       int siz = (int)values_y.size() - 1;
49318       CImg<charT> txt(32);
49319       CImg<T> a_label;
49320       if (siz<=0) { // Degenerated case
49321         draw_line(x,0,x,_height - 1,color,opacity,pattern);
49322         if (!siz) {
49323           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y);
49324           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
49325           const int
49326             _yt = (height() - a_label.height())/2,
49327             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
49328             _xt = x - 2 - a_label.width(),
49329             xt = _xt>=0?_xt:x + 3;
49330           draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
49331           if (allow_zero || *txt!='0' || txt[1]!=0)
49332             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
49333         }
49334       } else { // Regular case
49335         if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
49336         else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
49337         cimg_foroff(values_y,y) {
49338           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)values_y(y),round_y):(double)values_y(y));
49339           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
49340           const int
49341             yi = (int)(y*(_height - 1)/siz),
49342             _yt = yi - a_label.height()/2,
49343             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
49344             _xt = x - 2 - a_label.width(),
49345             xt = _xt>=0?_xt:x + 3;
49346           draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
49347           if (allow_zero || *txt!='0' || txt[1]!=0)
49348             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
49349         }
49350       }
49351       return *this;
49352     }
49353 
49354     //! Draw labeled horizontal and vertical axes.
49355     /**
49356        \param values_x Values along the X-axis.
49357        \param values_y Values along the Y-axis.
49358        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49359        \param opacity Drawing opacity.
49360        \param pattern_x Drawing pattern for the X-axis.
49361        \param pattern_y Drawing pattern for the Y-axis.
49362        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
49363        \param allow_zero Enable/disable the drawing of label '0' if found.
49364     **/
49365     template<typename tx, typename ty, typename tc>
49366     CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
49367                        const tc *const color, const float opacity=1,
49368                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
49369                        const unsigned int font_height=13, const bool allow_zero=true,
49370                        const float round_x=0, const float round_y=0) {
49371       if (is_empty()) return *this;
49372       const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
49373       const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
49374       if (sizx>=0) {
49375         float ox = (float)*nvalues_x;
49376         for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
49377           const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
49378           if (nx*ox<=0) {
49379             draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y);
49380             break;
49381           }
49382           ox = nx;
49383         }
49384       }
49385       const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
49386       const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
49387       if (sizy>0) {
49388         float oy = (float)nvalues_y[0];
49389         for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
49390           const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
49391           if (ny*oy<=0) {
49392             draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x);
49393             break;
49394           }
49395           oy = ny;
49396         }
49397       }
49398       return *this;
49399     }
49400 
49401     //! Draw labeled horizontal and vertical axes \overloading.
49402     template<typename tc>
49403     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
49404                        const tc *const color, const float opacity=1,
49405                        const int subdivisionx=-60, const int subdivisiony=-60,
49406                        const float precisionx=0, const float precisiony=0,
49407                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
49408                        const unsigned int font_height=13) {
49409       if (is_empty()) return *this;
49410       const bool allow_zero = (x0*x1>0) || (y0*y1>0);
49411       const float
49412         dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
49413         px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx,
49414         py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony;
49415       if (x0!=x1 && y0!=y1)
49416         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),
49417                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
49418                   color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py);
49419       else if (x0==x1 && y0!=y1)
49420         draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
49421                   color,opacity,pattern_y,font_height,py);
49422       else if (x0!=x1 && y0==y1)
49423         draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0,
49424                   color,opacity,pattern_x,font_height,px);
49425       return *this;
49426     }
49427 
49428     //! Draw 2D grid.
49429     /**
49430        \param values_x X-coordinates of the vertical lines.
49431        \param values_y Y-coordinates of the horizontal lines.
49432        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49433        \param opacity Drawing opacity.
49434        \param pattern_x Drawing pattern for vertical lines.
49435        \param pattern_y Drawing pattern for horizontal lines.
49436     **/
49437     template<typename tx, typename ty, typename tc>
49438     CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
49439                        const tc *const color, const float opacity=1,
49440                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
49441       if (is_empty()) return *this;
49442       if (values_x) cimg_foroff(values_x,x) {
49443           const int xi = (int)values_x[x];
49444           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
49445         }
49446       if (values_y) cimg_foroff(values_y,y) {
49447           const int yi = (int)values_y[y];
49448           if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
49449         }
49450       return *this;
49451     }
49452 
49453     //! Draw 2D grid \simplification.
49454     template<typename tc>
49455     CImg<T>& draw_grid(const float delta_x,  const float delta_y,
49456                        const float offsetx, const float offsety,
49457                        const bool invertx, const bool inverty,
49458                        const tc *const color, const float opacity=1,
49459                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
49460       if (is_empty()) return *this;
49461       CImg<uintT> seqx, seqy;
49462       if (delta_x!=0) {
49463         const float dx = delta_x>0?delta_x:_width*-delta_x/100;
49464         const unsigned int nx = (unsigned int)(_width/dx);
49465         seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
49466         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
49467         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
49468       }
49469       if (delta_y!=0) {
49470         const float dy = delta_y>0?delta_y:_height*-delta_y/100;
49471         const unsigned int ny = (unsigned int)(_height/dy);
49472         seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
49473         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
49474         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
49475      }
49476       return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
49477     }
49478 
49479     //! Draw 1D graph.
49480     /**
49481        \param data Image containing the graph values I = f(x).
49482        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49483        \param opacity Drawing opacity.
49484 
49485        \param plot_type Define the type of the plot:
49486                       - 0 = No plot.
49487                       - 1 = Plot using segments.
49488                       - 2 = Plot using cubic splines.
49489                       - 3 = Plot with bars.
49490        \param vertex_type Define the type of points:
49491                       - 0 = No points.
49492                       - 1 = Point.
49493                       - 2 = Straight cross.
49494                       - 3 = Diagonal cross.
49495                       - 4 = Filled circle.
49496                       - 5 = Outlined circle.
49497                       - 6 = Square.
49498                       - 7 = Diamond.
49499        \param ymin Lower bound of the y-range.
49500        \param ymax Upper bound of the y-range.
49501        \param pattern Drawing pattern.
49502        \note
49503          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
49504     **/
49505     template<typename t, typename tc>
49506     CImg<T>& draw_graph(const CImg<t>& data,
49507                         const tc *const color, const float opacity=1,
49508                         const unsigned int plot_type=1, const int vertex_type=1,
49509                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
49510       if (is_empty() || _height<=1) return *this;
49511       if (!color)
49512         throw CImgArgumentException(_cimg_instance
49513                                     "draw_graph(): Specified color is (null).",
49514                                     cimg_instance);
49515 
49516       // Create shaded colors for displaying bar plots.
49517       CImg<tc> color1, color2;
49518       if (plot_type==3) {
49519         color1.assign(_spectrum); color2.assign(_spectrum);
49520         cimg_forC(*this,c) {
49521           color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
49522           color2[c] = (tc)(color[c]*0.4f);
49523         }
49524       }
49525 
49526       // Compute min/max and normalization factors.
49527       const ulongT
49528         siz = data.size(),
49529         _siz1 = siz - (plot_type!=3),
49530         siz1 = _siz1?_siz1:1;
49531       const unsigned int
49532         _width1 = _width - (plot_type!=3),
49533         width1 = _width1?_width1:1;
49534       double m = ymin, M = ymax;
49535       if (ymin==ymax) m = (double)data.max_min(M);
49536       if (m==M) { --m; ++M; }
49537       const float ca = (float)(M-m)/(_height - 1);
49538       bool init_hatch = true;
49539 
49540       // Draw graph edges
49541       switch (plot_type%4) {
49542       case 1 : { // Segments
49543         int oX = 0, oY = (int)cimg::round((data[0] - m)/ca);
49544         if (siz==1) {
49545           const int Y = (int)cimg::round((*data - m)/ca);
49546           draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
49547         } else {
49548           const float fx = (float)_width/siz1;
49549           for (ulongT off = 1; off<siz; ++off) {
49550             const int
49551               X = (int)cimg::round(off*fx) - 1,
49552               Y = (int)cimg::round((data[off]-m)/ca);
49553             draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
49554             oX = X; oY = Y;
49555             init_hatch = false;
49556           }
49557         }
49558       } break;
49559       case 2 : { // Spline
49560         const CImg<t> ndata(data._data,siz,1,1,1,true);
49561         int oY = (int)cimg::round((data[0] - m)/ca);
49562         cimg_forX(*this,x) {
49563           const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
49564           if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
49565           init_hatch = false;
49566           oY = Y;
49567         }
49568       } break;
49569       case 3 : { // Bars
49570         const int Y0 = (int)cimg::round(-m/ca);
49571         const float fx = (float)_width/siz1;
49572         int oX = 0;
49573         cimg_foroff(data,off) {
49574           const int
49575             X = (int)cimg::round((off + 1)*fx) - 1,
49576             Y = (int)cimg::round((data[off] - m)/ca);
49577           draw_rectangle(oX,Y0,X,Y,color,opacity).
49578             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
49579             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
49580             draw_line(X,Y,X,Y0,color1.data(),opacity).
49581             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
49582           oX = X + 1;
49583         }
49584       } break;
49585       default : break; // No edges
49586       }
49587 
49588       // Draw graph points
49589       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
49590       const float fx = (float)_width1/siz1;
49591       switch (vertex_type%8) {
49592       case 1 : { // Point
49593         cimg_foroff(data,off) {
49594           const int
49595             X = (int)cimg::round(off*fx + wb2),
49596             Y = (int)cimg::round((data[off]-m)/ca);
49597           draw_point(X,Y,color,opacity);
49598         }
49599       } break;
49600       case 2 : { // Straight Cross
49601         cimg_foroff(data,off) {
49602           const int
49603             X = (int)cimg::round(off*fx + wb2),
49604             Y = (int)cimg::round((data[off]-m)/ca);
49605           draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
49606         }
49607       } break;
49608       case 3 : { // Diagonal Cross
49609         cimg_foroff(data,off) {
49610           const int
49611             X = (int)cimg::round(off*fx + wb2),
49612             Y = (int)cimg::round((data[off]-m)/ca);
49613           draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
49614         }
49615       } break;
49616       case 4 : { // Filled Circle
49617         cimg_foroff(data,off) {
49618           const int
49619             X = (int)cimg::round(off*fx + wb2),
49620             Y = (int)cimg::round((data[off]-m)/ca);
49621           draw_circle(X,Y,3,color,opacity);
49622         }
49623       } break;
49624       case 5 : { // Outlined circle
49625         cimg_foroff(data,off) {
49626           const int
49627             X = (int)cimg::round(off*fx + wb2),
49628             Y = (int)cimg::round((data[off]-m)/ca);
49629           draw_circle(X,Y,3,color,opacity,~0U);
49630         }
49631       } break;
49632       case 6 : { // Square
49633         cimg_foroff(data,off) {
49634           const int
49635             X = (int)cimg::round(off*fx + wb2),
49636             Y = (int)cimg::round((data[off]-m)/ca);
49637           draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
49638         }
49639       } break;
49640       case 7 : { // Diamond
49641         cimg_foroff(data,off) {
49642           const int
49643             X = (int)cimg::round(off*fx + wb2),
49644             Y = (int)cimg::round((data[off]-m)/ca);
49645           draw_line(X,Y - 4,X + 4,Y,color,opacity).
49646             draw_line(X + 4,Y,X,Y + 4,color,opacity).
49647             draw_line(X,Y + 4,X - 4,Y,color,opacity).
49648             draw_line(X - 4,Y,X,Y - 4,color,opacity);
49649         }
49650       } break;
49651       default : break; // No points
49652       }
49653       return *this;
49654     }
49655 
49656     bool _draw_fill(const int x, const int y, const int z,
49657                     const CImg<T>& ref, const float tolerance2) const {
49658       const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
49659       const ulongT off = _width*_height*_depth;
49660       float diff = 0;
49661       cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
49662       return diff<=tolerance2;
49663     }
49664 
49665     //! Draw filled 3D region with the flood fill algorithm.
49666     /**
49667        \param x0 X-coordinate of the starting point of the region to fill.
49668        \param y0 Y-coordinate of the starting point of the region to fill.
49669        \param z0 Z-coordinate of the starting point of the region to fill.
49670        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49671        \param[out] region Image that will contain the mask of the filled region mask, as an output.
49672        \param tolerance Tolerance concerning neighborhood values.
49673        \param opacity Opacity of the drawing.
49674        \param is_high_connectivity Tells if 8-connexity must be used.
49675        \return \c region is initialized with the binary mask of the filled region.
49676     **/
49677     template<typename tc, typename t>
49678     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
49679                         const tc *const color, const float opacity,
49680                         CImg<t> &region,
49681                         const float tolerance = 0,
49682                         const bool is_high_connectivity = false) {
49683 #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
49684                                stack[N] = x; stack(N,1) = y; stack(N++,2) = z
49685 #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
49686 #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
49687 
49688       if (!containsXYZC(x0,y0,z0,0)) return *this;
49689       const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f);
49690       const float tolerance2 = cimg::sqr(tolerance);
49691       const CImg<T> ref = get_vector_at(x0,y0,z0);
49692       CImg<uintT> stack(256,1,1,3);
49693       CImg<ucharT> _region(_width,_height,_depth,1,0);
49694       unsigned int N = 0;
49695       int x, y, z;
49696 
49697       _draw_fill_push(x0,y0,z0);
49698       while (N>0) {
49699         _draw_fill_pop(x,y,z);
49700         if (!_region(x,y,z)) {
49701           const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
49702           int xl = x, xr = x;
49703 
49704           // Using these booleans reduces the number of pushes drastically.
49705           bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
49706           for (int step = -1; step<2; step+=2) {
49707             while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
49708               if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
49709                 if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
49710               } else is_yp = false;
49711               if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
49712                 if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
49713               } else is_yn = false;
49714               if (depth()>1) {
49715                 if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
49716                   if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
49717                 } else is_zp = false;
49718                 if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
49719                   if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
49720                 } else is_zn = false;
49721               }
49722               if (is_high_connectivity) {
49723                 const int xp = x - 1, xn = x + 1;
49724                 if (yp>=0 && !is_yp) {
49725                   if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
49726                     _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
49727                   }
49728                   if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
49729                     _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
49730                   }
49731                 }
49732                 if (yn<height() && !is_yn) {
49733                   if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
49734                     _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
49735                   }
49736                   if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
49737                     _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
49738                   }
49739                 }
49740                 if (depth()>1) {
49741                   if (zp>=0 && !is_zp) {
49742                     if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
49743                       _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
49744                     }
49745                     if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
49746                       _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
49747                     }
49748 
49749                     if (yp>=0 && !is_yp) {
49750                       if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
49751                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
49752                       if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
49753                     }
49754                     if (yn<height() && !is_yn) {
49755                       if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
49756                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
49757                       if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
49758                     }
49759                   }
49760 
49761                   if (zn<depth() && !is_zn) {
49762                     if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
49763                       _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
49764                     }
49765                     if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
49766                       _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
49767                     }
49768 
49769                     if (yp>=0 && !is_yp) {
49770                       if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
49771                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
49772                       if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
49773                     }
49774                     if (yn<height() && !is_yn) {
49775                       if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
49776                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
49777                       if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
49778                     }
49779                   }
49780                 }
49781               }
49782               x+=step;
49783             }
49784             if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
49785             else xr = --x;
49786           }
49787           std::memset(_region.data(xl,y,z),1,xr - xl + 1);
49788           if (opacity==1) {
49789             if (sizeof(T)==1) {
49790               const int dx = xr - xl + 1;
49791               cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
49792             } else cimg_forC(*this,c) {
49793                 const T val = (T)color[c];
49794                 T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
49795               }
49796           } else cimg_forC(*this,c) {
49797               const T val = (T)(color[c]*nopacity);
49798               T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
49799             }
49800         }
49801       }
49802       _region.move_to(region);
49803       return *this;
49804     }
49805 
49806     //! Draw filled 3D region with the flood fill algorithm \simplification.
49807     template<typename tc>
49808     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
49809                        const tc *const color, const float opacity=1,
49810                        const float tolerance=0, const bool is_high_connexity=false) {
49811       CImg<ucharT> tmp;
49812       return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
49813     }
49814 
49815     //! Draw filled 2D region with the flood fill algorithm \simplification.
49816     template<typename tc>
49817     CImg<T>& draw_fill(const int x0, const int y0,
49818                        const tc *const color, const float opacity=1,
49819                        const float tolerance=0, const bool is_high_connexity=false) {
49820       CImg<ucharT> tmp;
49821       return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
49822     }
49823 
49824     //! Draw a random plasma texture.
49825     /**
49826        \param alpha Alpha-parameter.
49827        \param beta Beta-parameter.
49828        \param scale Scale-parameter.
49829        \note Use the mid-point algorithm to render.
49830     **/
49831     CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
49832       if (is_empty()) return *this;
49833       const int w = width(), h = height();
49834       const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
49835       cimg_uint64 rng = (cimg::_rand(),cimg::rng());
49836       cimg_forZC(*this,z,c) {
49837         CImg<T> ref = get_shared_slice(z,c);
49838         for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
49839           const int delta2 = delta>>1;
49840           const float r = alpha*delta + beta;
49841 
49842           // Square step.
49843           for (int y0 = 0; y0<h; y0+=delta)
49844             for (int x0 = 0; x0<w; x0+=delta) {
49845               const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
49846               const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
49847                                           r*cimg::rand(-1,1,&rng));
49848               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49849             }
49850 
49851           // Diamond steps.
49852           for (int y = -delta2; y<h; y+=delta)
49853             for (int x0=0; x0<w; x0+=delta) {
49854               const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
49855                 xc = (x0 + delta2)%w, yc = (y + delta2)%h;
49856               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49857                                           r*cimg::rand(-1,1,&rng));
49858               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49859             }
49860           for (int y0 = 0; y0<h; y0+=delta)
49861             for (int x = -delta2; x<w; x+=delta) {
49862               const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
49863                 xc = (x + delta2)%w, yc = (y0 + delta2)%h;
49864               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49865                                           r*cimg::rand(-1,1,&rng));
49866               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49867             }
49868           for (int y = -delta2; y<h; y+=delta)
49869             for (int x = -delta2; x<w; x+=delta) {
49870               const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
49871                 xc = (x + delta2)%w, yc = (y + delta2)%h;
49872               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
49873                                           r*cimg::rand(-1,1,&rng));
49874                 ref(xc,yc) = (T)(val<m?m:val>M?M:val);
49875             }
49876         }
49877       }
49878       cimg::srand(rng);
49879       return *this;
49880     }
49881 
49882     //! Draw a quadratic Mandelbrot or Julia 2D fractal.
49883     /**
49884        \param x0 X-coordinate of the upper-left pixel.
49885        \param y0 Y-coordinate of the upper-left pixel.
49886        \param x1 X-coordinate of the lower-right pixel.
49887        \param y1 Y-coordinate of the lower-right pixel.
49888        \param colormap Colormap.
49889        \param opacity Drawing opacity.
49890        \param z0r Real part of the upper-left fractal vertex.
49891        \param z0i Imaginary part of the upper-left fractal vertex.
49892        \param z1r Real part of the lower-right fractal vertex.
49893        \param z1i Imaginary part of the lower-right fractal vertex.
49894        \param iteration_max Maximum number of iterations for each estimated point.
49895        \param is_normalized_iteration Tells if iterations are normalized.
49896        \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
49897        \param param_r Real part of the Julia set parameter.
49898        \param param_i Imaginary part of the Julia set parameter.
49899        \note Fractal rendering is done by the Escape Time Algorithm.
49900     **/
49901     template<typename tc>
49902     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
49903                              const CImg<tc>& colormap, const float opacity=1,
49904                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
49905                              const unsigned int iteration_max=255,
49906                              const bool is_normalized_iteration=false,
49907                              const bool is_julia_set=false,
49908                              const double param_r=0, const double param_i=0) {
49909       if (is_empty()) return *this;
49910       CImg<tc> palette;
49911       if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
49912       if (palette && palette._spectrum!=_spectrum)
49913         throw CImgArgumentException(_cimg_instance
49914                                     "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
49915                                     "incompatible dimensions.",
49916                                     cimg_instance,
49917                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
49918 
49919       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.);
49920       const int
49921         _x0 = cimg::cut(x0,0,width() - 1),
49922         _y0 = cimg::cut(y0,0,height() - 1),
49923         _x1 = cimg::cut(x1,0,width() - 1),
49924         _y1 = cimg::cut(y1,0,height() - 1);
49925 
49926       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
49927                          cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048))
49928       for (int q = _y0; q<=_y1; ++q)
49929         for (int p = _x0; p<=_x1; ++p) {
49930           unsigned int iteration = 0;
49931           const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
49932           double zr, zi, cr, ci;
49933           if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
49934           else { zr = param_r; zi = param_i; cr = x; ci = y; }
49935           for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
49936             const double temp = zr*zr - zi*zi + cr;
49937             zi = 2*zr*zi + ci;
49938             zr = temp;
49939           }
49940           if (iteration>iteration_max) {
49941             if (palette) {
49942               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
49943               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
49944             } else {
49945               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
49946               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
49947             }
49948           } else if (is_normalized_iteration) {
49949             const float
49950               normz = (float)cimg::abs(zr*zr + zi*zi),
49951               niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
49952             if (palette) {
49953               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
49954               else cimg_forC(*this,c)
49955                      (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
49956             } else {
49957               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
49958               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
49959             }
49960           } else {
49961             if (palette) {
49962               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
49963               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
49964             } else {
49965               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
49966               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
49967             }
49968           }
49969         }
49970       return *this;
49971     }
49972 
49973     //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading.
49974     template<typename tc>
49975     CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
49976                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
49977                              const unsigned int iteration_max=255,
49978                              const bool is_normalized_iteration=false,
49979                              const bool is_julia_set=false,
49980                              const double param_r=0, const double param_i=0) {
49981       return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
49982                              z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
49983     }
49984 
49985     //! Draw a 1D gaussian function.
49986     /**
49987        \param xc X-coordinate of the gaussian center.
49988        \param sigma Standard variation of the gaussian distribution.
49989        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
49990        \param opacity Drawing opacity.
49991     **/
49992     template<typename tc>
49993     CImg<T>& draw_gaussian(const float xc, const float sigma,
49994                            const tc *const color, const float opacity=1) {
49995       if (is_empty()) return *this;
49996       if (!color)
49997         throw CImgArgumentException(_cimg_instance
49998                                     "draw_gaussian(): Specified color is (null).",
49999                                     cimg_instance);
50000       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
50001       const ulongT whd = (ulongT)_width*_height*_depth;
50002       const tc *col = color;
50003       cimg_forX(*this,x) {
50004         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
50005         T *ptrd = data(x,0,0,0);
50006         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
50007         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
50008         col-=_spectrum;
50009       }
50010       return *this;
50011     }
50012 
50013     //! Draw a 2D gaussian function.
50014     /**
50015        \param xc X-coordinate of the gaussian center.
50016        \param yc Y-coordinate of the gaussian center.
50017        \param tensor Covariance matrix (must be 2x2).
50018        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
50019        \param opacity Drawing opacity.
50020     **/
50021     template<typename t, typename tc>
50022     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
50023                            const tc *const color, const float opacity=1) {
50024       if (is_empty()) return *this;
50025       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
50026         throw CImgArgumentException(_cimg_instance
50027                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
50028                                     cimg_instance,
50029                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
50030       if (!color)
50031         throw CImgArgumentException(_cimg_instance
50032                                     "draw_gaussian(): Specified color is (null).",
50033                                     cimg_instance);
50034       typedef typename CImg<t>::Tfloat tfloat;
50035       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
50036       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
50037       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
50038       const ulongT whd = (ulongT)_width*_height*_depth;
50039       const tc *col = color;
50040       float dy = -yc;
50041       cimg_forY(*this,y) {
50042         float dx = -xc;
50043         cimg_forX(*this,x) {
50044           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
50045           T *ptrd = data(x,y,0,0);
50046           if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
50047           else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
50048           col-=_spectrum;
50049           ++dx;
50050         }
50051         ++dy;
50052       }
50053       return *this;
50054     }
50055 
50056     //! Draw a 2D gaussian function \overloading.
50057     template<typename tc>
50058     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
50059                            const tc *const color, const float opacity=1) {
50060       const double
50061         a = r1*ru*ru + r2*rv*rv,
50062         b = (r1-r2)*ru*rv,
50063         c = r1*rv*rv + r2*ru*ru;
50064       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
50065       return draw_gaussian(xc,yc,tensor,color,opacity);
50066     }
50067 
50068     //! Draw a 2D gaussian function \overloading.
50069     template<typename tc>
50070     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
50071                            const tc *const color, const float opacity=1) {
50072       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
50073     }
50074 
50075     //! Draw a 3D gaussian function \overloading.
50076     template<typename t, typename tc>
50077     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
50078                            const tc *const color, const float opacity=1) {
50079       if (is_empty()) return *this;
50080       typedef typename CImg<t>::Tfloat tfloat;
50081       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
50082         throw CImgArgumentException(_cimg_instance
50083                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
50084                                     cimg_instance,
50085                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
50086 
50087       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
50088       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);
50089       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
50090       const ulongT whd = (ulongT)_width*_height*_depth;
50091       const tc *col = color;
50092       cimg_forXYZ(*this,x,y,z) {
50093         const float
50094           dx = (x - xc), dy = (y - yc), dz = (z - zc),
50095           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
50096         T *ptrd = data(x,y,z,0);
50097         if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
50098         else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
50099         col-=_spectrum;
50100       }
50101       return *this;
50102     }
50103 
50104     //! Draw a 3D gaussian function \overloading.
50105     template<typename tc>
50106     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
50107                            const tc *const color, const float opacity=1) {
50108       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
50109     }
50110 
50111     //! Draw a 3D object.
50112     /**
50113        \param x0 X-coordinate of the 3D object position
50114        \param y0 Y-coordinate of the 3D object position
50115        \param z0 Z-coordinate of the 3D object position
50116        \param vertices Image Nx3 describing 3D point coordinates
50117        \param primitives List of P primitives
50118        \param colors List of P color (or textures)
50119        \param opacities Image or list of P opacities
50120        \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
50121        \param is_double_sided Tells if object faces have two sides or are oriented.
50122        \param focale length of the focale (0 for parallel projection)
50123        \param lightx X-coordinate of the light
50124        \param lighty Y-coordinate of the light
50125        \param lightz Z-coordinate of the light
50126        \param specular_lightness Amount of specular light.
50127        \param specular_shininess Shininess of the object
50128        \param g_opacity Global opacity of the object.
50129     **/
50130     template<typename tp, typename tf, typename tc, typename to>
50131     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50132                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50133                            const CImgList<tc>& colors, const CImg<to>& opacities,
50134                            const unsigned int render_type=4,
50135                            const bool is_double_sided=false, const float focale=700,
50136                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50137                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50138                            const float g_opacity=1) {
50139       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
50140                            is_double_sided,focale,lightx,lighty,lightz,
50141                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50142     }
50143 
50144     //! Draw a 3D object \simplification.
50145     template<typename tp, typename tf, typename tc, typename to, typename tz>
50146     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50147                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50148                            const CImgList<tc>& colors, const CImg<to>& opacities,
50149                            const unsigned int render_type,
50150                            const bool is_double_sided, const float focale,
50151                            const float lightx, const float lighty, const float lightz,
50152                            const float specular_lightness, const float specular_shininess,
50153                            const float g_opacity, CImg<tz>& zbuffer) {
50154       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
50155                             render_type,is_double_sided,focale,lightx,lighty,lightz,
50156                             specular_lightness,specular_shininess,g_opacity,1);
50157     }
50158 
50159 #ifdef cimg_use_board
50160     template<typename tp, typename tf, typename tc, typename to>
50161     CImg<T>& draw_object3d(LibBoard::Board& board,
50162                            const float x0, const float y0, const float z0,
50163                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50164                            const CImgList<tc>& colors, const CImg<to>& opacities,
50165                            const unsigned int render_type=4,
50166                            const bool is_double_sided=false, const float focale=700,
50167                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50168                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50169                            const float g_opacity=1) {
50170       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
50171                            is_double_sided,focale,lightx,lighty,lightz,
50172                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50173     }
50174 
50175     template<typename tp, typename tf, typename tc, typename to, typename tz>
50176     CImg<T>& draw_object3d(LibBoard::Board& board,
50177                            const float x0, const float y0, const float z0,
50178                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50179                            const CImgList<tc>& colors, const CImg<to>& opacities,
50180                            const unsigned int render_type,
50181                            const bool is_double_sided, const float focale,
50182                            const float lightx, const float lighty, const float lightz,
50183                            const float specular_lightness, const float specular_shininess,
50184                            const float g_opacity, CImg<tz>& zbuffer) {
50185       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
50186                             render_type,is_double_sided,focale,lightx,lighty,lightz,
50187                             specular_lightness,specular_shininess,g_opacity,1);
50188     }
50189 #endif
50190 
50191     //! Draw a 3D object \simplification.
50192     template<typename tp, typename tf, typename tc, typename to>
50193     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50194                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50195                            const CImgList<tc>& colors, const CImgList<to>& opacities,
50196                            const unsigned int render_type=4,
50197                            const bool is_double_sided=false, const float focale=700,
50198                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50199                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50200                            const float g_opacity=1) {
50201       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
50202                            is_double_sided,focale,lightx,lighty,lightz,
50203                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50204     }
50205 
50206     //! Draw a 3D object \simplification.
50207     template<typename tp, typename tf, typename tc, typename to, typename tz>
50208     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50209                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50210                            const CImgList<tc>& colors, const CImgList<to>& opacities,
50211                            const unsigned int render_type,
50212                            const bool is_double_sided, const float focale,
50213                            const float lightx, const float lighty, const float lightz,
50214                            const float specular_lightness, const float specular_shininess,
50215                            const float g_opacity, CImg<tz>& zbuffer) {
50216       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
50217                             render_type,is_double_sided,focale,lightx,lighty,lightz,
50218                             specular_lightness,specular_shininess,g_opacity,1);
50219     }
50220 
50221 #ifdef cimg_use_board
50222     template<typename tp, typename tf, typename tc, typename to>
50223     CImg<T>& draw_object3d(LibBoard::Board& board,
50224                            const float x0, const float y0, const float z0,
50225                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50226                            const CImgList<tc>& colors, const CImgList<to>& opacities,
50227                            const unsigned int render_type=4,
50228                            const bool is_double_sided=false, const float focale=700,
50229                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50230                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50231                            const float g_opacity=1) {
50232       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
50233                            is_double_sided,focale,lightx,lighty,lightz,
50234                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50235     }
50236 
50237     template<typename tp, typename tf, typename tc, typename to, typename tz>
50238     CImg<T>& draw_object3d(LibBoard::Board& board,
50239                            const float x0, const float y0, const float z0,
50240                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50241                            const CImgList<tc>& colors, const CImgList<to>& opacities,
50242                            const unsigned int render_type,
50243                            const bool is_double_sided, const float focale,
50244                            const float lightx, const float lighty, const float lightz,
50245                            const float specular_lightness, const float specular_shininess,
50246                            const float g_opacity, CImg<tz>& zbuffer) {
50247       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
50248                             render_type,is_double_sided,focale,lightx,lighty,lightz,
50249                             specular_lightness,specular_shininess,g_opacity,1);
50250     }
50251 #endif
50252 
50253     //! Draw a 3D object \simplification.
50254     template<typename tp, typename tf, typename tc>
50255     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50256                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50257                            const CImgList<tc>& colors,
50258                            const unsigned int render_type=4,
50259                            const bool is_double_sided=false, const float focale=700,
50260                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50261                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50262                            const float g_opacity=1) {
50263       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
50264                            render_type,is_double_sided,focale,lightx,lighty,lightz,
50265                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50266     }
50267 
50268     //! Draw a 3D object \simplification.
50269     template<typename tp, typename tf, typename tc, typename tz>
50270     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
50271                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50272                            const CImgList<tc>& colors,
50273                            const unsigned int render_type,
50274                            const bool is_double_sided, const float focale,
50275                            const float lightx, const float lighty, const float lightz,
50276                            const float specular_lightness, const float specular_shininess,
50277                            const float g_opacity, CImg<tz>& zbuffer) {
50278       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
50279                            render_type,is_double_sided,focale,lightx,lighty,lightz,
50280                            specular_lightness,specular_shininess,g_opacity,zbuffer);
50281     }
50282 
50283 #ifdef cimg_use_board
50284     template<typename tp, typename tf, typename tc, typename to>
50285     CImg<T>& draw_object3d(LibBoard::Board& board,
50286                            const float x0, const float y0, const float z0,
50287                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50288                            const CImgList<tc>& colors,
50289                            const unsigned int render_type=4,
50290                            const bool is_double_sided=false, const float focale=700,
50291                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
50292                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
50293                            const float g_opacity=1) {
50294       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
50295                            render_type,is_double_sided,focale,lightx,lighty,lightz,
50296                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
50297     }
50298 
50299     template<typename tp, typename tf, typename tc, typename to, typename tz>
50300     CImg<T>& draw_object3d(LibBoard::Board& board,
50301                            const float x0, const float y0, const float z0,
50302                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
50303                            const CImgList<tc>& colors,
50304                            const unsigned int render_type,
50305                            const bool is_double_sided, const float focale,
50306                            const float lightx, const float lighty, const float lightz,
50307                            const float specular_lightness, const float specular_shininess,
50308                            const float g_opacity, CImg<tz>& zbuffer) {
50309       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
50310                            render_type,is_double_sided,focale,lightx,lighty,lightz,
50311                            specular_lightness,specular_shininess,g_opacity,zbuffer);
50312     }
50313 #endif
50314 
50315     template<typename t, typename to>
50316     static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
50317       if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
50318       if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
50319       opacity.assign(opacities[n_primitive],true);
50320       return 1.f;
50321     }
50322 
50323     template<typename t, typename to>
50324     static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
50325       opacity.assign();
50326       return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive];
50327     }
50328 
50329     template<typename t>
50330     static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
50331       return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f;
50332     }
50333 
50334     template<typename t>
50335     static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
50336       return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f;
50337     }
50338 
50339     template<typename tz, typename tp, typename tf, typename tc, typename to>
50340     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
50341                             const float X, const float Y, const float Z,
50342                             const CImg<tp>& vertices,
50343                             const CImgList<tf>& primitives,
50344                             const CImgList<tc>& colors,
50345                             const to& opacities,
50346                             const unsigned int render_type,
50347                             const bool is_double_sided, const float focale,
50348                             const float lightx, const float lighty, const float lightz,
50349                             const float specular_lightness, const float specular_shininess,
50350                             const float g_opacity, const float sprite_scale) {
50351       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
50352       typedef typename to::value_type _to;
50353       if (is_empty() || !vertices || !primitives) return *this;
50354       CImg<char> error_message(1024);
50355       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
50356         throw CImgArgumentException(_cimg_instance
50357                                     "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).",
50358                                     cimg_instance,vertices._width,primitives._width,error_message.data());
50359 #ifndef cimg_use_board
50360       if (pboard) return *this;
50361 #endif
50362       if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety
50363 
50364       const float
50365         nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)),
50366         nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess),
50367         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
50368         nsl2 = 1 - 2*nsl1*nspec,
50369         nsl3 = nspec2 - nsl1 - nsl2;
50370 
50371       // Create light texture for phong-like rendering.
50372       CImg<floatT> light_texture;
50373       if (render_type==5) {
50374         if (colors._width>primitives._width) {
50375           static CImg<floatT> default_light_texture;
50376           static const tc *lptr = 0;
50377           static tc ref_values[64] = { 0 };
50378           const CImg<tc>& img = colors.back();
50379           bool is_same_texture = (lptr==img._data);
50380           if (is_same_texture)
50381             for (unsigned int r = 0, j = 0; j<8; ++j)
50382               for (unsigned int i = 0; i<8; ++i)
50383                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
50384                   is_same_texture = false; break;
50385                 }
50386           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
50387             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
50388             lptr = colors.back().data();
50389             for (unsigned int r = 0, j = 0; j<8; ++j)
50390               for (unsigned int i = 0; i<8; ++i)
50391                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
50392           }
50393           light_texture.assign(default_light_texture,true);
50394         } else {
50395           static CImg<floatT> default_light_texture;
50396           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
50397           if (!default_light_texture ||
50398               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
50399               specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
50400             default_light_texture.assign(512,512);
50401             const float
50402               dlx = lightx - X,
50403               dly = lighty - Y,
50404               dlz = lightz - Z,
50405               nl = cimg::hypot(dlx,dly,dlz),
50406               nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
50407               nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
50408               white[] = { 1 };
50409             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white);
50410             cimg_forXY(default_light_texture,x,y) {
50411               const float factor = default_light_texture(x,y);
50412               if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3);
50413             }
50414             default_light_texture.resize(-100,-100,1,_spectrum);
50415             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
50416           }
50417           light_texture.assign(default_light_texture,true);
50418         }
50419       }
50420 
50421       // Compute 3D to 2D projection.
50422       CImg<tpfloat> projections(vertices._width,2);
50423       tpfloat parallzmin = cimg::type<tpfloat>::max();
50424       const float absfocale = focale?cimg::abs(focale):0;
50425       if (absfocale) {
50426         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
50427         cimg_forX(projections,l) { // Perspective projection
50428           const tpfloat
50429             x = (tpfloat)vertices(l,0),
50430             y = (tpfloat)vertices(l,1),
50431             z = (tpfloat)vertices(l,2);
50432           const tpfloat projectedz = z + Z + absfocale;
50433           projections(l,1) = Y + absfocale*y/projectedz;
50434           projections(l,0) = X + absfocale*x/projectedz;
50435         }
50436       } else {
50437         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
50438         cimg_forX(projections,l) { // Parallel projection
50439           const tpfloat
50440             x = (tpfloat)vertices(l,0),
50441             y = (tpfloat)vertices(l,1),
50442             z = (tpfloat)vertices(l,2);
50443           if (z<parallzmin) parallzmin = z;
50444           projections(l,1) = Y + y;
50445           projections(l,0) = X + x;
50446         }
50447       }
50448 
50449       const float _focale = absfocale?absfocale:(1e5f-parallzmin);
50450       float zmax = 0;
50451       if (zbuffer) zmax = vertices.get_shared_row(2).max();
50452 
50453       // Compute visible primitives.
50454       CImg<uintT> visibles(primitives._width,1,1,1,~0U);
50455       CImg<tpfloat> zrange(primitives._width);
50456       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
50457       bool is_forward = zbuffer?true:false;
50458 
50459       cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096))
50460       cimglist_for(primitives,l) {
50461         const CImg<tf>& primitive = primitives[l];
50462         switch (primitive.size()) {
50463         case 1 : { // Point
50464           CImg<_to> _opacity;
50465           __draw_object3d(opacities,l,_opacity);
50466           if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
50467           const unsigned int i0 = (unsigned int)primitive(0);
50468           const tpfloat z0 = Z + vertices(i0,2);
50469           if (z0>zmin) {
50470             visibles(l) = (unsigned int)l;
50471             zrange(l) = z0;
50472           }
50473         } break;
50474         case 5 : { // Sphere
50475           const unsigned int
50476             i0 = (unsigned int)primitive(0),
50477             i1 = (unsigned int)primitive(1);
50478           const tpfloat
50479             Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
50480             Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
50481             Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
50482             _zc = Z + Zc,
50483             zc = _zc + _focale,
50484             xc = X + Xc*(absfocale?absfocale/zc:1),
50485             yc = Y + Yc*(absfocale?absfocale/zc:1),
50486             radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
50487                                       vertices(i1,1) - vertices(i0,1),
50488                                       vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
50489             xm = xc - radius,
50490             ym = yc - radius,
50491             xM = xc + radius,
50492             yM = yc + radius;
50493           if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
50494             visibles(l) = (unsigned int)l;
50495             zrange(l) = _zc;
50496           }
50497           is_forward = false;
50498         } break;
50499         case 2 : case 6 : { // Segment
50500           const unsigned int
50501             i0 = (unsigned int)primitive(0),
50502             i1 = (unsigned int)primitive(1);
50503           const tpfloat
50504             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
50505             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
50506           tpfloat xm, xM, ym, yM;
50507           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
50508           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
50509           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
50510             visibles(l) = (unsigned int)l;
50511             zrange(l) = (z0 + z1)/2;
50512           }
50513         } break;
50514         case 3 : case 9 : { // Triangle
50515           const unsigned int
50516             i0 = (unsigned int)primitive(0),
50517             i1 = (unsigned int)primitive(1),
50518             i2 = (unsigned int)primitive(2);
50519           const tpfloat
50520             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
50521             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
50522             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
50523           tpfloat xm, xM, ym, yM;
50524           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
50525           if (x2<xm) xm = x2;
50526           if (x2>xM) xM = x2;
50527           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
50528           if (y2<ym) ym = y2;
50529           if (y2>yM) yM = y2;
50530           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
50531             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
50532             if (is_double_sided || d<0) {
50533               visibles(l) = (unsigned int)l;
50534               zrange(l) = (z0 + z1 + z2)/3;
50535             }
50536           }
50537         } break;
50538         case 4 : case 12 : { // Quadrangle
50539           const unsigned int
50540             i0 = (unsigned int)primitive(0),
50541             i1 = (unsigned int)primitive(1),
50542             i2 = (unsigned int)primitive(2),
50543             i3 = (unsigned int)primitive(3);
50544           const tpfloat
50545             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
50546             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
50547             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
50548             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
50549           tpfloat xm, xM, ym, yM;
50550           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
50551           if (x2<xm) xm = x2;
50552           if (x2>xM) xM = x2;
50553           if (x3<xm) xm = x3;
50554           if (x3>xM) xM = x3;
50555           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
50556           if (y2<ym) ym = y2;
50557           if (y2>yM) yM = y2;
50558           if (y3<ym) ym = y3;
50559           if (y3>yM) yM = y3;
50560           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
50561             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
50562             if (is_double_sided || d<0) {
50563               visibles(l) = (unsigned int)l;
50564               zrange(l) = (z0 + z1 + z2 + z3)/4;
50565             }
50566           }
50567         } break;
50568         default :
50569           if (render_type==5) cimg::mutex(10,0);
50570           throw CImgArgumentException(_cimg_instance
50571                                       "draw_object3d(): Invalid primitive[%u] with size %u "
50572                                       "(should have size 1,2,3,4,5,6,9 or 12).",
50573                                       cimg_instance,
50574                                       l,primitive.size());
50575         }
50576       }
50577 
50578       // Force transparent primitives to be drawn last when zbuffer is activated
50579       // (and if object contains no spheres or sprites).
50580       if (is_forward)
50581         cimglist_for(primitives,l)
50582           if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
50583 
50584       // Sort only visibles primitives.
50585       unsigned int *p_visibles = visibles._data;
50586       tpfloat *p_zrange = zrange._data;
50587       const tpfloat *ptrz = p_zrange;
50588       cimg_for(visibles,ptr,unsigned int) {
50589         if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
50590         ++ptrz;
50591       }
50592       const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
50593       if (!nb_visibles) {
50594         if (render_type==5) cimg::mutex(10,0);
50595         return *this;
50596       }
50597       CImg<uintT> permutations;
50598       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
50599 
50600       // Compute light properties
50601       CImg<floatT> lightprops;
50602       switch (render_type) {
50603       case 3 : { // Flat Shading
50604         lightprops.assign(nb_visibles);
50605         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50606         cimg_forX(lightprops,l) {
50607           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
50608           const unsigned int psize = (unsigned int)primitive.size();
50609           if (psize==3 || psize==4 || psize==9 || psize==12) {
50610             const unsigned int
50611               i0 = (unsigned int)primitive(0),
50612               i1 = (unsigned int)primitive(1),
50613               i2 = (unsigned int)primitive(2);
50614             const tpfloat
50615               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
50616               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
50617               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
50618               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
50619               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
50620               nx = dy1*dz2 - dz1*dy2,
50621               ny = dz1*dx2 - dx1*dz2,
50622               nz = dx1*dy2 - dy1*dx2,
50623               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50624               lx = X + (x0 + x1 + x2)/3 - lightx,
50625               ly = Y + (y0 + y1 + y2)/3 - lighty,
50626               lz = Z + (z0 + z1 + z2)/3 - lightz,
50627               nl = 1e-5f + cimg::hypot(lx,ly,lz),
50628               factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
50629             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
50630           } else lightprops[l] = 1;
50631         }
50632       } break;
50633 
50634       case 4 : // Gouraud Shading
50635       case 5 : { // Phong-Shading
50636         CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
50637         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50638         for (int l = 0; l<(int)nb_visibles; ++l) {
50639           const CImg<tf>& primitive = primitives[visibles(l)];
50640           const unsigned int psize = (unsigned int)primitive.size();
50641           const bool
50642             triangle_flag = (psize==3) || (psize==9),
50643             quadrangle_flag = (psize==4) || (psize==12);
50644           if (triangle_flag || quadrangle_flag) {
50645             const unsigned int
50646               i0 = (unsigned int)primitive(0),
50647               i1 = (unsigned int)primitive(1),
50648               i2 = (unsigned int)primitive(2),
50649               i3 = quadrangle_flag?(unsigned int)primitive(3):0;
50650             const tpfloat
50651               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
50652               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
50653               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
50654               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
50655               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
50656               nnx = dy1*dz2 - dz1*dy2,
50657               nny = dz1*dx2 - dx1*dz2,
50658               nnz = dx1*dy2 - dy1*dx2,
50659               norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
50660               nx = nnx/norm,
50661               ny = nny/norm,
50662               nz = nnz/norm;
50663             unsigned int ix = 0, iy = 1, iz = 2;
50664             if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
50665             vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
50666             vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
50667             vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
50668             if (quadrangle_flag) {
50669               vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
50670             }
50671           }
50672         }
50673 
50674         if (is_double_sided) cimg_forX(vertices_normals,p) {
50675             const float
50676               nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
50677               nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
50678               n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
50679             if (n1>n0) {
50680               vertices_normals(p,0) = -nx1;
50681               vertices_normals(p,1) = -ny1;
50682               vertices_normals(p,2) = -nz1;
50683             }
50684           }
50685 
50686         if (render_type==4) {
50687           lightprops.assign(vertices._width);
50688           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50689           cimg_forX(lightprops,l) {
50690             const tpfloat
50691               nx = vertices_normals(l,0),
50692               ny = vertices_normals(l,1),
50693               nz = vertices_normals(l,2),
50694               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50695               lx = X + vertices(l,0) - lightx,
50696               ly = Y + vertices(l,1) - lighty,
50697               lz = Z + vertices(l,2) - lightz,
50698               nl = 1e-5f + cimg::hypot(lx,ly,lz),
50699               factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
50700             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
50701           }
50702         } else {
50703           const unsigned int
50704             lw2 = light_texture._width/2 - 1,
50705             lh2 = light_texture._height/2 - 1;
50706           lightprops.assign(vertices._width,2);
50707           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
50708           cimg_forX(lightprops,l) {
50709             const tpfloat
50710               nx = vertices_normals(l,0),
50711               ny = vertices_normals(l,1),
50712               nz = vertices_normals(l,2),
50713               norm = 1e-5f + cimg::hypot(nx,ny,nz),
50714               nnx = nx/norm,
50715               nny = ny/norm;
50716             lightprops(l,0) = lw2*(1 + nnx);
50717             lightprops(l,1) = lh2*(1 + nny);
50718           }
50719         }
50720       } break;
50721       }
50722 
50723       // Draw visible primitives
50724       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
50725       CImg<_to> _opacity;
50726 
50727       for (unsigned int l = 0; l<nb_visibles; ++l) {
50728         const unsigned int n_primitive = visibles(permutations(l));
50729         const CImg<tf>& primitive = primitives[n_primitive];
50730         const CImg<tc>
50731           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
50732           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
50733             __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
50734           &color = _color?_color:(__color?__color:default_color);
50735         const tc *const pcolor = color._data;
50736         float opacity = __draw_object3d(opacities,n_primitive,_opacity);
50737         if (_opacity.is_empty()) opacity*=g_opacity;
50738 
50739 #ifdef cimg_use_board
50740         LibBoard::Board &board = *(LibBoard::Board*)pboard;
50741 #endif
50742 
50743         switch (primitive.size()) {
50744         case 1 : { // Colored point or sprite
50745           const unsigned int n0 = (unsigned int)primitive[0];
50746           const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1));
50747 
50748           if (_opacity.is_empty()) { // Scalar opacity
50749 
50750             if (color.size()==_spectrum) { // Colored point
50751               draw_point(x0,y0,pcolor,opacity);
50752 
50753 #ifdef cimg_use_board
50754               if (pboard) {
50755                 board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50756                 board.drawDot((float)x0,height()-(float)y0);
50757               }
50758 #endif
50759             } else { // Sprite
50760               const tpfloat z = Z + vertices(n0,2);
50761               const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
50762               const unsigned int
50763                 _sw = (unsigned int)(color._width*factor),
50764                 _sh = (unsigned int)(color._height*factor),
50765                 sw = _sw?_sw:1, sh = _sh?_sh:1;
50766               const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
50767               if (sw<=3*_width/2 && sh<=3*_height/2 &&
50768                   (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
50769                 const CImg<tc>
50770                   _sprite = (sw!=color._width || sh!=color._height)?
50771                     color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
50772                   &sprite = _sprite?_sprite:color;
50773                 draw_image(nx0,ny0,sprite,opacity);
50774 
50775 #ifdef cimg_use_board
50776                 if (pboard) {
50777                   board.setPenColorRGBi(128,128,128);
50778                   board.setFillColor(LibBoard::Color::Null);
50779                   board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
50780                 }
50781 #endif
50782               }
50783             }
50784           } else { // Opacity mask
50785             const tpfloat z = Z + vertices(n0,2);
50786             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
50787             const unsigned int
50788               _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
50789               _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
50790               sw = _sw?_sw:1, sh = _sh?_sh:1;
50791             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
50792             if (sw<=3*_width/2 && sh<=3*_height/2 &&
50793                 (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
50794               const CImg<tc>
50795                 _sprite = (sw!=color._width || sh!=color._height)?
50796                   color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
50797                 &sprite = _sprite?_sprite:color;
50798               const CImg<_to>
50799                 _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
50800                   _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
50801                 &nopacity = _nopacity?_nopacity:_opacity;
50802               draw_image(nx0,ny0,sprite,nopacity,g_opacity);
50803 
50804 #ifdef cimg_use_board
50805               if (pboard) {
50806                 board.setPenColorRGBi(128,128,128);
50807                 board.setFillColor(LibBoard::Color::Null);
50808                 board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
50809               }
50810 #endif
50811             }
50812           }
50813         } break;
50814         case 2 : { // Colored line
50815           const unsigned int
50816             n0 = (unsigned int)primitive[0],
50817             n1 = (unsigned int)primitive[1];
50818           const int
50819             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50820             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
50821           const float
50822             z0 = vertices(n0,2) + Z + _focale,
50823             z1 = vertices(n1,2) + Z + _focale;
50824           if (render_type) {
50825             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
50826             else draw_line(x0,y0,x1,y1,pcolor,opacity);
50827 
50828 #ifdef cimg_use_board
50829             if (pboard) {
50830               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50831               board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
50832             }
50833 #endif
50834           } else {
50835             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
50836 
50837 #ifdef cimg_use_board
50838             if (pboard) {
50839               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50840               board.drawDot((float)x0,height() - (float)y0);
50841               board.drawDot((float)x1,height() - (float)y1);
50842             }
50843 #endif
50844           }
50845         } break;
50846         case 5 : { // Colored sphere
50847           const unsigned int
50848             n0 = (unsigned int)primitive[0],
50849             n1 = (unsigned int)primitive[1],
50850             is_wireframe = (unsigned int)primitive[2],
50851             is_radius = (unsigned int)primitive[3];
50852           float Xc,Yc,Zc,radius;
50853           if (is_radius) {
50854             Xc = (float)vertices(n0,0);
50855             Yc = (float)vertices(n0,1);
50856             Zc = (float)vertices(n0,2);
50857             radius = cimg::hypot(vertices(n1,0) - vertices(n0,0),
50858                                  vertices(n1,1) - vertices(n0,1),
50859                                  vertices(n1,2) - vertices(n0,2));
50860           } else {
50861             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0));
50862             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1));
50863             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2));
50864             radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
50865                                       vertices(n1,1) - vertices(n0,1),
50866                                       vertices(n1,2) - vertices(n0,2));
50867           }
50868           const float
50869             zc = Z + Zc + _focale,
50870             af = absfocale?absfocale/zc:1,
50871             xc = X + Xc*af,
50872             yc = Y + Yc*af;
50873           radius*=af;
50874 
50875           switch (render_type) {
50876           case 0 :
50877             draw_point((int)xc,(int)yc,pcolor,opacity);
50878 
50879 #ifdef cimg_use_board
50880             if (pboard) {
50881               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50882               board.drawDot(xc,height() - yc);
50883             }
50884 #endif
50885             break;
50886           case 1 :
50887             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
50888 
50889 #ifdef cimg_use_board
50890             if (pboard) {
50891               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50892               board.setFillColor(LibBoard::Color::Null);
50893               board.drawCircle(xc,height() - yc,radius);
50894             }
50895 #endif
50896             break;
50897           default :
50898             if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
50899             else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
50900 
50901 #ifdef cimg_use_board
50902             if (pboard) {
50903               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50904               if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
50905               else {
50906                 board.setFillColor(LibBoard::Color::Null);
50907                 board.drawCircle(xc,height() - yc,radius);
50908               }
50909             }
50910 #endif
50911             break;
50912           }
50913         } break;
50914         case 6 : { // Textured line
50915           if (!__color) {
50916             if (render_type==5) cimg::mutex(10,0);
50917             throw CImgArgumentException(_cimg_instance
50918                                         "draw_object3d(): Undefined texture for line primitive [%u].",
50919                                         cimg_instance,n_primitive);
50920           }
50921           const unsigned int
50922             n0 = (unsigned int)primitive[0],
50923             n1 = (unsigned int)primitive[1];
50924           const int
50925             tx0 = (int)primitive[2], ty0 = (int)primitive[3],
50926             tx1 = (int)primitive[4], ty1 = (int)primitive[5],
50927             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50928             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
50929           const float
50930             z0 = vertices(n0,2) + Z + _focale,
50931             z1 = vertices(n1,2) + Z + _focale;
50932           if (render_type) {
50933             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
50934             else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
50935 
50936 #ifdef cimg_use_board
50937             if (pboard) {
50938               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50939               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50940             }
50941 #endif
50942           } else {
50943             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50944                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50945               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50946                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
50947 
50948 #ifdef cimg_use_board
50949             if (pboard) {
50950               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50951               board.drawDot((float)x0,height() - (float)y0);
50952               board.drawDot((float)x1,height() - (float)y1);
50953             }
50954 #endif
50955           }
50956         } break;
50957         case 3 : { // Colored triangle
50958           const unsigned int
50959             n0 = (unsigned int)primitive[0],
50960             n1 = (unsigned int)primitive[1],
50961             n2 = (unsigned int)primitive[2];
50962           const int
50963             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50964             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50965             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
50966           const float
50967             z0 = vertices(n0,2) + Z + _focale,
50968             z1 = vertices(n1,2) + Z + _focale,
50969             z2 = vertices(n2,2) + Z + _focale;
50970           switch (render_type) {
50971           case 0 :
50972             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
50973 
50974 #ifdef cimg_use_board
50975             if (pboard) {
50976               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50977               board.drawDot((float)x0,height() - (float)y0);
50978               board.drawDot((float)x1,height() - (float)y1);
50979               board.drawDot((float)x2,height() - (float)y2);
50980             }
50981 #endif
50982             break;
50983           case 1 :
50984             if (zbuffer)
50985               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
50986                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
50987             else
50988               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
50989                 draw_line(x1,y1,x2,y2,pcolor,opacity);
50990 
50991 #ifdef cimg_use_board
50992             if (pboard) {
50993               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50994               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50995               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
50996               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50997             }
50998 #endif
50999             break;
51000           case 2 :
51001             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
51002             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
51003 
51004 #ifdef cimg_use_board
51005             if (pboard) {
51006               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
51007               board.fillTriangle((float)x0,height() - (float)y0,
51008                                  (float)x1,height() - (float)y1,
51009                                  (float)x2,height() - (float)y2);
51010             }
51011 #endif
51012             break;
51013           case 3 :
51014             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
51015             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
51016 
51017 #ifdef cimg_use_board
51018             if (pboard) {
51019               const float lp = std::min(lightprops(l),1.f);
51020               board.setPenColorRGBi((unsigned char)(color[0]*lp),
51021                                      (unsigned char)(color[1]*lp),
51022                                      (unsigned char)(color[2]*lp),
51023                                      (unsigned char)(opacity*255));
51024               board.fillTriangle((float)x0,height() - (float)y0,
51025                                  (float)x1,height() - (float)y1,
51026                                  (float)x2,height() - (float)y2);
51027             }
51028 #endif
51029             break;
51030           case 4 :
51031             if (zbuffer)
51032               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
51033                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
51034             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
51035 
51036 #ifdef cimg_use_board
51037             if (pboard) {
51038               board.setPenColorRGBi((unsigned char)(color[0]),
51039                                      (unsigned char)(color[1]),
51040                                      (unsigned char)(color[2]),
51041                                      (unsigned char)(opacity*255));
51042               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
51043                                          (float)x1,height() - (float)y1,lightprops(n1),
51044                                          (float)x2,height() - (float)y2,lightprops(n2));
51045             }
51046 #endif
51047             break;
51048           case 5 : {
51049             const unsigned int
51050               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
51051               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
51052               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1));
51053             if (zbuffer)
51054               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
51055             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
51056 
51057 #ifdef cimg_use_board
51058             if (pboard) {
51059               const float
51060                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
51061                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
51062                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
51063                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
51064                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
51065                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
51066               board.setPenColorRGBi((unsigned char)(color[0]),
51067                                      (unsigned char)(color[1]),
51068                                      (unsigned char)(color[2]),
51069                                      (unsigned char)(opacity*255));
51070               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
51071                                          (float)x1,height() - (float)y1,l1,
51072                                          (float)x2,height() - (float)y2,l2);
51073             }
51074 #endif
51075           } break;
51076           }
51077         } break;
51078         case 4 : { // Colored quadrangle
51079           const unsigned int
51080             n0 = (unsigned int)primitive[0],
51081             n1 = (unsigned int)primitive[1],
51082             n2 = (unsigned int)primitive[2],
51083             n3 = (unsigned int)primitive[3];
51084           const int
51085             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
51086             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
51087             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
51088             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)),
51089             xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
51090           const float
51091             z0 = vertices(n0,2) + Z + _focale,
51092             z1 = vertices(n1,2) + Z + _focale,
51093             z2 = vertices(n2,2) + Z + _focale,
51094             z3 = vertices(n3,2) + Z + _focale,
51095             zc = (z0 + z1 + z2 + z3)/4;
51096 
51097           switch (render_type) {
51098           case 0 :
51099             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
51100               draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
51101 
51102 #ifdef cimg_use_board
51103             if (pboard) {
51104               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
51105               board.drawDot((float)x0,height() - (float)y0);
51106               board.drawDot((float)x1,height() - (float)y1);
51107               board.drawDot((float)x2,height() - (float)y2);
51108               board.drawDot((float)x3,height() - (float)y3);
51109             }
51110 #endif
51111             break;
51112           case 1 :
51113             if (zbuffer)
51114               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
51115                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
51116             else
51117               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
51118                 draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
51119 
51120 #ifdef cimg_use_board
51121             if (pboard) {
51122               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
51123               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
51124               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
51125               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
51126               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
51127             }
51128 #endif
51129             break;
51130           case 2 :
51131             if (zbuffer)
51132               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
51133                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
51134             else
51135               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
51136 
51137 #ifdef cimg_use_board
51138             if (pboard) {
51139               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
51140               board.fillTriangle((float)x0,height() - (float)y0,
51141                                  (float)x1,height() - (float)y1,
51142                                  (float)x2,height() - (float)y2);
51143               board.fillTriangle((float)x0,height() - (float)y0,
51144                                  (float)x2,height() - (float)y2,
51145                                  (float)x3,height() - (float)y3);
51146             }
51147 #endif
51148             break;
51149           case 3 :
51150             if (zbuffer)
51151               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
51152                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
51153             else
51154               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
51155                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
51156 
51157 #ifdef cimg_use_board
51158             if (pboard) {
51159               const float lp = std::min(lightprops(l),1.f);
51160               board.setPenColorRGBi((unsigned char)(color[0]*lp),
51161                                      (unsigned char)(color[1]*lp),
51162                                      (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
51163               board.fillTriangle((float)x0,height() - (float)y0,
51164                                  (float)x1,height() - (float)y1,
51165                                  (float)x2,height() - (float)y2);
51166               board.fillTriangle((float)x0,height() - (float)y0,
51167                                  (float)x2,height() - (float)y2,
51168                                  (float)x3,height() - (float)y3);
51169             }
51170 #endif
51171             break;
51172           case 4 : {
51173             const float
51174               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
51175               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
51176               lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
51177             if (zbuffer)
51178               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
51179               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
51180               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
51181                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
51182             else
51183               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
51184               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
51185               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
51186                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
51187 
51188 #ifdef cimg_use_board
51189             if (pboard) {
51190               board.setPenColorRGBi((unsigned char)(color[0]),
51191                                      (unsigned char)(color[1]),
51192                                      (unsigned char)(color[2]),
51193                                      (unsigned char)(opacity*255));
51194               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
51195                                          (float)x1,height() - (float)y1,lightprop1,
51196                                          (float)x2,height() - (float)y2,lightprop2);
51197               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
51198                                          (float)x2,height() - (float)y2,lightprop2,
51199                                          (float)x3,height() - (float)y3,lightprop3);
51200             }
51201 #endif
51202           } break;
51203           case 5 : {
51204             const unsigned int
51205               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
51206               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
51207               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
51208               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)),
51209               lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
51210             if (zbuffer)
51211               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
51212               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
51213               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
51214                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
51215             else
51216               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
51217               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
51218               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
51219                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
51220 
51221 #ifdef cimg_use_board
51222             if (pboard) {
51223               const float
51224                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
51225                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
51226                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
51227                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
51228               board.setPenColorRGBi((unsigned char)(color[0]),
51229                                      (unsigned char)(color[1]),
51230                                      (unsigned char)(color[2]),
51231                                      (unsigned char)(opacity*255));
51232               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
51233                                          (float)x1,height() - (float)y1,l1,
51234                                          (float)x2,height() - (float)y2,l2);
51235               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
51236                                          (float)x2,height() - (float)y2,l2,
51237                                          (float)x3,height() - (float)y3,l3);
51238             }
51239 #endif
51240           } break;
51241           }
51242         } break;
51243         case 9 : { // Textured triangle
51244           if (!__color) {
51245             if (render_type==5) cimg::mutex(10,0);
51246             throw CImgArgumentException(_cimg_instance
51247                                         "draw_object3d(): Undefined texture for triangle primitive [%u].",
51248                                         cimg_instance,n_primitive);
51249           }
51250           const unsigned int
51251             n0 = (unsigned int)primitive[0],
51252             n1 = (unsigned int)primitive[1],
51253             n2 = (unsigned int)primitive[2];
51254           const int
51255             tx0 = (int)primitive[3], ty0 = (int)primitive[4],
51256             tx1 = (int)primitive[5], ty1 = (int)primitive[6],
51257             tx2 = (int)primitive[7], ty2 = (int)primitive[8],
51258             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
51259             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
51260             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
51261           const float
51262             z0 = vertices(n0,2) + Z + _focale,
51263             z1 = vertices(n1,2) + Z + _focale,
51264             z2 = vertices(n2,2) + Z + _focale;
51265           switch (render_type) {
51266           case 0 :
51267             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
51268                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
51269               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
51270                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
51271               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
51272                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
51273 #ifdef cimg_use_board
51274             if (pboard) {
51275               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51276               board.drawDot((float)x0,height() - (float)y0);
51277               board.drawDot((float)x1,height() - (float)y1);
51278               board.drawDot((float)x2,height() - (float)y2);
51279             }
51280 #endif
51281             break;
51282           case 1 :
51283             if (zbuffer)
51284               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
51285                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
51286                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
51287             else
51288               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
51289                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
51290                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
51291 
51292 #ifdef cimg_use_board
51293             if (pboard) {
51294               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51295               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
51296               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
51297               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
51298             }
51299 #endif
51300             break;
51301           case 2 :
51302             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
51303             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
51304 
51305 #ifdef cimg_use_board
51306             if (pboard) {
51307               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51308               board.fillTriangle((float)x0,height() - (float)y0,
51309                                  (float)x1,height() - (float)y1,
51310                                  (float)x2,height() - (float)y2);
51311             }
51312 #endif
51313             break;
51314           case 3 :
51315             if (zbuffer)
51316               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
51317             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
51318 
51319 #ifdef cimg_use_board
51320             if (pboard) {
51321               const float lp = std::min(lightprops(l),1.f);
51322               board.setPenColorRGBi((unsigned char)(128*lp),
51323                                     (unsigned char)(128*lp),
51324                                     (unsigned char)(128*lp),
51325                                     (unsigned char)(opacity*255));
51326               board.fillTriangle((float)x0,height() - (float)y0,
51327                                  (float)x1,height() - (float)y1,
51328                                  (float)x2,height() - (float)y2);
51329             }
51330 #endif
51331             break;
51332           case 4 :
51333             if (zbuffer)
51334               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51335                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
51336             else
51337               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51338                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
51339 
51340 #ifdef cimg_use_board
51341             if (pboard) {
51342               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51343               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
51344                                         (float)x1,height() - (float)y1,lightprops(n1),
51345                                         (float)x2,height() - (float)y2,lightprops(n2));
51346             }
51347 #endif
51348             break;
51349           case 5 :
51350             if (zbuffer)
51351               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
51352                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
51353                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
51354                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
51355                             opacity);
51356             else
51357               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
51358                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
51359                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
51360                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
51361                             opacity);
51362 
51363 #ifdef cimg_use_board
51364             if (pboard) {
51365               const float
51366                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
51367                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
51368                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
51369                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
51370                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
51371                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
51372               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51373               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
51374                                         (float)x1,height() - (float)y1,l1,
51375                                         (float)x2,height() - (float)y2,l2);
51376             }
51377 #endif
51378             break;
51379           }
51380         } break;
51381         case 12 : { // Textured quadrangle
51382           if (!__color) {
51383             if (render_type==5) cimg::mutex(10,0);
51384             throw CImgArgumentException(_cimg_instance
51385                                         "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
51386                                         cimg_instance,n_primitive);
51387           }
51388           const unsigned int
51389             n0 = (unsigned int)primitive[0],
51390             n1 = (unsigned int)primitive[1],
51391             n2 = (unsigned int)primitive[2],
51392             n3 = (unsigned int)primitive[3];
51393           const int
51394             tx0 = (int)primitive[4], ty0 = (int)primitive[5],
51395             tx1 = (int)primitive[6], ty1 = (int)primitive[7],
51396             tx2 = (int)primitive[8], ty2 = (int)primitive[9],
51397             tx3 = (int)primitive[10], ty3 = (int)primitive[11],
51398             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
51399             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
51400             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
51401             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1));
51402           const float
51403             z0 = vertices(n0,2) + Z + _focale,
51404             z1 = vertices(n1,2) + Z + _focale,
51405             z2 = vertices(n2,2) + Z + _focale,
51406             z3 = vertices(n3,2) + Z + _focale;
51407 
51408           switch (render_type) {
51409           case 0 :
51410             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
51411                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
51412               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
51413                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
51414               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
51415                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
51416               draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
51417                                                    ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
51418 
51419 #ifdef cimg_use_board
51420             if (pboard) {
51421               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51422               board.drawDot((float)x0,height() - (float)y0);
51423               board.drawDot((float)x1,height() - (float)y1);
51424               board.drawDot((float)x2,height() - (float)y2);
51425               board.drawDot((float)x3,height() - (float)y3);
51426             }
51427 #endif
51428             break;
51429           case 1 :
51430             if (zbuffer)
51431               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
51432                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
51433                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
51434                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
51435             else
51436               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
51437                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
51438                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
51439                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
51440 
51441 #ifdef cimg_use_board
51442             if (pboard) {
51443               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51444               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
51445               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
51446               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
51447               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
51448             }
51449 #endif
51450             break;
51451           case 2 :
51452             if (zbuffer)
51453               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
51454                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
51455             else
51456               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
51457                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
51458 
51459 #ifdef cimg_use_board
51460             if (pboard) {
51461               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51462               board.fillTriangle((float)x0,height() - (float)y0,
51463                                  (float)x1,height() - (float)y1,
51464                                  (float)x2,height() - (float)y2);
51465               board.fillTriangle((float)x0,height() - (float)y0,
51466                                  (float)x2,height() - (float)y2,
51467                                  (float)x3,height() - (float)y3);
51468             }
51469 #endif
51470             break;
51471           case 3 :
51472             if (zbuffer)
51473               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
51474                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
51475             else
51476               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
51477                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
51478 
51479 #ifdef cimg_use_board
51480             if (pboard) {
51481               const float lp = std::min(lightprops(l),1.f);
51482               board.setPenColorRGBi((unsigned char)(128*lp),
51483                                      (unsigned char)(128*lp),
51484                                      (unsigned char)(128*lp),
51485                                      (unsigned char)(opacity*255));
51486               board.fillTriangle((float)x0,height() - (float)y0,
51487                                  (float)x1,height() - (float)y1,
51488                                  (float)x2,height() - (float)y2);
51489               board.fillTriangle((float)x0,height() - (float)y0,
51490                                  (float)x2,height() - (float)y2,
51491                                  (float)x3,height() - (float)y3);
51492             }
51493 #endif
51494             break;
51495           case 4 : {
51496             const float
51497               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
51498               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
51499             if (zbuffer)
51500               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51501                             lightprop0,lightprop1,lightprop2,opacity).
51502                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
51503                               lightprop0,lightprop2,lightprop3,opacity);
51504             else
51505               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51506                             lightprop0,lightprop1,lightprop2,opacity).
51507                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
51508                               lightprop0,lightprop2,lightprop3,opacity);
51509 
51510 #ifdef cimg_use_board
51511             if (pboard) {
51512               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51513               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
51514                                          (float)x1,height() - (float)y1,lightprop1,
51515                                          (float)x2,height() - (float)y2,lightprop2);
51516               board.fillGouraudTriangle((float)x0,height()  -(float)y0,lightprop0,
51517                                          (float)x2,height() - (float)y2,lightprop2,
51518                                          (float)x3,height() - (float)y3,lightprop3);
51519             }
51520 #endif
51521           } break;
51522           case 5 : {
51523             const unsigned int
51524               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
51525               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
51526               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
51527               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1));
51528             if (zbuffer)
51529               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51530                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
51531                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
51532                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
51533             else
51534               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
51535                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
51536                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
51537                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
51538 #ifdef cimg_use_board
51539             if (pboard) {
51540               const float
51541                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
51542                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
51543                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
51544                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
51545               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
51546               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
51547                                          (float)x1,height() - (float)y1,l1,
51548                                          (float)x2,height() - (float)y2,l2);
51549               board.fillGouraudTriangle((float)x0,height()  -(float)y0,l0,
51550                                          (float)x2,height() - (float)y2,l2,
51551                                          (float)x3,height() - (float)y3,l3);
51552             }
51553 #endif
51554           } break;
51555           }
51556         } break;
51557         }
51558       }
51559       if (render_type==5) cimg::mutex(10,0);
51560       return *this;
51561     }
51562 
51563     //@}
51564     //---------------------------
51565     //
51566     //! \name Data Input
51567     //@{
51568     //---------------------------
51569 
51570     //! Launch simple interface to select a shape from an image.
51571     /**
51572        \param disp Display window to use.
51573        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
51574        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
51575        \param exit_on_anykey Exit function when any key is pressed.
51576     **/
51577     CImg<T>& select(CImgDisplay &disp,
51578                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
51579                     const bool exit_on_anykey=false,
51580                     const bool is_deep_selection_default=false) {
51581       return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
51582     }
51583 
51584     //! Simple interface to select a shape from an image \overloading.
51585     CImg<T>& select(const char *const title,
51586                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
51587                     const bool exit_on_anykey=false,
51588                     const bool is_deep_selection_default=false) {
51589       return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
51590     }
51591 
51592     //! Simple interface to select a shape from an image \newinstance.
51593     CImg<intT> get_select(CImgDisplay &disp,
51594                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
51595                           const bool exit_on_anykey=false,
51596                           const bool is_deep_selection_default=false) const {
51597       return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
51598     }
51599 
51600     //! Simple interface to select a shape from an image \newinstance.
51601     CImg<intT> get_select(const char *const title,
51602                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
51603                           const bool exit_on_anykey=false,
51604                           const bool is_deep_selection_default=false) const {
51605       CImgDisplay disp;
51606       return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
51607     }
51608 
51609     CImg<intT> _select(CImgDisplay &disp, const char *const title,
51610                        const unsigned int feature_type, unsigned int *const XYZ,
51611                        const int origX, const int origY, const int origZ,
51612                        const bool exit_on_anykey,
51613                        const bool reset_view3d,
51614                        const bool force_display_z_coord,
51615                        const bool is_deep_selection_default) const {
51616       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
51617       if (!disp) {
51618         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
51619         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
51620       } else {
51621         if (title) disp.set_title("%s",title);
51622         disp.move_inside_screen();
51623       }
51624 
51625       CImg<T> thumb;
51626       if (width()>disp.screen_width() || height()>disp.screen_height())
51627         get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
51628 
51629       const unsigned int old_normalization = disp.normalization();
51630       bool old_is_resized = disp.is_resized();
51631       disp._normalization = 0;
51632       disp.show().set_key(0).set_wheel().show_mouse();
51633 
51634       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
51635       int area = 0, area_started = 0, area_clicked = 0, phase = 0,
51636         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width),
51637         Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height),
51638         Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
51639         X1 =-1, Y1 = -1, Z1 = -1,
51640         X3d = -1, Y3d = -1,
51641         oX3d = X3d, oY3d = -1,
51642         omx = -1, omy = -1;
51643       float X = -1, Y = -1, Z = -1;
51644       unsigned int key = 0, font_size = 32;
51645 
51646       bool is_deep_selection = is_deep_selection_default,
51647         shape_selected = false, text_down = false, visible_cursor = true;
51648       static CImg<floatT> pose3d;
51649       static bool is_view3d = false, is_axes = true;
51650       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
51651       CImg<floatT> points3d, opacities3d, sel_opacities3d;
51652       CImgList<uintT> primitives3d, sel_primitives3d;
51653       CImgList<ucharT> colors3d, sel_colors3d;
51654       CImg<ucharT> visu, visu0, view3d;
51655       CImg<charT> text(1024); *text = 0;
51656 
51657       while (!key && !disp.is_closed() && !shape_selected) {
51658 
51659         // Handle mouse motion and selection
51660         int
51661           mx = disp.mouse_x(),
51662           my = disp.mouse_y();
51663 
51664         const float
51665           mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
51666           mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
51667 
51668         area = 0;
51669         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
51670         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
51671         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
51672         if (mX>=width() && mY>=height()) area = 4;
51673         if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
51674 
51675         CImg<charT> filename(32);
51676 
51677         switch (key = disp.key()) {
51678 #if cimg_OS!=2
51679         case cimg::keyCTRLRIGHT :
51680 #endif
51681         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
51682         case cimg::keyPAGEUP :
51683           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
51684         case cimg::keyPAGEDOWN :
51685           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
51686         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51687             is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
51688           } break;
51689         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51690             disp.set_fullscreen(false).
51691               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51692                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
51693               _is_resized = true;
51694             disp.set_key(key,false); key = 0; visu0.assign();
51695           } break;
51696         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51697             disp.set_fullscreen(false).
51698               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
51699             disp.set_key(key,false); key = 0; visu0.assign();
51700           } break;
51701         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51702             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
51703             disp.set_key(key,false); key = 0; visu0.assign();
51704           } break;
51705         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51706             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
51707             disp.set_key(key,false); key = 0; visu0.assign();
51708           } break;
51709         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51710             is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
51711           } break;
51712         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51713             static unsigned int snap_number = 0;
51714             std::FILE *file;
51715             do {
51716               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
51717               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51718             } while (file);
51719             if (visu0) {
51720               (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp);
51721               visu0.save(filename);
51722               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
51723             }
51724             disp.set_key(key,false); key = 0;
51725           } break;
51726         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51727             static unsigned int snap_number = 0;
51728             std::FILE *file;
51729             do {
51730 
51731 #ifdef cimg_use_zlib
51732               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
51733 #else
51734               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
51735 #endif
51736               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51737             } while (file);
51738             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
51739             save(filename);
51740             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
51741             disp.set_key(key,false); key = 0;
51742           } break;
51743         }
51744 
51745         switch (area) {
51746 
51747         case 0 : // When mouse is out of image range
51748           mx = my = -1; X = Y = Z = -1;
51749           break;
51750 
51751         case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections
51752           const unsigned int but = disp.button();
51753           const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
51754 
51755           if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step)
51756             if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
51757             X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
51758           }
51759           if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes)
51760             switch (area_started) {
51761             case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
51762             case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
51763             case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
51764             }
51765           }
51766           if (b2 && area_clicked==area) { // When moving through the image/volume
51767             if (phase) {
51768               if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
51769               X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
51770             } else {
51771               if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
51772               X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
51773             }
51774           }
51775           if (b3) { // Reset selection
51776             X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
51777             visu0.assign();
51778           }
51779           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel)
51780             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
51781                 !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
51782               switch (area) {
51783               case 1 :
51784                 if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
51785                 visu0.assign(); break;
51786               case 2 :
51787                 if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
51788                 visu0.assign(); break;
51789               case 3 :
51790                 if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
51791                 visu0.assign(); break;
51792               }
51793               disp.set_wheel();
51794             } else key = ~0U;
51795           }
51796 
51797           if ((phase==0 && b1) ||
51798               (phase==1 && !b1) ||
51799               (phase==2 && b1)) switch (phase) { // Detect change of phase
51800             case 0 :
51801               if (area==area_clicked) {
51802                 X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
51803               } break;
51804             case 1 :
51805               if (area==area_started) {
51806                 X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
51807                 if (_depth>1) {
51808                   if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
51809                   if (is_deep_selection) ++phase;
51810                 }
51811               } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
51812               break;
51813             case 2 : ++phase; break;
51814             }
51815         } break;
51816 
51817         case 4 : // When mouse is over the 3D view
51818           if (is_view3d && points3d) {
51819             X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
51820             Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
51821             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
51822             // Left + right buttons: reset.
51823             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
51824             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate
51825               const float
51826                 R = 0.45f*std::min(view3d._width,view3d._height),
51827                 R2 = R*R,
51828                 u0 = (float)(oX3d - view3d.width()/2),
51829                 v0 = (float)(oY3d - view3d.height()/2),
51830                 u1 = (float)(X3d - view3d.width()/2),
51831                 v1 = (float)(Y3d - view3d.height()/2),
51832                 n0 = cimg::hypot(u0,v0),
51833                 n1 = cimg::hypot(u1,v1),
51834                 nu0 = n0>R?(u0*R/n0):u0,
51835                 nv0 = n0>R?(v0*R/n0):v0,
51836                 nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
51837                 nu1 = n1>R?(u1*R/n1):u1,
51838                 nv1 = n1>R?(v1*R/n1):v1,
51839                 nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
51840                 u = nv0*nw1 - nw0*nv1,
51841                 v = nw0*nu1 - nu0*nw1,
51842                 w = nv0*nu1 - nu0*nv1,
51843                 n = cimg::hypot(u,v,w),
51844                 alpha = (float)std::asin(n/R2)*180/cimg::PI;
51845               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
51846               view3d.assign();
51847             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button: zoom
51848               pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
51849             }
51850             if (disp.wheel()) { // Wheel: zoom
51851               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
51852             }
51853             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift
51854               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
51855             }
51856             oX3d = X3d; oY3d = Y3d;
51857           }
51858           mx = my = -1; X = Y = Z = -1;
51859           break;
51860         }
51861 
51862         if (phase) {
51863           if (!feature_type) shape_selected = phase?true:false;
51864           else {
51865             if (_depth>1) shape_selected = (phase==3)?true:false;
51866             else shape_selected = (phase==2)?true:false;
51867           }
51868         }
51869 
51870         if (X0<0) X0 = 0;
51871         if (X0>=width()) X0 = width() - 1;
51872         if (Y0<0) Y0 = 0;
51873         if (Y0>=height()) Y0 = height() - 1;
51874         if (Z0<0) Z0 = 0;
51875         if (Z0>=depth()) Z0 = depth() - 1;
51876         if (X1<1) X1 = 0;
51877         if (X1>=width()) X1 = width() - 1;
51878         if (Y1<0) Y1 = 0;
51879         if (Y1>=height()) Y1 = height() - 1;
51880         if (Z1<0) Z1 = 0;
51881         if (Z1>=depth()) Z1 = depth() - 1;
51882 
51883         // Draw visualization image on the display
51884         if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
51885 
51886           if (!visu0) { // Create image of projected planes
51887             if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
51888             else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
51889             visu0.resize(disp);
51890             view3d.assign();
51891             points3d.assign();
51892           }
51893 
51894           if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images
51895             const unsigned int
51896               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
51897               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
51898               x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
51899               y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
51900             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
51901               move_to(view3d);
51902             if (!points3d) {
51903               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
51904               points3d.append(CImg<floatT>(8,3,1,1,
51905                                            0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
51906                                            0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
51907                                            0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
51908               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
51909               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
51910               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
51911               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
51912               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
51913               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
51914               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
51915               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
51916               if (!phase) {
51917                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
51918                 sel_primitives3d.assign();
51919                 sel_colors3d.assign();
51920                 sel_opacities3d.assign();
51921               } else {
51922                 if (feature_type==2) {
51923                   points3d.append(CImg<floatT>(8,3,1,1,
51924                                                X0,X1,X1,X0,X0,X1,X1,X0,
51925                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
51926                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
51927                   sel_primitives3d.assign();
51928                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
51929                   CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
51930                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
51931                   CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
51932                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
51933                   CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
51934                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
51935                   CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
51936                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
51937                   CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
51938                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
51939                   CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
51940                 } else {
51941                   points3d.append(CImg<floatT>(2,3,1,1,
51942                                                X0,X1,
51943                                                Y0,Y1,
51944                                                Z0,Z1),'x');
51945                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
51946                 }
51947                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
51948                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
51949               }
51950               points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
51951               points3d*=0.75f*std::min(view3d._width,view3d._height);
51952             }
51953 
51954             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
51955             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
51956             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
51957             if (sel_primitives3d)
51958               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
51959                                    pose3d(3,1) + 0.5f*view3d._height,
51960                                    pose3d(3,2),
51961                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
51962                                    2,true,500,0,0,0,0,0,1,zbuffer3d);
51963             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
51964                                  pose3d(3,1) + 0.5f*view3d._height,
51965                                  pose3d(3,2),
51966                                  rotated_points3d,primitives3d,colors3d,opacities3d,
51967                                  2,true,500,0,0,0,0,0,1,zbuffer3d);
51968             visu0.draw_image(x3d,y3d,view3d);
51969           }
51970           visu = visu0;
51971 
51972           if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
51973           else {
51974             if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
51975             else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
51976             const int d = (depth()>1)?depth():0;
51977             int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
51978             if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
51979             int
51980               w = disp.width(), W = width() + d,
51981               h = disp.height(), H = height() + d,
51982               _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
51983               _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
51984               _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1),
51985               _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1),
51986               _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
51987               _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
51988               _zxn = (int)((_vZ + width() + 1.f)*w/W - 1),
51989                        zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1),
51990               _zyn = (int)((_vZ + height() + 1.f)*h/H - 1),
51991                        zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1),
51992               _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()),
51993               _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()),
51994               xc = (xp + xn)/2,
51995               yc = (yp + yn)/2,
51996               zxc = (zxp + zxn)/2,
51997               zyc = (zyp + zyn)/2,
51998               xf = (int)(X*w/W),
51999               yf = (int)(Y*h/H),
52000               zxf = (int)((Z + width())*w/W),
52001               zyf = (int)((Z + height())*h/H);
52002 
52003             if (is_axes) { // Draw axes
52004               visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
52005                 draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
52006                 draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
52007                 draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
52008               if (_depth>1)
52009                 visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
52010                   draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
52011                   draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
52012                   draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
52013             }
52014 
52015             // Draw box cursor.
52016             if (xn - xp>=4 && yn - yp>=4)
52017               visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
52018                 draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
52019                 draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
52020             if (_depth>1) {
52021               if (yn - yp>=4 && zxn - zxp>=4)
52022                 visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
52023                                               draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
52024                                               draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
52025               if (xn - xp>=4 && zyn - zyp>=4)
52026                 visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
52027                           draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
52028                           draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
52029             }
52030 
52031             // Draw selection.
52032             if (phase && (phase!=1 || area_started==area)) {
52033               const int
52034                 _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
52035                 _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
52036                 _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1),
52037                 _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1),
52038                 _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
52039                 _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
52040                 _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1),
52041                 zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1),
52042                 _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1),
52043                 zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1),
52044                 xc0 = (xp0 + xn0)/2,
52045                 yc0 = (yp0 + yn0)/2,
52046                 zxc0 = (zxp0 + zxn0)/2,
52047                 zyc0 = (zyp0 + zyn0)/2;
52048 
52049               switch (feature_type) {
52050               case 1 : { // Vector
52051                 visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333).
52052                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC);
52053                 if (d) {
52054                   visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333).
52055                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC).
52056                     draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333).
52057                     draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC);
52058                 }
52059               } break;
52060               case 2 : { // Box
52061                 visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
52062                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
52063                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA);
52064                 if (xc0!=xc && yc0!=yc)
52065                   visu.draw_line(xc0,yc0,xc,yc,background_color,0.9f,0x33333333).
52066                     draw_line(xc0,yc0,xc,yc,foreground_color,0.9f,0xCCCCCCCC);
52067                 if (d) {
52068                   visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
52069                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
52070                                    background_color,0.9f,0x55555555).
52071                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
52072                                    foreground_color,0.9f,0xAAAAAAAA);
52073                   if (zxc0!=zxc && yc0!=yc)
52074                     visu.draw_line(zxc0,yc0,zxc,yc,background_color,0.9f,0x33333333).
52075                       draw_line(zxc0,yc0,zxc,yc,foreground_color,0.9f,0xCCCCCCCC);
52076                   visu.draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
52077                                       background_color,0.2f).
52078                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
52079                                    background_color,0.9f,0x55555555).
52080                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
52081                                    foreground_color,0.9f,0xAAAAAAAA);
52082                   if (xp0!=xn && zyp0!=zyn)
52083                     visu.draw_line(xp0,zyp0,xn,zyn,background_color,0.9f,0x33333333).
52084                       draw_line(xp0,zyp0,xn,zyn,foreground_color,0.9f,0xCCCCCCCC);
52085                 }
52086               } break;
52087               case 3 : { // Ellipse
52088                 visu.draw_ellipse(xc0,yc0,
52089                                   (float)cimg::abs(xc - xc0),
52090                                   (float)cimg::abs(yc - yc0),0,background_color,0.2f).
52091                   draw_ellipse(xc0,yc0,
52092                                (float)cimg::abs(xc - xc0),
52093                                (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
52094                   draw_point(xc0,yc0,foreground_color,0.9f);
52095                 if (d) {
52096                   visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
52097                                     background_color,0.2f).
52098                     draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
52099                                  foreground_color,0.9f,~0U).
52100                     draw_point(zxc0,yc0,foreground_color,0.9f).
52101                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
52102                                  background_color,0.2f).
52103                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
52104                                  foreground_color,0.9f,~0U).
52105                     draw_point(xc0,zyc0,foreground_color,0.9f);
52106                 }
52107               } break;
52108               }
52109             }
52110 
52111             // Draw text info.
52112             if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
52113             if (!feature_type || !phase) {
52114               if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
52115                 if (_depth>1 || force_display_z_coord)
52116                   cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
52117                 else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
52118                 CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
52119                 const bool is_large_spectrum = values._height>8;
52120                 if (is_large_spectrum)
52121                   values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0);
52122                 char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
52123                 for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
52124                   cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
52125                                 cimg::type<T>::format(values[c]));
52126                   ctext += std::strlen(ctext);
52127                   if (c==3 && is_large_spectrum) {
52128                     cimg_snprintf(ctext,24," ...");
52129                     ctext += std::strlen(ctext);
52130                   }
52131                   *(ctext++) = ' '; *ctext = 0;
52132                 }
52133                 std::strcpy(text._data + std::strlen(text),"] ");
52134               }
52135             } else switch (feature_type) {
52136               case 1 : {
52137                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
52138                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
52139                 if (_depth>1 || force_display_z_coord)
52140                   cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
52141                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
52142                 else if (_width!=1 && _height!=1)
52143                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
52144                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length,
52145                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
52146                 else
52147                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
52148                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length);
52149               } break;
52150               case 2 : {
52151                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
52152                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
52153                 if (_depth>1 || force_display_z_coord)
52154                   cimg_snprintf(text,text._width,
52155                                 " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ",
52156                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
52157                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
52158                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
52159                 else if (_width!=1 && _height!=1)
52160                   cimg_snprintf(text,text._width,
52161                                 " Box ( %d,%d ) - ( %d,%d )\n Size = ( %d,%d ), Length = %g \n Angle = %g\260 ",
52162                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
52163                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
52164                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
52165                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
52166                 else
52167                   cimg_snprintf(text,text._width,
52168                                 " Box ( %d,%d ) - ( %d,%d )\n Size = (%d,%d), Length = %g ",
52169                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
52170                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
52171                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
52172               } break;
52173               default :
52174                 if (_depth>1 || force_display_z_coord)
52175                   cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ",
52176                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
52177                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
52178                 else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ",
52179                                    origX + X0,origY + Y0,origX + X1,origY + Y1,
52180                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
52181               }
52182             if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data);
52183           }
52184 
52185           disp.display(visu);
52186         }
52187         if (!shape_selected) disp.wait();
52188         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
52189         omx = mx; omy = my;
52190         if (!exit_on_anykey && key && key!=cimg::keyESC &&
52191             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52192           key = 0;
52193         }
52194       }
52195 
52196       // Return result.
52197       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
52198       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
52199       if (shape_selected) {
52200         if (feature_type==2) {
52201           if (is_deep_selection) switch (area_started) {
52202             case 1 : Z0 = 0; Z1 = _depth - 1; break;
52203             case 2 : Y0 = 0; Y1 = _height - 1; break;
52204             case 3 : X0 = 0; X1 = _width - 1; break;
52205           }
52206           if (X0>X1) cimg::swap(X0,X1);
52207           if (Y0>Y1) cimg::swap(Y0,Y1);
52208           if (Z0>Z1) cimg::swap(Z0,Z1);
52209         }
52210         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
52211         switch (feature_type) {
52212         case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
52213         case 3 :
52214           res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
52215           res[0] = X0; res[1] = Y0; res[2] = Z0;
52216           break;
52217         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
52218         }
52219       }
52220       if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
52221       if (!visible_cursor) disp.show_mouse();
52222       disp._normalization = old_normalization;
52223       disp._is_resized = old_is_resized;
52224       if (key!=~0U) disp.set_key(key);
52225       return res;
52226     }
52227 
52228     // Return a visualizable uchar8 image for display routines.
52229     CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
52230                              const int x, const int y, const int z) const {
52231       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
52232       const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
52233       CImg<Tuchar> img2d;
52234       if (_depth>1) {
52235         const int mdisp = std::min(disp.screen_width(),disp.screen_height());
52236         if (depth()>mdisp) {
52237           crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
52238           img2d.projections2d(x,y,z*img2d._depth/_depth);
52239         } else crop.get_projections2d(x,y,z).move_to(img2d);
52240       } else CImg<Tuchar>(crop,false).move_to(img2d);
52241 
52242       // Check for inf and NaN values.
52243       if (cimg::type<T>::is_float() && normalization) {
52244         bool is_inf = false, is_nan = false;
52245         cimg_for(img2d,ptr,Tuchar)
52246           if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
52247           else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
52248         if (is_inf || is_nan) {
52249           Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
52250           if (!normalization) { m0 = 0; M0 = 255; }
52251           else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
52252           else {
52253             cimg_for(img2d,ptr,Tuchar)
52254               if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
52255                 if (*ptr<(Tuchar)m0) m0 = *ptr;
52256                 if (*ptr>(Tuchar)M0) M0 = *ptr;
52257               }
52258           }
52259           const T
52260             val_minf = (T)(normalization==1 || normalization==3?m0 - cimg::abs(m0):m0),
52261             val_pinf = (T)(normalization==1 || normalization==3?M0 + cimg::abs(M0):M0);
52262           if (is_nan)
52263             cimg_for(img2d,ptr,Tuchar)
52264               if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values
52265           if (is_inf)
52266             cimg_for(img2d,ptr,Tuchar)
52267               if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values
52268         }
52269       }
52270 
52271       switch (normalization) {
52272       case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
52273       case 2 : {
52274         const float m = disp._min, M = disp._max;
52275         (img2d-=m)*=255.f/(M - m>0?M - m:1);
52276       } break;
52277       case 3 :
52278         if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
52279         else {
52280           const float
52281             m = (float)cimg::type<T>::min(),
52282             M = (float)cimg::type<T>::max();
52283           (img2d-=m)*=255.f/(M - m>0?M - m:1);
52284         } break;
52285       }
52286       if (img2d.spectrum()==2) img2d.channels(0,2);
52287       return img2d;
52288     }
52289 
52290     //! Select sub-graph in a graph.
52291     CImg<intT> get_select_graph(CImgDisplay &disp,
52292                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
52293                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
52294                                 const char *const labely=0, const double ymin=0, const double ymax=0,
52295                                 const bool exit_on_anykey=false) const {
52296       if (is_empty())
52297         throw CImgInstanceException(_cimg_instance
52298                                     "select_graph(): Empty instance.",
52299                                     cimg_instance);
52300       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
52301                    set_title("CImg<%s>",pixel_type());
52302       const ulongT siz = (ulongT)_width*_height*_depth;
52303       const unsigned int old_normalization = disp.normalization();
52304       disp.show().set_button().set_wheel()._normalization = 0;
52305 
52306       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
52307       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
52308       if (nymin==nymax) { --nymin; ++nymax; }
52309       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }
52310 
52311       static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
52312       static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
52313 
52314       CImg<ucharT> colormap(3,_spectrum);
52315       if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
52316       else {
52317         colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
52318         if (_spectrum>1) { colormap(0,1) = 10;  colormap(1,1) = 220; colormap(2,1) = 10;  }
52319         if (_spectrum>2) { colormap(0,2) = 10;  colormap(1,2) = 10;  colormap(2,2) = 220; }
52320         if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10;  }
52321         if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10;  colormap(2,4) = 220; }
52322         if (_spectrum>5) { colormap(0,5) = 10;  colormap(1,5) = 220; colormap(2,5) = 220; }
52323         if (_spectrum>6) {
52324           cimg_uint64 rng = 10;
52325           cimg_for_inY(colormap,6,colormap.height()-1,k) {
52326             colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
52327             colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
52328             colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
52329           }
52330         }
52331       }
52332 
52333       CImg<ucharT> visu0, visu, graph, text, axes;
52334       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
52335       const unsigned int one = plot_type==3?0U:1U;
52336       unsigned int okey = 0, obutton = 0, font_size = 32;
52337       CImg<charT> message(1024);
52338       CImg_3x3(I,unsigned char);
52339 
52340       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
52341         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
52342         const unsigned int key = disp.key(), button = disp.button();
52343 
52344         // Generate graph representation.
52345         if (!visu0) {
52346           visu0.assign(disp.width(),disp.height(),1,3,220);
52347           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
52348           if (gdimx>0 && gdimy>0) {
52349             graph.assign(gdimx,gdimy,1,3,255);
52350             if (siz<32) {
52351               if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
52352                                          false,true,black,0.2f,0x33333333,0x33333333);
52353             } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
52354             cimg_forC(*this,c)
52355               graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
52356                                plot_type,vertex_type,nymax,nymin);
52357 
52358             axes.assign(gdimx,gdimy,1,1,0);
52359             const float
52360               dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
52361               px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
52362               py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
52363             const CImg<Tdouble>
52364               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
52365                 CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
52366               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);
52367 
52368             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
52369             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
52370             if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
52371             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
52372             if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
52373             if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);
52374 
52375             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
52376               if (Icc) {
52377                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
52378                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
52379               }
52380               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
52381                 cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
52382 
52383             visu0.draw_image(16,16,graph);
52384             visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
52385               draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
52386           } else graph.assign();
52387           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
52388           visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
52389           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
52390           visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
52391           visu.assign();
52392         }
52393 
52394         // Generate and display current view.
52395         if (!visu) {
52396           visu.assign(visu0);
52397           if (graph && x0>=0 && x1>=0) {
52398             const int
52399               nx0 = x0<=x1?x0:x1,
52400               nx1 = x0<=x1?x1:x0,
52401               ny0 = y0<=y1?y0:y1,
52402               ny1 = y0<=y1?y1:y0,
52403               sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
52404               sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
52405               sy0 = 16 + ny0,
52406               sy1 = 16 + ny1;
52407             if (y0>=0 && y1>=0)
52408               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
52409             else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
52410                    draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
52411                    draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
52412           }
52413           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
52414             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
52415             const unsigned int
52416               x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
52417             const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
52418             if (_spectrum>=7)
52419               cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
52420                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
52421                             (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
52422                             (double)(*this)(x,0,0,_spectrum - 1));
52423             else {
52424               cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
52425               cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
52426               cimg_sprintf(message._data + std::strlen(message),")");
52427             }
52428             if (x0>=0 && x1>=0) {
52429               const unsigned int
52430                 nx0 = (unsigned int)(x0<=x1?x0:x1),
52431                 nx1 = (unsigned int)(x0<=x1?x1:x0),
52432                 ny0 = (unsigned int)(y0<=y1?y0:y1),
52433                 ny1 = (unsigned int)(y0<=y1?y1:y0);
52434               const double
52435                 cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
52436                 cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
52437                 cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
52438                 cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
52439               if (y0>=0 && y1>=0)
52440                 cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
52441                              x0,cx0,cy0,x1 + one,cx1,cy1);
52442               else
52443                 cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
52444                              x0,cx0,x1 + one,cx1);
52445             }
52446             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
52447             visu.draw_image((visu.width() - text.width())/2,1,~text);
52448           }
52449           visu.display(disp);
52450         }
52451 
52452         // Test keys.
52453         CImg<charT> filename(32);
52454         switch (okey = key) {
52455 #if cimg_OS!=2
52456         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
52457 #endif
52458         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
52459         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52460           disp.set_fullscreen(false).
52461             resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
52462                    CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
52463             _is_resized = true;
52464           disp.set_key(key,false); okey = 0;
52465         } break;
52466         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52467           disp.set_fullscreen(false).
52468             resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
52469           disp.set_key(key,false); okey = 0;
52470         } break;
52471         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52472             disp.set_fullscreen(false).
52473               resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
52474                                     CImgDisplay::screen_height()/2,1),false)._is_resized = true;
52475             disp.set_key(key,false); okey = 0;
52476           } break;
52477         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52478             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
52479             disp.set_key(key,false); okey = 0;
52480           } break;
52481         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52482             static unsigned int snap_number = 0;
52483             if (visu || visu0) {
52484               CImg<ucharT> &screen = visu?visu:visu0;
52485               std::FILE *file;
52486               do {
52487                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
52488                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
52489               } while (file);
52490               (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
52491               screen.save(filename);
52492               (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
52493             }
52494             disp.set_key(key,false); okey = 0;
52495           } break;
52496         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52497             static unsigned int snap_number = 0;
52498             if (visu || visu0) {
52499               CImg<ucharT> &screen = visu?visu:visu0;
52500               std::FILE *file;
52501               do {
52502 
52503 #ifdef cimg_use_zlib
52504                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
52505 #else
52506                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
52507 #endif
52508                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
52509               } while (file);
52510               (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp);
52511               save(filename);
52512               (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp);
52513             }
52514             disp.set_key(key,false); okey = 0;
52515           } break;
52516         }
52517 
52518         // Handle mouse motion and mouse buttons.
52519         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
52520           visu.assign();
52521           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
52522             const int
52523               mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
52524               cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
52525               my = mouse_y - 16,
52526               cy = cimg::cut(my,0,disp.height() - 32);
52527             if (button&1) {
52528               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
52529             }
52530             else if (button&2) {
52531               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
52532             }
52533             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
52534           } else if (!button && obutton) selected = true;
52535           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
52536         }
52537         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
52538         if (visu && visu0) disp.wait();
52539         if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
52540             (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52541           disp.set_key(key,false);
52542           okey = 0;
52543         }
52544       }
52545 
52546       disp._normalization = old_normalization;
52547       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
52548       if (y1<y0) cimg::swap(y0,y1);
52549       disp.set_key(okey);
52550       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
52551     }
52552 
52553     //! Load image from a file.
52554     /**
52555        \param filename Filename, as a C-string.
52556        \note The extension of \c filename defines the file format. If no filename
52557        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
52558     **/
52559     CImg<T>& load(const char *const filename) {
52560       if (!filename)
52561         throw CImgArgumentException(_cimg_instance
52562                                     "load(): Specified filename is (null).",
52563                                     cimg_instance);
52564 
52565       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
52566         CImg<charT> filename_local(256);
52567         load(cimg::load_network(filename,filename_local));
52568         std::remove(filename_local);
52569         return *this;
52570       }
52571 
52572       const char *const ext = cimg::split_filename(filename);
52573       const unsigned int omode = cimg::exception_mode();
52574       cimg::exception_mode(0);
52575       bool is_loaded = true;
52576       try {
52577 #ifdef cimg_load_plugin
52578         cimg_load_plugin(filename);
52579 #endif
52580 #ifdef cimg_load_plugin1
52581         cimg_load_plugin1(filename);
52582 #endif
52583 #ifdef cimg_load_plugin2
52584         cimg_load_plugin2(filename);
52585 #endif
52586 #ifdef cimg_load_plugin3
52587         cimg_load_plugin3(filename);
52588 #endif
52589 #ifdef cimg_load_plugin4
52590         cimg_load_plugin4(filename);
52591 #endif
52592 #ifdef cimg_load_plugin5
52593         cimg_load_plugin5(filename);
52594 #endif
52595 #ifdef cimg_load_plugin6
52596         cimg_load_plugin6(filename);
52597 #endif
52598 #ifdef cimg_load_plugin7
52599         cimg_load_plugin7(filename);
52600 #endif
52601 #ifdef cimg_load_plugin8
52602         cimg_load_plugin8(filename);
52603 #endif
52604         // Text formats
52605         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
52606         else if (!cimg::strcasecmp(ext,"csv") ||
52607                  !cimg::strcasecmp(ext,"dlm") ||
52608                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
52609         else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename);
52610 
52611         // 2D binary formats
52612         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
52613         else if (!cimg::strcasecmp(ext,"jpg") ||
52614                  !cimg::strcasecmp(ext,"jpeg") ||
52615                  !cimg::strcasecmp(ext,"jpe") ||
52616                  !cimg::strcasecmp(ext,"jfif") ||
52617                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
52618         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
52619         else if (!cimg::strcasecmp(ext,"ppm") ||
52620                  !cimg::strcasecmp(ext,"pgm") ||
52621                  !cimg::strcasecmp(ext,"pnm") ||
52622                  !cimg::strcasecmp(ext,"pbm") ||
52623                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
52624         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
52625         else if (!cimg::strcasecmp(ext,"tif") ||
52626                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
52627         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
52628         else if (!cimg::strcasecmp(ext,"arw") ||
52629                  !cimg::strcasecmp(ext,"cr2") ||
52630                  !cimg::strcasecmp(ext,"crw") ||
52631                  !cimg::strcasecmp(ext,"dcr") ||
52632                  !cimg::strcasecmp(ext,"dng") ||
52633                  !cimg::strcasecmp(ext,"mrw") ||
52634                  !cimg::strcasecmp(ext,"nef") ||
52635                  !cimg::strcasecmp(ext,"orf") ||
52636                  !cimg::strcasecmp(ext,"pix") ||
52637                  !cimg::strcasecmp(ext,"ptx") ||
52638                  !cimg::strcasecmp(ext,"raf") ||
52639                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
52640         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
52641         else if (!cimg::strcasecmp(ext,"heic") ||
52642                  !cimg::strcasecmp(ext,"avif")) load_heif(filename);
52643 
52644         // 3D binary formats
52645         else if (!cimg::strcasecmp(ext,"dcm") ||
52646                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
52647         else if (!cimg::strcasecmp(ext,"hdr") ||
52648                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
52649         else if (!cimg::strcasecmp(ext,"par") ||
52650                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
52651         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
52652         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
52653         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
52654         else if (!cimg::strcasecmp(ext,"cimg") ||
52655                  !cimg::strcasecmp(ext,"cimgz") ||
52656                  !*ext)  return load_cimg(filename);
52657 
52658         // Archive files
52659         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
52660 
52661         // Image sequences
52662         else if (!cimg::strcasecmp(ext,"avi") ||
52663                  !cimg::strcasecmp(ext,"mov") ||
52664                  !cimg::strcasecmp(ext,"asf") ||
52665                  !cimg::strcasecmp(ext,"divx") ||
52666                  !cimg::strcasecmp(ext,"flv") ||
52667                  !cimg::strcasecmp(ext,"mpg") ||
52668                  !cimg::strcasecmp(ext,"m1v") ||
52669                  !cimg::strcasecmp(ext,"m2v") ||
52670                  !cimg::strcasecmp(ext,"m4v") ||
52671                  !cimg::strcasecmp(ext,"mjp") ||
52672                  !cimg::strcasecmp(ext,"mp4") ||
52673                  !cimg::strcasecmp(ext,"mkv") ||
52674                  !cimg::strcasecmp(ext,"mpe") ||
52675                  !cimg::strcasecmp(ext,"movie") ||
52676                  !cimg::strcasecmp(ext,"ogm") ||
52677                  !cimg::strcasecmp(ext,"ogg") ||
52678                  !cimg::strcasecmp(ext,"ogv") ||
52679                  !cimg::strcasecmp(ext,"qt") ||
52680                  !cimg::strcasecmp(ext,"rm") ||
52681                  !cimg::strcasecmp(ext,"vob") ||
52682                  !cimg::strcasecmp(ext,"webm") ||
52683                  !cimg::strcasecmp(ext,"wmv") ||
52684                  !cimg::strcasecmp(ext,"xvid") ||
52685                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
52686         else is_loaded = false;
52687       } catch (CImgIOException&) { is_loaded = false; }
52688 
52689       // If nothing loaded, try to guess file format from magic number in file.
52690       if (!is_loaded) {
52691         std::FILE *file = cimg::std_fopen(filename,"rb");
52692         if (!file) {
52693           cimg::exception_mode(omode);
52694           throw CImgIOException(_cimg_instance
52695                                 "load(): Failed to open file '%s'.",
52696                                 cimg_instance,
52697                                 filename);
52698         }
52699 
52700         const char *const f_type = cimg::ftype(file,filename);
52701         cimg::fclose(file);
52702         is_loaded = true;
52703         try {
52704           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
52705           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
52706           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
52707           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
52708           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
52709           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
52710           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
52711           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
52712           else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
52713           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
52714           else is_loaded = false;
52715         } catch (CImgIOException&) { is_loaded = false; }
52716       }
52717 
52718       // If nothing loaded, try to load file with other means.
52719       if (!is_loaded) {
52720         try {
52721           load_other(filename);
52722         } catch (CImgIOException&) {
52723           cimg::exception_mode(omode);
52724           throw CImgIOException(_cimg_instance
52725                                 "load(): Failed to recognize format of file '%s'.",
52726                                 cimg_instance,
52727                                 filename);
52728         }
52729       }
52730       cimg::exception_mode(omode);
52731       return *this;
52732     }
52733 
52734     //! Load image from a file \newinstance.
52735     static CImg<T> get_load(const char *const filename) {
52736       return CImg<T>().load(filename);
52737     }
52738 
52739     //! Load image from an ascii file.
52740     /**
52741        \param filename Filename, as a C -string.
52742     **/
52743     CImg<T>& load_ascii(const char *const filename) {
52744       return _load_ascii(0,filename);
52745     }
52746 
52747     //! Load image from an ascii file \inplace.
52748     static CImg<T> get_load_ascii(const char *const filename) {
52749       return CImg<T>().load_ascii(filename);
52750     }
52751 
52752     //! Load image from an ascii file \overloading.
52753     CImg<T>& load_ascii(std::FILE *const file) {
52754       return _load_ascii(file,0);
52755     }
52756 
52757     //! Loadimage from an ascii file \newinstance.
52758     static CImg<T> get_load_ascii(std::FILE *const file) {
52759       return CImg<T>().load_ascii(file);
52760     }
52761 
52762     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
52763       if (!file && !filename)
52764         throw CImgArgumentException(_cimg_instance
52765                                     "load_ascii(): Specified filename is (null).",
52766                                     cimg_instance);
52767 
52768       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52769       CImg<charT> line(256); *line = 0;
52770       int err = std::fscanf(nfile,"%255[^\n]",line._data);
52771       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
52772       cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
52773       err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
52774       if (!dx || !dy || !dz || !dc) {
52775         if (!file) cimg::fclose(nfile);
52776         throw CImgIOException(_cimg_instance
52777                               "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
52778                               "to (%u,%u,%u,%u).",
52779                               cimg_instance,
52780                               filename?filename:"(FILE*)",dx,dy,dz,dc);
52781       }
52782       assign(dx,dy,dz,dc);
52783       const ulongT siz = size();
52784       ulongT off = 0;
52785       double val;
52786       T *ptr = _data;
52787       for (err = 1, off = 0; off<siz && err==1; ++off) {
52788         err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
52789         *(ptr++) = (T)val;
52790       }
52791       if (err!=1)
52792         cimg::warn(_cimg_instance
52793                    "load_ascii(): Only %lu/%lu values read from file '%s'.",
52794                    cimg_instance,
52795                    off - 1,siz,filename?filename:"(FILE*)");
52796 
52797       if (!file) cimg::fclose(nfile);
52798       return *this;
52799     }
52800 
52801     //! Load image from a DLM file.
52802     /**
52803       \param filename Filename, as a C-string.
52804     **/
52805     CImg<T>& load_dlm(const char *const filename) {
52806       return _load_dlm(0,filename);
52807     }
52808 
52809     //! Load image from a DLM file \newinstance.
52810     static CImg<T> get_load_dlm(const char *const filename) {
52811       return CImg<T>().load_dlm(filename);
52812     }
52813 
52814     //! Load image from a DLM file \overloading.
52815     CImg<T>& load_dlm(std::FILE *const file) {
52816       return _load_dlm(file,0);
52817     }
52818 
52819     //! Load image from a DLM file \newinstance.
52820     static CImg<T> get_load_dlm(std::FILE *const file) {
52821       return CImg<T>().load_dlm(file);
52822     }
52823 
52824     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
52825       if (!file && !filename)
52826         throw CImgArgumentException(_cimg_instance
52827                                     "load_dlm(): Specified filename is (null).",
52828                                     cimg_instance);
52829 
52830       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
52831       CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
52832       unsigned int cdx = 0, dx = 0, dy = 0;
52833       int err = 0;
52834       double val;
52835       assign(256,256,1,1,(T)0);
52836       while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
52837         if (err>0) (*this)(cdx++,dy) = (T)val;
52838         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
52839         char c = 0;
52840         if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
52841           dx = std::max(cdx,dx);
52842           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
52843           cdx = 0;
52844         }
52845       }
52846       if (cdx && err==1) { dx = cdx; ++dy; }
52847       if (!dx || !dy) {
52848         if (!file) cimg::fclose(nfile);
52849         throw CImgIOException(_cimg_instance
52850                               "load_dlm(): Invalid DLM file '%s'.",
52851                               cimg_instance,
52852                               filename?filename:"(FILE*)");
52853       }
52854       resize(dx,dy,1,1,0);
52855       if (!file) cimg::fclose(nfile);
52856       return *this;
52857     }
52858 
52859     //! Load image from a BMP file.
52860     /**
52861        \param filename Filename, as a C-string.
52862     **/
52863     CImg<T>& load_bmp(const char *const filename) {
52864       return _load_bmp(0,filename);
52865     }
52866 
52867     //! Load image from a BMP file \newinstance.
52868     static CImg<T> get_load_bmp(const char *const filename) {
52869       return CImg<T>().load_bmp(filename);
52870     }
52871 
52872     //! Load image from a BMP file \overloading.
52873     CImg<T>& load_bmp(std::FILE *const file) {
52874       return _load_bmp(file,0);
52875     }
52876 
52877     //! Load image from a BMP file \newinstance.
52878     static CImg<T> get_load_bmp(std::FILE *const file) {
52879       return CImg<T>().load_bmp(file);
52880     }
52881 
52882     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
52883       if (!file && !filename)
52884         throw CImgArgumentException(_cimg_instance
52885                                     "load_bmp(): Specified filename is (null).",
52886                                     cimg_instance);
52887 
52888       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52889       CImg<ucharT> header(54);
52890       cimg::fread(header._data,54,nfile);
52891       if (*header!='B' || header[1]!='M') {
52892         if (!file) cimg::fclose(nfile);
52893         throw CImgIOException(_cimg_instance
52894                               "load_bmp(): Invalid BMP file '%s'.",
52895                               cimg_instance,
52896                               filename?filename:"(FILE*)");
52897       }
52898 
52899       // Read header and pixel buffer
52900       int
52901         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
52902         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
52903         header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
52904         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
52905         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
52906         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
52907         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
52908         bpp = header[0x1C] + (header[0x1D]<<8);
52909 
52910       if (!file_size || file_size==offset) {
52911         cimg::fseek(nfile,0,SEEK_END);
52912         file_size = (int)cimg::ftell(nfile);
52913         cimg::fseek(nfile,54,SEEK_SET);
52914       }
52915       if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
52916 
52917       const int
52918         dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
52919         align_bytes = (4 - dx_bytes%4)%4;
52920       const ulongT
52921         cimg_iobuffer = (ulongT)24*1024*1024,
52922         buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes);
52923 
52924       CImg<intT> colormap;
52925       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
52926       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
52927       const int xoffset = offset - 14 - header_size - 4*nb_colors;
52928       if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
52929 
52930       CImg<ucharT> buffer;
52931       if (buf_size<cimg_iobuffer) {
52932         buffer.assign(buf_size,1,1,1,0);
52933         cimg::fread(buffer._data,buf_size,nfile);
52934       } else buffer.assign(dx_bytes + align_bytes);
52935       unsigned char *ptrs = buffer;
52936 
52937       // Decompress buffer (if necessary)
52938       if (compression==1 || compression==2) {
52939         if (file)
52940           throw CImgIOException(_cimg_instance
52941                                 "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
52942                                 cimg_instance);
52943         else {
52944           if (!file) cimg::fclose(nfile);
52945           return load_other(filename);
52946         }
52947       }
52948 
52949       // Read pixel data
52950       assign(dx,cimg::abs(dy),1,3,0);
52951       switch (bpp) {
52952       case 1 : { // Monochrome
52953         if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
52954           if (buf_size>=cimg_iobuffer) {
52955             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52956             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52957           }
52958           unsigned char mask = 0x80, val = 0;
52959           cimg_forX(*this,x) {
52960             if (mask==0x80) val = *(ptrs++);
52961             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
52962             (*this)(x,y,2) = (T)*(col++);
52963             (*this)(x,y,1) = (T)*(col++);
52964             (*this)(x,y,0) = (T)*(col++);
52965             mask = cimg::ror(mask);
52966           }
52967           ptrs+=align_bytes;
52968         }
52969       } break;
52970       case 4 : { // 16 colors
52971         if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
52972           if (buf_size>=cimg_iobuffer) {
52973             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52974             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52975           }
52976           unsigned char mask = 0xF0, val = 0;
52977           cimg_forX(*this,x) {
52978             if (mask==0xF0) val = *(ptrs++);
52979             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
52980             const unsigned char *col = (unsigned char*)(colormap._data + color);
52981             (*this)(x,y,2) = (T)*(col++);
52982             (*this)(x,y,1) = (T)*(col++);
52983             (*this)(x,y,0) = (T)*(col++);
52984             mask = cimg::ror(mask,4);
52985           }
52986           ptrs+=align_bytes;
52987         }
52988       } break;
52989       case 8 : { // 256 colors
52990         if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
52991           if (buf_size>=cimg_iobuffer) {
52992             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
52993             cimg::fseek(nfile,align_bytes,SEEK_CUR);
52994           }
52995           cimg_forX(*this,x) {
52996             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
52997             (*this)(x,y,2) = (T)*(col++);
52998             (*this)(x,y,1) = (T)*(col++);
52999             (*this)(x,y,0) = (T)*(col++);
53000           }
53001           ptrs+=align_bytes;
53002         }
53003       } break;
53004       case 16 : { // 16 bits colors (RGB565)
53005         for (int y = height() - 1; y>=0; --y) {
53006           if (buf_size>=cimg_iobuffer) {
53007             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
53008             cimg::fseek(nfile,align_bytes,SEEK_CUR);
53009           }
53010           cimg_forX(*this,x) {
53011             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
53012             const unsigned short col = (unsigned short)c2<<8 | c1;
53013             (*this)(x,y,2) = (T)((col&0x1F)<<3);
53014             (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3);
53015             (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3);
53016           }
53017           ptrs+=align_bytes;
53018         }
53019       } break;
53020       case 24 : { // 24 bits colors
53021         for (int y = height() - 1; y>=0; --y) {
53022           if (buf_size>=cimg_iobuffer) {
53023             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
53024             cimg::fseek(nfile,align_bytes,SEEK_CUR);
53025           }
53026           cimg_forX(*this,x) {
53027             (*this)(x,y,2) = (T)*(ptrs++);
53028             (*this)(x,y,1) = (T)*(ptrs++);
53029             (*this)(x,y,0) = (T)*(ptrs++);
53030           }
53031           ptrs+=align_bytes;
53032         }
53033       } break;
53034       case 32 : { // 32 bits colors
53035         for (int y = height() - 1; y>=0; --y) {
53036           if (buf_size>=cimg_iobuffer) {
53037             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
53038             cimg::fseek(nfile,align_bytes,SEEK_CUR);
53039           }
53040           cimg_forX(*this,x) {
53041             (*this)(x,y,2) = (T)*(ptrs++);
53042             (*this)(x,y,1) = (T)*(ptrs++);
53043             (*this)(x,y,0) = (T)*(ptrs++);
53044             ++ptrs;
53045           }
53046           ptrs+=align_bytes;
53047         }
53048       } break;
53049       }
53050       if (dy<0) mirror('y');
53051       if (!file) cimg::fclose(nfile);
53052       return *this;
53053     }
53054 
53055     //! Load image from a JPEG file.
53056     /**
53057        \param filename Filename, as a C-string.
53058     **/
53059     CImg<T>& load_jpeg(const char *const filename) {
53060       return _load_jpeg(0,filename);
53061     }
53062 
53063     //! Load image from a JPEG file \newinstance.
53064     static CImg<T> get_load_jpeg(const char *const filename) {
53065       return CImg<T>().load_jpeg(filename);
53066     }
53067 
53068     //! Load image from a JPEG file \overloading.
53069     CImg<T>& load_jpeg(std::FILE *const file) {
53070       return _load_jpeg(file,0);
53071     }
53072 
53073     //! Load image from a JPEG file \newinstance.
53074     static CImg<T> get_load_jpeg(std::FILE *const file) {
53075       return CImg<T>().load_jpeg(file);
53076     }
53077 
53078     // Custom error handler for libjpeg.
53079 #ifdef cimg_use_jpeg
53080     struct _cimg_error_mgr {
53081       struct jpeg_error_mgr original;
53082       jmp_buf setjmp_buffer;
53083       char message[JMSG_LENGTH_MAX];
53084     };
53085 
53086     typedef struct _cimg_error_mgr *_cimg_error_ptr;
53087 
53088     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
53089       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
53090       (*cinfo->err->format_message)(cinfo,c_err->message);
53091       jpeg_destroy(cinfo);  // Clean memory and temp files
53092       longjmp(c_err->setjmp_buffer,1);
53093     }
53094 #endif
53095 
53096     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
53097       if (!file && !filename)
53098         throw CImgArgumentException(_cimg_instance
53099                                     "load_jpeg(): Specified filename is (null).",
53100                                     cimg_instance);
53101 
53102 #ifndef cimg_use_jpeg
53103       if (file)
53104         throw CImgIOException(_cimg_instance
53105                               "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
53106                               cimg_instance);
53107       else return load_other(filename);
53108 #else
53109 
53110       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53111       struct jpeg_decompress_struct cinfo;
53112       struct _cimg_error_mgr jerr;
53113       cinfo.err = jpeg_std_error(&jerr.original);
53114       jerr.original.error_exit = _cimg_jpeg_error_exit;
53115       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
53116         if (!file) cimg::fclose(nfile);
53117         throw CImgIOException(_cimg_instance
53118                              "load_jpeg(): Error message returned by libjpeg: %s.",
53119                              cimg_instance,jerr.message);
53120       }
53121 
53122       jpeg_create_decompress(&cinfo);
53123       jpeg_stdio_src(&cinfo,nfile);
53124       jpeg_read_header(&cinfo,TRUE);
53125       jpeg_start_decompress(&cinfo);
53126 
53127       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
53128         if (!file) {
53129           cimg::fclose(nfile);
53130           return load_other(filename);
53131         } else
53132           throw CImgIOException(_cimg_instance
53133                                 "load_jpeg(): Failed to load JPEG data from file '%s'.",
53134                                 cimg_instance,filename?filename:"(FILE*)");
53135       }
53136       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
53137       JSAMPROW row_pointer[1];
53138       try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
53139       catch (...) { if (!file) cimg::fclose(nfile); throw; }
53140       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
53141         *ptr_a = _data + 3UL*_width*_height;
53142       while (cinfo.output_scanline<cinfo.output_height) {
53143         *row_pointer = buffer._data;
53144         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
53145           cimg::warn(_cimg_instance
53146                      "load_jpeg(): Incomplete data in file '%s'.",
53147                      cimg_instance,filename?filename:"(FILE*)");
53148           break;
53149         }
53150         const unsigned char *ptrs = buffer._data;
53151         switch (_spectrum) {
53152         case 1 : {
53153           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
53154         } break;
53155         case 3 : {
53156           cimg_forX(*this,x) {
53157             *(ptr_r++) = (T)*(ptrs++);
53158             *(ptr_g++) = (T)*(ptrs++);
53159             *(ptr_b++) = (T)*(ptrs++);
53160           }
53161         } break;
53162         case 4 : {
53163           cimg_forX(*this,x) {
53164             *(ptr_r++) = (T)*(ptrs++);
53165             *(ptr_g++) = (T)*(ptrs++);
53166             *(ptr_b++) = (T)*(ptrs++);
53167             *(ptr_a++) = (T)*(ptrs++);
53168           }
53169         } break;
53170         }
53171       }
53172       jpeg_finish_decompress(&cinfo);
53173       jpeg_destroy_decompress(&cinfo);
53174       if (!file) cimg::fclose(nfile);
53175       return *this;
53176 #endif
53177     }
53178 
53179     //! Load image from a file, using Magick++ library.
53180     /**
53181        \param filename Filename, as a C-string.
53182     **/
53183     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
53184     //   This is experimental code, not much tested, use with care.
53185     CImg<T>& load_magick(const char *const filename) {
53186       if (!filename)
53187         throw CImgArgumentException(_cimg_instance
53188                                     "load_magick(): Specified filename is (null).",
53189                                     cimg_instance);
53190 
53191 #ifdef cimg_use_magick
53192       Magick::Image image(filename);
53193       const unsigned int W = image.size().width(), H = image.size().height();
53194       switch (image.type()) {
53195       case Magick::PaletteMatteType :
53196       case Magick::TrueColorMatteType :
53197       case Magick::ColorSeparationType : {
53198         assign(W,H,1,4);
53199         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);
53200         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
53201         for (ulongT off = (ulongT)W*H; off; --off) {
53202           *(ptr_r++) = (T)(pixels->red);
53203           *(ptr_g++) = (T)(pixels->green);
53204           *(ptr_b++) = (T)(pixels->blue);
53205           *(ptr_a++) = (T)(pixels->opacity);
53206           ++pixels;
53207         }
53208       } break;
53209       case Magick::PaletteType :
53210       case Magick::TrueColorType : {
53211         assign(W,H,1,3);
53212         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
53213         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
53214         for (ulongT off = (ulongT)W*H; off; --off) {
53215           *(ptr_r++) = (T)(pixels->red);
53216           *(ptr_g++) = (T)(pixels->green);
53217           *(ptr_b++) = (T)(pixels->blue);
53218           ++pixels;
53219         }
53220       } break;
53221       case Magick::GrayscaleMatteType : {
53222         assign(W,H,1,2);
53223         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
53224         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
53225         for (ulongT off = (ulongT)W*H; off; --off) {
53226           *(ptr_r++) = (T)(pixels->red);
53227           *(ptr_a++) = (T)(pixels->opacity);
53228           ++pixels;
53229         }
53230       } break;
53231       default : {
53232         assign(W,H,1,1);
53233         T *ptr_r = data(0,0,0,0);
53234         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
53235         for (ulongT off = (ulongT)W*H; off; --off) {
53236           *(ptr_r++) = (T)(pixels->red);
53237           ++pixels;
53238         }
53239       }
53240       }
53241       return *this;
53242 #else
53243       throw CImgIOException(_cimg_instance
53244                             "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
53245                             cimg_instance,
53246                             filename);
53247 #endif
53248     }
53249 
53250     //! Load image from a file, using Magick++ library \newinstance.
53251     static CImg<T> get_load_magick(const char *const filename) {
53252       return CImg<T>().load_magick(filename);
53253     }
53254 
53255     //! Load image from a PNG file.
53256     /**
53257        \param filename Filename, as a C-string.
53258        \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
53259     **/
53260     CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_value=0) {
53261       return _load_png(0,filename,bits_per_value);
53262     }
53263 
53264     //! Load image from a PNG file \newinstance.
53265     static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_value=0) {
53266       return CImg<T>().load_png(filename,bits_per_value);
53267     }
53268 
53269     //! Load image from a PNG file \overloading.
53270     CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
53271       return _load_png(file,0,bits_per_value);
53272     }
53273 
53274     //! Load image from a PNG file \newinstance.
53275     static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
53276       return CImg<T>().load_png(file,bits_per_value);
53277     }
53278 
53279     // (Note: Most of this function has been written by Eric Fausett)
53280     CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) {
53281       if (!file && !filename)
53282         throw CImgArgumentException(_cimg_instance
53283                                     "load_png(): Specified filename is (null).",
53284                                     cimg_instance);
53285 
53286 #ifndef cimg_use_png
53287       cimg::unused(bits_per_value);
53288       if (file)
53289         throw CImgIOException(_cimg_instance
53290                               "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
53291                               cimg_instance);
53292 
53293       else return load_other(filename);
53294 #else
53295       // Open file and check for PNG validity
53296 #if defined __GNUC__
53297       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
53298       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
53299 #else
53300       const char *nfilename = filename;
53301       std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
53302 #endif
53303       unsigned char pngCheck[8] = { 0 };
53304       cimg::fread(pngCheck,8,(std::FILE*)nfile);
53305       if (png_sig_cmp(pngCheck,0,8)) {
53306         if (!file) cimg::fclose(nfile);
53307         throw CImgIOException(_cimg_instance
53308                               "load_png(): Invalid PNG file '%s'.",
53309                               cimg_instance,
53310                               nfilename?nfilename:"(FILE*)");
53311       }
53312 
53313       // Setup PNG structures for read
53314       png_voidp user_error_ptr = 0;
53315       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
53316       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
53317       if (!png_ptr) {
53318         if (!file) cimg::fclose(nfile);
53319         throw CImgIOException(_cimg_instance
53320                               "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
53321                               cimg_instance,
53322                               nfilename?nfilename:"(FILE*)");
53323       }
53324       png_infop info_ptr = png_create_info_struct(png_ptr);
53325       if (!info_ptr) {
53326         if (!file) cimg::fclose(nfile);
53327         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
53328         throw CImgIOException(_cimg_instance
53329                               "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
53330                               cimg_instance,
53331                               nfilename?nfilename:"(FILE*)");
53332       }
53333       png_infop end_info = png_create_info_struct(png_ptr);
53334       if (!end_info) {
53335         if (!file) cimg::fclose(nfile);
53336         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
53337         throw CImgIOException(_cimg_instance
53338                               "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
53339                               cimg_instance,
53340                               nfilename?nfilename:"(FILE*)");
53341       }
53342 
53343       // Error handling callback for png file reading
53344       if (setjmp(png_jmpbuf(png_ptr))) {
53345         if (!file) cimg::fclose((std::FILE*)nfile);
53346         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
53347         throw CImgIOException(_cimg_instance
53348                               "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
53349                               cimg_instance,
53350                               nfilename?nfilename:"(FILE*)");
53351       }
53352       png_init_io(png_ptr, nfile);
53353       png_set_sig_bytes(png_ptr, 8);
53354 
53355       // Get PNG Header Info up to data block
53356       png_read_info(png_ptr,info_ptr);
53357       png_uint_32 W, H;
53358       int bit_depth, color_type, interlace_type;
53359       bool is_gray = false;
53360       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
53361       if (bits_per_value) *bits_per_value = (unsigned int)bit_depth;
53362 
53363       // Transforms to unify image data
53364       if (color_type==PNG_COLOR_TYPE_PALETTE) {
53365         png_set_palette_to_rgb(png_ptr);
53366         color_type = PNG_COLOR_TYPE_RGB;
53367         bit_depth = 8;
53368       }
53369       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
53370         png_set_expand_gray_1_2_4_to_8(png_ptr);
53371         is_gray = true;
53372         bit_depth = 8;
53373       }
53374       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
53375         png_set_tRNS_to_alpha(png_ptr);
53376         color_type |= PNG_COLOR_MASK_ALPHA;
53377       }
53378       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
53379         png_set_gray_to_rgb(png_ptr);
53380         color_type |= PNG_COLOR_MASK_COLOR;
53381         is_gray = true;
53382       }
53383       if (color_type==PNG_COLOR_TYPE_RGB)
53384         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
53385 
53386       png_read_update_info(png_ptr,info_ptr);
53387       if (bit_depth!=8 && bit_depth!=16) {
53388         if (!file) cimg::fclose(nfile);
53389         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
53390         throw CImgIOException(_cimg_instance
53391                               "load_png(): Invalid bit depth %u in file '%s'.",
53392                               cimg_instance,
53393                               bit_depth,nfilename?nfilename:"(FILE*)");
53394       }
53395       const int byte_depth = bit_depth>>3;
53396 
53397       // Allocate memory for image reading
53398       png_bytep *const imgData = new png_bytep[H];
53399       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
53400       png_read_image(png_ptr,imgData);
53401       png_read_end(png_ptr,end_info);
53402 
53403       // Read pixel data
53404       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
53405         if (!file) cimg::fclose(nfile);
53406         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
53407         throw CImgIOException(_cimg_instance
53408                               "load_png(): Invalid color coding type %u in file '%s'.",
53409                               cimg_instance,
53410                               color_type,nfilename?nfilename:"(FILE*)");
53411       }
53412       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
53413       try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
53414       catch (...) { if (!file) cimg::fclose(nfile); throw; }
53415       T
53416         *ptr_r = data(0,0,0,0),
53417         *ptr_g = is_gray?0:data(0,0,0,1),
53418         *ptr_b = is_gray?0:data(0,0,0,2),
53419         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
53420       switch (bit_depth) {
53421       case 8 : {
53422         cimg_forY(*this,y) {
53423           const unsigned char *ptrs = (unsigned char*)imgData[y];
53424           cimg_forX(*this,x) {
53425             *(ptr_r++) = (T)*(ptrs++);
53426             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
53427             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
53428             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
53429           }
53430         }
53431       } break;
53432       case 16 : {
53433         cimg_forY(*this,y) {
53434           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
53435           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
53436           cimg_forX(*this,x) {
53437             *(ptr_r++) = (T)*(ptrs++);
53438             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
53439             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
53440             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
53441           }
53442         }
53443       } break;
53444       }
53445       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
53446 
53447       // Deallocate image read memory
53448       cimg_forY(*this,n) delete[] imgData[n];
53449       delete[] imgData;
53450       if (!file) cimg::fclose(nfile);
53451       return *this;
53452 #endif
53453     }
53454 
53455     //! Load image from a PNM file.
53456     /**
53457       \param filename Filename, as a C-string.
53458     **/
53459     CImg<T>& load_pnm(const char *const filename) {
53460       return _load_pnm(0,filename);
53461     }
53462 
53463     //! Load image from a PNM file \newinstance.
53464     static CImg<T> get_load_pnm(const char *const filename) {
53465       return CImg<T>().load_pnm(filename);
53466     }
53467 
53468     //! Load image from a PNM file \overloading.
53469     CImg<T>& load_pnm(std::FILE *const file) {
53470       return _load_pnm(file,0);
53471     }
53472 
53473     //! Load image from a PNM file \newinstance.
53474     static CImg<T> get_load_pnm(std::FILE *const file) {
53475       return CImg<T>().load_pnm(file);
53476     }
53477 
53478     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
53479       if (!file && !filename)
53480         throw CImgArgumentException(_cimg_instance
53481                                     "load_pnm(): Specified filename is (null).",
53482                                     cimg_instance);
53483 
53484       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53485       unsigned int ppm_type, W, H, D = 1, colormax = 255;
53486       CImg<charT> item(16384,1,1,1,0);
53487       int err, rval, gval, bval;
53488       const longT cimg_iobuffer = (longT)24*1024*1024;
53489       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53490       if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
53491         if (!file) cimg::fclose(nfile);
53492         throw CImgIOException(_cimg_instance
53493                               "load_pnm(): PNM header not found in file '%s'.",
53494                               cimg_instance,
53495                               filename?filename:"(FILE*)");
53496       }
53497       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53498       if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
53499         if (!file) cimg::fclose(nfile);
53500         throw CImgIOException(_cimg_instance
53501                               "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
53502                               cimg_instance,
53503                               filename?filename:"(FILE*)");
53504       }
53505       if (ppm_type!=1 && ppm_type!=4) {
53506         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
53507           while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53508           if (cimg_sscanf(item,"%u",&colormax)!=1)
53509             cimg::warn(_cimg_instance
53510                        "load_pnm(): COLORMAX field is undefined in file '%s'.",
53511                        cimg_instance,
53512                        filename?filename:"(FILE*)");
53513         } else { colormax = D; D = 1; }
53514       }
53515       std::fgetc(nfile);
53516 
53517       if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension
53518         const cimg_int64 siz = cimg::fsize(filename);
53519         if (W*H*D>siz)
53520           throw CImgIOException(_cimg_instance
53521                                 "load_pnm(): Specified image dimensions in file '%s' exceed file size.",
53522                                 cimg_instance,
53523                                 filename);
53524       }
53525 
53526       switch (ppm_type) {
53527       case 1 : { // 2D B&W ascii
53528         assign(W,H,1,1);
53529         T* ptrd = _data;
53530         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
53531       } break;
53532       case 2 : { // 2D grey ascii
53533         assign(W,H,1,1);
53534         T* ptrd = _data;
53535         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
53536       } break;
53537       case 3 : { // 2D color ascii
53538         assign(W,H,1,3);
53539         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
53540         cimg_forXY(*this,x,y) {
53541           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
53542             *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
53543           } else break;
53544         }
53545       } break;
53546       case 4 : { // 2D b&w binary (support 3D PINK extension)
53547         CImg<ucharT> raw;
53548         assign(W,H,D,1);
53549         T *ptrd = data(0,0,0,0);
53550         unsigned int w = 0, h = 0, d = 0;
53551         for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
53552           raw.assign(std::min(to_read,cimg_iobuffer));
53553           cimg::fread(raw._data,raw._width,nfile);
53554           to_read-=raw._width;
53555           const unsigned char *ptrs = raw._data;
53556           unsigned char mask = 0, val = 0;
53557           for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
53558             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
53559             *(ptrd++) = (T)((val&mask)?0:255);
53560             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
53561           }
53562         }
53563       } break;
53564       case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension)
53565         if (colormax<256) { // 8 bits
53566           CImg<ucharT> raw;
53567           assign(W,H,D,1);
53568           T *ptrd = data(0,0,0,0);
53569           for (longT to_read = (longT)size(); to_read>0; ) {
53570             raw.assign(std::min(to_read,cimg_iobuffer));
53571             cimg::fread(raw._data,raw._width,nfile);
53572             to_read-=raw._width;
53573             const unsigned char *ptrs = raw._data;
53574             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53575           }
53576         } else { // 16 bits
53577           CImg<ushortT> raw;
53578           assign(W,H,D,1);
53579           T *ptrd = data(0,0,0,0);
53580           for (longT to_read = (longT)size(); to_read>0; ) {
53581             raw.assign(std::min(to_read,cimg_iobuffer/2));
53582             cimg::fread(raw._data,raw._width,nfile);
53583             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
53584             to_read-=raw._width;
53585             const unsigned short *ptrs = raw._data;
53586             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53587           }
53588         }
53589       } break;
53590       case 6 : { // 2D color binary
53591         if (colormax<256) { // 8 bits
53592           CImg<ucharT> raw;
53593           assign(W,H,1,3);
53594           T
53595             *ptr_r = data(0,0,0,0),
53596             *ptr_g = data(0,0,0,1),
53597             *ptr_b = data(0,0,0,2);
53598           for (longT to_read = (longT)size(); to_read>0; ) {
53599             raw.assign(std::min(to_read,cimg_iobuffer));
53600             cimg::fread(raw._data,raw._width,nfile);
53601             to_read-=raw._width;
53602             const unsigned char *ptrs = raw._data;
53603             for (ulongT off = (ulongT)raw._width/3; off; --off) {
53604               *(ptr_r++) = (T)*(ptrs++);
53605               *(ptr_g++) = (T)*(ptrs++);
53606               *(ptr_b++) = (T)*(ptrs++);
53607             }
53608           }
53609         } else { // 16 bits
53610           CImg<ushortT> raw;
53611           assign(W,H,1,3);
53612           T
53613             *ptr_r = data(0,0,0,0),
53614             *ptr_g = data(0,0,0,1),
53615             *ptr_b = data(0,0,0,2);
53616           for (longT to_read = (longT)size(); to_read>0; ) {
53617             raw.assign(std::min(to_read,cimg_iobuffer/2));
53618             cimg::fread(raw._data,raw._width,nfile);
53619             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
53620             to_read-=raw._width;
53621             const unsigned short *ptrs = raw._data;
53622             for (ulongT off = (ulongT)raw._width/3; off; --off) {
53623               *(ptr_r++) = (T)*(ptrs++);
53624               *(ptr_g++) = (T)*(ptrs++);
53625               *(ptr_b++) = (T)*(ptrs++);
53626             }
53627           }
53628         }
53629       } break;
53630       case 8 : { // 2D/3D grey binary with int32 integers (PINK extension)
53631         CImg<intT> raw;
53632         assign(W,H,D,1);
53633         T *ptrd = data(0,0,0,0);
53634         for (longT to_read = (longT)size(); to_read>0; ) {
53635           raw.assign(std::min(to_read,cimg_iobuffer));
53636           cimg::fread(raw._data,raw._width,nfile);
53637           to_read-=raw._width;
53638           const int *ptrs = raw._data;
53639           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53640         }
53641       } break;
53642       case 9 : { // 2D/3D grey binary with float values (PINK extension)
53643         CImg<floatT> raw;
53644         assign(W,H,D,1);
53645         T *ptrd = data(0,0,0,0);
53646         for (longT to_read = (longT)size(); to_read>0; ) {
53647           raw.assign(std::min(to_read,cimg_iobuffer));
53648           cimg::fread(raw._data,raw._width,nfile);
53649           to_read-=raw._width;
53650           const float *ptrs = raw._data;
53651           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
53652         }
53653       } break;
53654       default :
53655         assign();
53656         if (!file) cimg::fclose(nfile);
53657         throw CImgIOException(_cimg_instance
53658                               "load_pnm(): PNM type 'P%d' found, but type is not supported.",
53659                               cimg_instance,
53660                               filename?filename:"(FILE*)",ppm_type);
53661       }
53662       if (!file) cimg::fclose(nfile);
53663       return *this;
53664     }
53665 
53666     //! Load image from a PFM file.
53667     /**
53668       \param filename Filename, as a C-string.
53669     **/
53670     CImg<T>& load_pfm(const char *const filename) {
53671       return _load_pfm(0,filename);
53672     }
53673 
53674     //! Load image from a PFM file \newinstance.
53675     static CImg<T> get_load_pfm(const char *const filename) {
53676       return CImg<T>().load_pfm(filename);
53677     }
53678 
53679     //! Load image from a PFM file \overloading.
53680     CImg<T>& load_pfm(std::FILE *const file) {
53681       return _load_pfm(file,0);
53682     }
53683 
53684     //! Load image from a PFM file \newinstance.
53685     static CImg<T> get_load_pfm(std::FILE *const file) {
53686       return CImg<T>().load_pfm(file);
53687     }
53688 
53689     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
53690       if (!file && !filename)
53691         throw CImgArgumentException(_cimg_instance
53692                                     "load_pfm(): Specified filename is (null).",
53693                                     cimg_instance);
53694 
53695       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53696       char pfm_type;
53697       CImg<charT> item(16384,1,1,1,0);
53698       int W = 0, H = 0, err = 0;
53699       double scale = 0;
53700       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53701       if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
53702         if (!file) cimg::fclose(nfile);
53703         throw CImgIOException(_cimg_instance
53704                               "load_pfm(): PFM header not found in file '%s'.",
53705                               cimg_instance,
53706                               filename?filename:"(FILE*)");
53707       }
53708       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53709       if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
53710         if (!file) cimg::fclose(nfile);
53711         throw CImgIOException(_cimg_instance
53712                               "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
53713                               cimg_instance,
53714                               filename?filename:"(FILE*)");
53715       } else if (W<=0 || H<=0) {
53716         if (!file) cimg::fclose(nfile);
53717         throw CImgIOException(_cimg_instance
53718                               "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
53719                               cimg_instance,W,H,
53720                               filename?filename:"(FILE*)");
53721       }
53722       if (err==2) {
53723         while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
53724         if (cimg_sscanf(item,"%lf",&scale)!=1)
53725           cimg::warn(_cimg_instance
53726                      "load_pfm(): SCALE field is undefined in file '%s'.",
53727                      cimg_instance,
53728                      filename?filename:"(FILE*)");
53729       }
53730       std::fgetc(nfile);
53731       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
53732       if (is_color) {
53733         assign(W,H,1,3,(T)0);
53734         CImg<floatT> buf(3*W);
53735         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
53736         cimg_forY(*this,y) {
53737           cimg::fread(buf._data,3*W,nfile);
53738           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
53739           const float *ptrs = buf._data;
53740           cimg_forX(*this,x) {
53741             *(ptr_r++) = (T)*(ptrs++);
53742             *(ptr_g++) = (T)*(ptrs++);
53743             *(ptr_b++) = (T)*(ptrs++);
53744           }
53745         }
53746       } else {
53747         assign(W,H,1,1,(T)0);
53748         CImg<floatT> buf(W);
53749         T *ptrd = data(0,0,0,0);
53750         cimg_forY(*this,y) {
53751           cimg::fread(buf._data,W,nfile);
53752           if (is_inverted) cimg::invert_endianness(buf._data,W);
53753           const float *ptrs = buf._data;
53754           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
53755         }
53756       }
53757       if (!file) cimg::fclose(nfile);
53758       return mirror('y');  // Most of the .pfm files are flipped along the y-axis
53759     }
53760 
53761     //! Load image from a RGB file.
53762     /**
53763       \param filename Filename, as a C-string.
53764       \param dimw Width of the image buffer.
53765       \param dimh Height of the image buffer.
53766     **/
53767     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53768       return _load_rgb(0,filename,dimw,dimh);
53769     }
53770 
53771     //! Load image from a RGB file \newinstance.
53772     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53773       return CImg<T>().load_rgb(filename,dimw,dimh);
53774     }
53775 
53776     //! Load image from a RGB file \overloading.
53777     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53778       return _load_rgb(file,0,dimw,dimh);
53779     }
53780 
53781     //! Load image from a RGB file \newinstance.
53782     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53783       return CImg<T>().load_rgb(file,dimw,dimh);
53784     }
53785 
53786     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
53787                        const unsigned int dimw, const unsigned int dimh) {
53788       if (!file && !filename)
53789         throw CImgArgumentException(_cimg_instance
53790                                     "load_rgb(): Specified filename is (null).",
53791                                     cimg_instance);
53792 
53793       if (!dimw || !dimh) return assign();
53794       const longT cimg_iobuffer = (longT)24*1024*1024;
53795       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53796       CImg<ucharT> raw;
53797       assign(dimw,dimh,1,3);
53798       T
53799         *ptr_r = data(0,0,0,0),
53800         *ptr_g = data(0,0,0,1),
53801         *ptr_b = data(0,0,0,2);
53802       for (longT to_read = (longT)size(); to_read>0; ) {
53803         raw.assign(std::min(to_read,cimg_iobuffer));
53804         cimg::fread(raw._data,raw._width,nfile);
53805         to_read-=raw._width;
53806         const unsigned char *ptrs = raw._data;
53807         for (ulongT off = raw._width/3UL; off; --off) {
53808           *(ptr_r++) = (T)*(ptrs++);
53809           *(ptr_g++) = (T)*(ptrs++);
53810           *(ptr_b++) = (T)*(ptrs++);
53811         }
53812       }
53813       if (!file) cimg::fclose(nfile);
53814       return *this;
53815     }
53816 
53817     //! Load image from a RGBA file.
53818     /**
53819        \param filename Filename, as a C-string.
53820        \param dimw Width of the image buffer.
53821        \param dimh Height of the image buffer.
53822     **/
53823     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53824       return _load_rgba(0,filename,dimw,dimh);
53825     }
53826 
53827     //! Load image from a RGBA file \newinstance.
53828     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
53829       return CImg<T>().load_rgba(filename,dimw,dimh);
53830     }
53831 
53832     //! Load image from a RGBA file \overloading.
53833     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53834       return _load_rgba(file,0,dimw,dimh);
53835     }
53836 
53837     //! Load image from a RGBA file \newinstance.
53838     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
53839       return CImg<T>().load_rgba(file,dimw,dimh);
53840     }
53841 
53842     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
53843                         const unsigned int dimw, const unsigned int dimh) {
53844       if (!file && !filename)
53845         throw CImgArgumentException(_cimg_instance
53846                                     "load_rgba(): Specified filename is (null).",
53847                                     cimg_instance);
53848 
53849       if (!dimw || !dimh) return assign();
53850       const longT cimg_iobuffer = (longT)24*1024*1024;
53851       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53852       CImg<ucharT> raw;
53853       assign(dimw,dimh,1,4);
53854       T
53855         *ptr_r = data(0,0,0,0),
53856         *ptr_g = data(0,0,0,1),
53857         *ptr_b = data(0,0,0,2),
53858         *ptr_a = data(0,0,0,3);
53859       for (longT to_read = (longT)size(); to_read>0; ) {
53860         raw.assign(std::min(to_read,cimg_iobuffer));
53861         cimg::fread(raw._data,raw._width,nfile);
53862         to_read-=raw._width;
53863         const unsigned char *ptrs = raw._data;
53864         for (ulongT off = raw._width/4UL; off; --off) {
53865           *(ptr_r++) = (T)*(ptrs++);
53866           *(ptr_g++) = (T)*(ptrs++);
53867           *(ptr_b++) = (T)*(ptrs++);
53868           *(ptr_a++) = (T)*(ptrs++);
53869         }
53870       }
53871       if (!file) cimg::fclose(nfile);
53872       return *this;
53873     }
53874 
53875     //! Load image from a TIFF file.
53876     /**
53877        \param filename Filename, as a C-string.
53878        \param first_frame First frame to read (for multi-pages tiff).
53879        \param last_frame Last frame to read (for multi-pages tiff).
53880        \param step_frame Step value of frame reading.
53881        \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
53882        \param[out] voxel_size Voxel size, as stored in the filename.
53883        \param[out] description Description, as stored in the filename.
53884        \note
53885        - libtiff support is enabled by defining the precompilation
53886         directive \c cimg_use_tif.
53887        - When libtiff is enabled, 2D and 3D (multipage) several
53888         channel per pixel are supported for
53889         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
53890        - If \c cimg_use_tiff is not defined at compile time the
53891         function uses CImg<T>& load_other(const char*).
53892      **/
53893     CImg<T>& load_tiff(const char *const filename,
53894                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53895                        const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
53896                        float *const voxel_size=0, CImg<charT> *const description=0) {
53897       if (!filename)
53898         throw CImgArgumentException(_cimg_instance
53899                                     "load_tiff(): Specified filename is (null).",
53900                                     cimg_instance);
53901 
53902       const unsigned int
53903         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
53904         nstep_frame = step_frame?step_frame:1;
53905       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
53906 
53907 #ifndef cimg_use_tiff
53908       cimg::unused(bits_per_value,voxel_size,description);
53909       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
53910         throw CImgArgumentException(_cimg_instance
53911                                     "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
53912                                     cimg_instance,
53913                                     filename);
53914       return load_other(filename);
53915 #else
53916 #if cimg_verbosity<3
53917       TIFFSetWarningHandler(0);
53918       TIFFSetErrorHandler(0);
53919 #endif
53920       TIFF *tif = TIFFOpen(filename,"r");
53921       if (tif) {
53922         unsigned int nb_images = 0;
53923         do ++nb_images; while (TIFFReadDirectory(tif));
53924         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
53925           cimg::warn(_cimg_instance
53926                      "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
53927                      cimg_instance,
53928                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
53929 
53930         if (nfirst_frame>=nb_images) return assign();
53931         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
53932         TIFFSetDirectory(tif,0);
53933         CImg<T> frame;
53934         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
53935           frame._load_tiff(tif,l,bits_per_value,voxel_size,description);
53936           if (l==nfirst_frame)
53937             assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
53938           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
53939             resize(std::max(frame._width,_width),
53940                    std::max(frame._height,_height),-100,
53941                    std::max(frame._spectrum,_spectrum),0);
53942           draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
53943         }
53944         TIFFClose(tif);
53945       } else throw CImgIOException(_cimg_instance
53946                                    "load_tiff(): Failed to open file '%s'.",
53947                                    cimg_instance,
53948                                    filename);
53949       return *this;
53950 #endif
53951     }
53952 
53953     //! Load image from a TIFF file \newinstance.
53954     static CImg<T> get_load_tiff(const char *const filename,
53955                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53956                                  const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
53957                                  float *const voxel_size=0, CImg<charT> *const description=0) {
53958       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
53959     }
53960 
53961     // (Original contribution by Jerome Boulanger).
53962 #ifdef cimg_use_tiff
53963     template<typename t>
53964     void _load_tiff_tiled_contig(TIFF *const tif, const uint16_t samplesperpixel,
53965                                  const uint32_t nx, const uint32_t ny, const uint32_t tw, const uint32_t th) {
53966       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
53967       if (buf) {
53968         for (unsigned int row = 0; row<ny; row+=th)
53969           for (unsigned int col = 0; col<nx; col+=tw) {
53970             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
53971               _TIFFfree(buf); TIFFClose(tif);
53972               throw CImgIOException(_cimg_instance
53973                                     "load_tiff(): Invalid tile in file '%s'.",
53974                                     cimg_instance,
53975                                     TIFFFileName(tif));
53976             }
53977             const t *ptr = buf;
53978             for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
53979               for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
53980                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
53981                   (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
53982           }
53983         _TIFFfree(buf);
53984       }
53985     }
53986 
53987     template<typename t>
53988     void _load_tiff_tiled_separate(TIFF *const tif, const uint16_t samplesperpixel,
53989                                    const uint32_t nx, const uint32_t ny, const uint32_t tw, const uint32_t th) {
53990       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
53991       if (buf) {
53992         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
53993           for (unsigned int row = 0; row<ny; row+=th)
53994             for (unsigned int col = 0; col<nx; col+=tw) {
53995               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
53996                 _TIFFfree(buf); TIFFClose(tif);
53997                 throw CImgIOException(_cimg_instance
53998                                       "load_tiff(): Invalid tile in file '%s'.",
53999                                       cimg_instance,
54000                                       TIFFFileName(tif));
54001               }
54002               const t *ptr = buf;
54003               for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
54004                 for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
54005                   (*this)(cc,rr,vv) = (T)*(ptr++);
54006             }
54007         _TIFFfree(buf);
54008       }
54009     }
54010 
54011     template<typename t>
54012     void _load_tiff_contig(TIFF *const tif, const uint16_t samplesperpixel, const uint32_t nx, const uint32_t ny) {
54013       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
54014       if (buf) {
54015         uint32_t row, rowsperstrip = (uint32_t)-1;
54016         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
54017         for (row = 0; row<ny; row+= rowsperstrip) {
54018           uint32_t nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
54019           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
54020           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
54021             _TIFFfree(buf); TIFFClose(tif);
54022             throw CImgIOException(_cimg_instance
54023                                   "load_tiff(): Invalid strip in file '%s'.",
54024                                   cimg_instance,
54025                                   TIFFFileName(tif));
54026           }
54027           const t *ptr = buf;
54028           for (unsigned int rr = 0; rr<nrow; ++rr)
54029             for (unsigned int cc = 0; cc<nx; ++cc)
54030               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
54031         }
54032         _TIFFfree(buf);
54033       }
54034     }
54035 
54036     template<typename t>
54037     void _load_tiff_separate(TIFF *const tif, const uint16_t samplesperpixel, const uint32_t nx, const uint32_t ny) {
54038       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
54039       if (buf) {
54040         uint32_t row, rowsperstrip = (uint32_t)-1;
54041         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
54042         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
54043           for (row = 0; row<ny; row+= rowsperstrip) {
54044             uint32_t nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
54045             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
54046             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
54047               _TIFFfree(buf); TIFFClose(tif);
54048               throw CImgIOException(_cimg_instance
54049                                     "load_tiff(): Invalid strip in file '%s'.",
54050                                     cimg_instance,
54051                                     TIFFFileName(tif));
54052             }
54053             const t *ptr = buf;
54054             for (unsigned int rr = 0;rr<nrow; ++rr)
54055               for (unsigned int cc = 0; cc<nx; ++cc)
54056                 (*this)(cc,row + rr,vv) = (T)*(ptr++);
54057           }
54058         _TIFFfree(buf);
54059       }
54060     }
54061 
54062     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value,
54063                         float *const voxel_size, CImg<charT> *const description) {
54064       if (!TIFFSetDirectory(tif,directory)) return assign();
54065       uint16_t samplesperpixel = 1, bitspersample = 8, photo = 0;
54066       uint16_t sampleformat = 1;
54067       uint32_t nx = 1, ny = 1;
54068       const char *const filename = TIFFFileName(tif);
54069       const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
54070       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
54071       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
54072       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
54073       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
54074       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
54075       if (bits_per_value) *bits_per_value = (unsigned int)bitspersample;
54076       if (voxel_size) {
54077         const char *s_description = 0;
54078         float vx = 0, vy = 0, vz = 0;
54079         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
54080           const char *s_desc = std::strstr(s_description,"VX=");
54081           if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format
54082             voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
54083           }
54084           s_desc = std::strstr(s_description,"spacing=");
54085           if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format
54086             voxel_size[2] = vz;
54087           }
54088         }
54089         TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
54090         TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
54091         voxel_size[0] = 1.f/voxel_size[0];
54092         voxel_size[1] = 1.f/voxel_size[1];
54093       }
54094       if (description) {
54095         const char *s_description = 0;
54096         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
54097           CImg<charT>::string(s_description).move_to(*description);
54098       }
54099       const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
54100       assign(nx,ny,1,spectrum);
54101 
54102       if ((photo>=3 && sampleformat==1 &&
54103            (bitspersample==4 || bitspersample==8) &&
54104            (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
54105           (bitspersample==1 && samplesperpixel==1)) {
54106         // Special case for unsigned color images.
54107         uint32_t *const raster = (uint32_t*)_TIFFmalloc(nx*ny*sizeof(uint32_t));
54108         if (!raster) {
54109           _TIFFfree(raster); TIFFClose(tif);
54110           throw CImgException(_cimg_instance
54111                               "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
54112                               cimg_instance,
54113                               cimg::strbuffersize(nx*ny*sizeof(uint32_t)),filename);
54114         }
54115         TIFFReadRGBAImage(tif,nx,ny,raster,0);
54116         switch (spectrum) {
54117         case 1 :
54118           cimg_forXY(*this,x,y)
54119             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
54120           break;
54121         case 3 :
54122           cimg_forXY(*this,x,y) {
54123             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
54124             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
54125             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
54126           }
54127           break;
54128         case 4 :
54129           cimg_forXY(*this,x,y) {
54130             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
54131             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
54132             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
54133             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
54134           }
54135           break;
54136         }
54137         _TIFFfree(raster);
54138       } else { // Other cases
54139         uint16_t config;
54140         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
54141         if (TIFFIsTiled(tif)) {
54142           uint32_t tw = 1, th = 1;
54143           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
54144           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
54145           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
54146             case 8 :
54147               if (sampleformat==SAMPLEFORMAT_UINT)
54148                 _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
54149               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
54150               break;
54151             case 16 :
54152               if (sampleformat==SAMPLEFORMAT_UINT)
54153                 _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
54154               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
54155               break;
54156             case 32 :
54157               if (sampleformat==SAMPLEFORMAT_UINT)
54158                 _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
54159               else if (sampleformat==SAMPLEFORMAT_INT)
54160                 _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
54161               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
54162               break;
54163             case 64 :
54164               if (sampleformat==SAMPLEFORMAT_UINT)
54165                 _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
54166               else if (sampleformat==SAMPLEFORMAT_INT)
54167                 _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
54168               else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
54169               break;
54170             } else switch (bitspersample) {
54171             case 8 :
54172               if (sampleformat==SAMPLEFORMAT_UINT)
54173                 _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
54174               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
54175               break;
54176             case 16 :
54177               if (sampleformat==SAMPLEFORMAT_UINT)
54178                 _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
54179               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
54180               break;
54181             case 32 :
54182               if (sampleformat==SAMPLEFORMAT_UINT)
54183                 _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
54184               else if (sampleformat==SAMPLEFORMAT_INT)
54185                 _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
54186               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
54187               break;
54188             case 64 :
54189               if (sampleformat==SAMPLEFORMAT_UINT)
54190                 _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
54191               else if (sampleformat==SAMPLEFORMAT_INT)
54192                 _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
54193               else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
54194               break;
54195             }
54196         } else {
54197           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
54198             case 8 :
54199               if (sampleformat==SAMPLEFORMAT_UINT)
54200                 _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
54201               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
54202               break;
54203             case 16 :
54204               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
54205               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
54206               break;
54207             case 32 :
54208               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
54209               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
54210               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
54211               break;
54212             case 64 :
54213               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
54214               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
54215               else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
54216               break;
54217             } else switch (bitspersample) {
54218             case 8 :
54219               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
54220               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
54221               break;
54222             case 16 :
54223               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
54224               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
54225               break;
54226             case 32 :
54227               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
54228               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
54229               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
54230               break;
54231             case 64 :
54232               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
54233               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
54234               else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
54235               break;
54236             }
54237         }
54238       }
54239       return *this;
54240     }
54241 #endif
54242 
54243     //! Load image from a MINC2 file.
54244     /**
54245         \param filename Filename, as a C-string.
54246     **/
54247     // (Original code by Haz-Edine Assemlal).
54248     CImg<T>& load_minc2(const char *const filename) {
54249       if (!filename)
54250         throw CImgArgumentException(_cimg_instance
54251                                     "load_minc2(): Specified filename is (null).",
54252                                     cimg_instance);
54253 #ifndef cimg_use_minc2
54254       return load_other(filename);
54255 #else
54256       minc::minc_1_reader rdr;
54257       rdr.open(filename);
54258       assign(rdr.ndim(1)?rdr.ndim(1):1,
54259              rdr.ndim(2)?rdr.ndim(2):1,
54260              rdr.ndim(3)?rdr.ndim(3):1,
54261              rdr.ndim(4)?rdr.ndim(4):1);
54262       if (pixel_type()==cimg::type<unsigned char>::string())
54263         rdr.setup_read_byte();
54264       else if (pixel_type()==cimg::type<int>::string())
54265         rdr.setup_read_int();
54266       else if (pixel_type()==cimg::type<double>::string())
54267         rdr.setup_read_double();
54268       else
54269         rdr.setup_read_float();
54270       minc::load_standard_volume(rdr,this->_data);
54271       return *this;
54272 #endif
54273     }
54274 
54275     //! Load image from a MINC2 file \newinstance.
54276     static CImg<T> get_load_minc2(const char *const filename) {
54277       return CImg<T>().load_analyze(filename);
54278     }
54279 
54280     //! Load image from an ANALYZE7.5/NIFTI file.
54281     /**
54282        \param filename Filename, as a C-string.
54283        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
54284     **/
54285     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
54286       return _load_analyze(0,filename,voxel_size);
54287     }
54288 
54289     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
54290     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
54291       return CImg<T>().load_analyze(filename,voxel_size);
54292     }
54293 
54294     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
54295     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
54296       return _load_analyze(file,0,voxel_size);
54297     }
54298 
54299     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
54300     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
54301       return CImg<T>().load_analyze(file,voxel_size);
54302     }
54303 
54304     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
54305       if (!file && !filename)
54306         throw CImgArgumentException(_cimg_instance
54307                                     "load_analyze(): Specified filename is (null).",
54308                                     cimg_instance);
54309 
54310       std::FILE *nfile_header = 0, *nfile = 0;
54311       if (!file) {
54312         CImg<charT> body(1024);
54313         const char *const ext = cimg::split_filename(filename,body);
54314         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file
54315           nfile_header = cimg::fopen(filename,"rb");
54316           cimg_sprintf(body._data + std::strlen(body),".img");
54317           nfile = cimg::fopen(body,"rb");
54318         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file
54319           nfile = cimg::fopen(filename,"rb");
54320           cimg_sprintf(body._data + std::strlen(body),".hdr");
54321           nfile_header = cimg::fopen(body,"rb");
54322         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file
54323       } else nfile_header = nfile = file; // File is a Niftii file
54324       if (!nfile || !nfile_header)
54325         throw CImgIOException(_cimg_instance
54326                               "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
54327                               cimg_instance,
54328                               filename?filename:"(FILE*)");
54329 
54330       // Read header.
54331       bool endian = false;
54332       unsigned int header_size;
54333       cimg::fread(&header_size,1,nfile_header);
54334       if (!header_size)
54335         throw CImgIOException(_cimg_instance
54336                               "load_analyze(): Invalid zero-size header in file '%s'.",
54337                               cimg_instance,
54338                               filename?filename:"(FILE*)");
54339       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
54340 
54341       unsigned char *const header = new unsigned char[header_size];
54342       cimg::fread(header + 4,header_size - 4,nfile_header);
54343       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
54344       if (endian) {
54345         cimg::invert_endianness((short*)(header + 40),5);
54346         cimg::invert_endianness((short*)(header + 70),1);
54347         cimg::invert_endianness((short*)(header + 72),1);
54348         cimg::invert_endianness((float*)(header + 76),4);
54349         cimg::invert_endianness((float*)(header + 108),1);
54350         cimg::invert_endianness((float*)(header + 112),1);
54351       }
54352 
54353       if (nfile_header==nfile) {
54354         const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
54355         std::fseek(nfile,vox_offset,SEEK_SET);
54356       }
54357 
54358       unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
54359       if (!dim[0])
54360         cimg::warn(_cimg_instance
54361                    "load_analyze(): File '%s' defines an image with zero dimensions.",
54362                    cimg_instance,
54363                    filename?filename:"(FILE*)");
54364 
54365       if (dim[0]>4)
54366         cimg::warn(_cimg_instance
54367                    "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
54368                    cimg_instance,
54369                    filename?filename:"(FILE*)",dim[0]);
54370 
54371       if (dim[0]>=1) dimx = dim[1];
54372       if (dim[0]>=2) dimy = dim[2];
54373       if (dim[0]>=3) dimz = dim[3];
54374       if (dim[0]>=4) dimv = dim[4];
54375       float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
54376       const unsigned short datatype = *(unsigned short*)(header + 70);
54377       if (voxel_size) {
54378         const float *vsize = (float*)(header + 76);
54379         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
54380       }
54381       delete[] header;
54382 
54383       // Read pixel data.
54384       assign(dimx,dimy,dimz,dimv);
54385       const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
54386       switch (datatype) {
54387       case 2 : {
54388         unsigned char *const buffer = new unsigned char[pdim];
54389         cimg::fread(buffer,pdim,nfile);
54390         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
54391         delete[] buffer;
54392       } break;
54393       case 4 : {
54394         short *const buffer = new short[pdim];
54395         cimg::fread(buffer,pdim,nfile);
54396         if (endian) cimg::invert_endianness(buffer,pdim);
54397         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
54398         delete[] buffer;
54399       } break;
54400       case 8 : {
54401         int *const buffer = new int[pdim];
54402         cimg::fread(buffer,pdim,nfile);
54403         if (endian) cimg::invert_endianness(buffer,pdim);
54404         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
54405         delete[] buffer;
54406       } break;
54407       case 16 : {
54408         float *const buffer = new float[pdim];
54409         cimg::fread(buffer,pdim,nfile);
54410         if (endian) cimg::invert_endianness(buffer,pdim);
54411         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
54412         delete[] buffer;
54413       } break;
54414       case 64 : {
54415         double *const buffer = new double[pdim];
54416         cimg::fread(buffer,pdim,nfile);
54417         if (endian) cimg::invert_endianness(buffer,pdim);
54418         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
54419         delete[] buffer;
54420       } break;
54421       default :
54422         if (!file) cimg::fclose(nfile);
54423         throw CImgIOException(_cimg_instance
54424                               "load_analyze(): Unable to load datatype %d in file '%s'",
54425                               cimg_instance,
54426                               datatype,filename?filename:"(FILE*)");
54427       }
54428       if (!file) cimg::fclose(nfile);
54429       return *this;
54430     }
54431 
54432     //! Load image from a .cimg[z] file.
54433     /**
54434       \param filename Filename, as a C-string.
54435       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54436       \param align Appending alignment.
54437     **/
54438     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
54439       CImgList<T> list;
54440       list.load_cimg(filename);
54441       if (list._width==1) return list[0].move_to(*this);
54442       return assign(list.get_append(axis,align));
54443     }
54444 
54445     //! Load image from a .cimg[z] file \newinstance
54446     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
54447       return CImg<T>().load_cimg(filename,axis,align);
54448     }
54449 
54450     //! Load image from a .cimg[z] file \overloading.
54451     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
54452       CImgList<T> list;
54453       list.load_cimg(file);
54454       if (list._width==1) return list[0].move_to(*this);
54455       return assign(list.get_append(axis,align));
54456     }
54457 
54458     //! Load image from a .cimg[z] file \newinstance
54459     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
54460       return CImg<T>().load_cimg(file,axis,align);
54461     }
54462 
54463     //! Load sub-images of a .cimg file.
54464     /**
54465       \param filename Filename, as a C-string.
54466       \param n0 Starting frame.
54467       \param n1 Ending frame (~0U for max).
54468       \param x0 X-coordinate of the starting sub-image vertex.
54469       \param y0 Y-coordinate of the starting sub-image vertex.
54470       \param z0 Z-coordinate of the starting sub-image vertex.
54471       \param c0 C-coordinate of the starting sub-image vertex.
54472       \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
54473       \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
54474       \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
54475       \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
54476       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54477       \param align Appending alignment.
54478     **/
54479     CImg<T>& load_cimg(const char *const filename,
54480                        const unsigned int n0, const unsigned int n1,
54481                        const unsigned int x0, const unsigned int y0,
54482                        const unsigned int z0, const unsigned int c0,
54483                        const unsigned int x1, const unsigned int y1,
54484                        const unsigned int z1, const unsigned int c1,
54485                        const char axis='z', const float align=0) {
54486       CImgList<T> list;
54487       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
54488       if (list._width==1) return list[0].move_to(*this);
54489       return assign(list.get_append(axis,align));
54490     }
54491 
54492     //! Load sub-images of a .cimg file \newinstance.
54493     static CImg<T> get_load_cimg(const char *const filename,
54494                                  const unsigned int n0, const unsigned int n1,
54495                                  const unsigned int x0, const unsigned int y0,
54496                                  const unsigned int z0, const unsigned int c0,
54497                                  const unsigned int x1, const unsigned int y1,
54498                                  const unsigned int z1, const unsigned int c1,
54499                                  const char axis='z', const float align=0) {
54500       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
54501     }
54502 
54503     //! Load sub-images of a .cimg file \overloading.
54504     CImg<T>& load_cimg(std::FILE *const file,
54505                        const unsigned int n0, const unsigned int n1,
54506                        const unsigned int x0, const unsigned int y0,
54507                        const unsigned int z0, const unsigned int c0,
54508                        const unsigned int x1, const unsigned int y1,
54509                        const unsigned int z1, const unsigned int c1,
54510                        const char axis='z', const float align=0) {
54511       CImgList<T> list;
54512       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
54513       if (list._width==1) return list[0].move_to(*this);
54514       return assign(list.get_append(axis,align));
54515     }
54516 
54517     //! Load sub-images of a .cimg file \newinstance.
54518     static CImg<T> get_load_cimg(std::FILE *const file,
54519                                  const unsigned int n0, const unsigned int n1,
54520                                  const unsigned int x0, const unsigned int y0,
54521                                  const unsigned int z0, const unsigned int c0,
54522                                  const unsigned int x1, const unsigned int y1,
54523                                  const unsigned int z1, const unsigned int c1,
54524                                  const char axis='z', const float align=0) {
54525       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
54526     }
54527 
54528     //! Load image from an INRIMAGE-4 file.
54529     /**
54530        \param filename Filename, as a C-string.
54531        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
54532     **/
54533     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
54534       return _load_inr(0,filename,voxel_size);
54535     }
54536 
54537     //! Load image from an INRIMAGE-4 file \newinstance.
54538     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
54539       return CImg<T>().load_inr(filename,voxel_size);
54540     }
54541 
54542     //! Load image from an INRIMAGE-4 file \overloading.
54543     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
54544       return _load_inr(file,0,voxel_size);
54545     }
54546 
54547     //! Load image from an INRIMAGE-4 file \newinstance.
54548     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
54549       return CImg<T>().load_inr(file,voxel_size);
54550     }
54551 
54552     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
54553       CImg<charT> item(1024), tmp1(64), tmp2(64);
54554       *item = *tmp1 = *tmp2 = 0;
54555       out[0] = std::fscanf(file,"%63s",item._data);
54556       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
54557       if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
54558         throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
54559                               pixel_type());
54560 
54561       while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
54562         cimg_sscanf(item," XDIM%*[^0-9]%d",out);
54563         cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
54564         cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
54565         cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
54566         cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
54567         if (voxel_size) {
54568           cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
54569           cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
54570           cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
54571         }
54572         if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
54573         switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
54574         case 0 : break;
54575         case 2 :
54576           out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
54577           std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
54578         case 1 :
54579           if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
54580           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
54581           if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
54582           if (out[4]>=0) break; // fallthrough
54583         default :
54584           throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
54585                                 pixel_type(),
54586                                 tmp2._data);
54587         }
54588       }
54589       if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
54590         throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
54591                               pixel_type(),
54592                               out[0],out[1],out[2],out[3]);
54593       if (out[4]<0 || out[5]<0)
54594         throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
54595                               pixel_type());
54596       if (out[6]<0)
54597         throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
54598                               pixel_type());
54599       if (out[7]<0)
54600         throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
54601                               pixel_type());
54602     }
54603 
54604     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
54605 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
54606      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
54607         Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
54608         cimg_forYZ(*this,y,z) { \
54609             cimg::fread(val,fopt[0]*fopt[3],nfile); \
54610             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
54611             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
54612           } \
54613         delete[] val; \
54614         loaded = true; \
54615       }
54616 
54617       if (!file && !filename)
54618         throw CImgArgumentException(_cimg_instance
54619                                     "load_inr(): Specified filename is (null).",
54620                                     cimg_instance);
54621 
54622       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
54623       int fopt[8], endian = cimg::endianness()?1:0;
54624       bool loaded = false;
54625       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
54626       _load_inr_header(nfile,fopt,voxel_size);
54627       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
54628       _cimg_load_inr_case(0,0,8,unsigned char);
54629       _cimg_load_inr_case(0,1,8,char);
54630       _cimg_load_inr_case(0,0,16,unsigned short);
54631       _cimg_load_inr_case(0,1,16,short);
54632       _cimg_load_inr_case(0,0,32,unsigned int);
54633       _cimg_load_inr_case(0,1,32,int);
54634       _cimg_load_inr_case(1,0,32,float);
54635       _cimg_load_inr_case(1,1,32,float);
54636       _cimg_load_inr_case(1,0,64,double);
54637       _cimg_load_inr_case(1,1,64,double);
54638       if (!loaded) {
54639         if (!file) cimg::fclose(nfile);
54640         throw CImgIOException(_cimg_instance
54641                               "load_inr(): Unknown pixel type defined in file '%s'.",
54642                               cimg_instance,
54643                               filename?filename:"(FILE*)");
54644       }
54645       if (!file) cimg::fclose(nfile);
54646       return *this;
54647     }
54648 
54649     //! Load image from a EXR file.
54650     /**
54651       \param filename Filename, as a C-string.
54652     **/
54653     CImg<T>& load_exr(const char *const filename) {
54654       if (!filename)
54655         throw CImgArgumentException(_cimg_instance
54656                                     "load_exr(): Specified filename is (null).",
54657                                     cimg_instance);
54658 #if defined(cimg_use_openexr)
54659       Imf::RgbaInputFile file(filename);
54660       Imath::Box2i dw = file.dataWindow();
54661       const int
54662         inwidth = dw.max.x - dw.min.x + 1,
54663         inheight = dw.max.y - dw.min.y + 1;
54664       Imf::Array2D<Imf::Rgba> pixels;
54665       pixels.resizeErase(inheight,inwidth);
54666       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
54667       file.readPixels(dw.min.y, dw.max.y);
54668       assign(inwidth,inheight,1,4);
54669       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);
54670       cimg_forXY(*this,x,y) {
54671         *(ptr_r++) = (T)pixels[y][x].r;
54672         *(ptr_g++) = (T)pixels[y][x].g;
54673         *(ptr_b++) = (T)pixels[y][x].b;
54674         *(ptr_a++) = (T)pixels[y][x].a;
54675       }
54676       return *this;
54677 #elif defined(cimg_use_tinyexr)
54678       float *res;
54679       const char *err = 0;
54680       int width = 0, height = 0;
54681       const int ret = LoadEXR(&res,&width,&height,filename,&err);
54682       if (ret) throw CImgIOException(_cimg_instance
54683                                      "load_exr(): Unable to load EXR file '%s'.",
54684                                      cimg_instance,filename);
54685       CImg<floatT>(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
54686       std::free(res);
54687       return *this;
54688 #else
54689       return load_other(filename);
54690 #endif
54691     }
54692 
54693     //! Load image from a EXR file \newinstance.
54694     static CImg<T> get_load_exr(const char *const filename) {
54695       return CImg<T>().load_exr(filename);
54696     }
54697 
54698     //! Load image from a PANDORE-5 file.
54699     /**
54700       \param filename Filename, as a C-string.
54701     **/
54702     CImg<T>& load_pandore(const char *const filename) {
54703       return _load_pandore(0,filename);
54704     }
54705 
54706     //! Load image from a PANDORE-5 file \newinstance.
54707     static CImg<T> get_load_pandore(const char *const filename) {
54708       return CImg<T>().load_pandore(filename);
54709     }
54710 
54711     //! Load image from a PANDORE-5 file \overloading.
54712     CImg<T>& load_pandore(std::FILE *const file) {
54713       return _load_pandore(file,0);
54714     }
54715 
54716     //! Load image from a PANDORE-5 file \newinstance.
54717     static CImg<T> get_load_pandore(std::FILE *const file) {
54718       return CImg<T>().load_pandore(file);
54719     }
54720 
54721     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
54722 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
54723         cimg::fread(dims,nbdim,nfile); \
54724         if (endian) cimg::invert_endianness(dims,nbdim); \
54725         assign(nwidth,nheight,ndepth,ndim); \
54726         const size_t siz = size(); \
54727         stype *buffer = new stype[siz]; \
54728         cimg::fread(buffer,siz,nfile); \
54729         if (endian) cimg::invert_endianness(buffer,siz); \
54730         T *ptrd = _data; \
54731         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
54732         buffer-=siz; \
54733         delete[] buffer
54734 
54735 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
54736         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
54737         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
54738         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
54739         else throw CImgIOException(_cimg_instance \
54740                                    "load_pandore(): Unknown pixel datatype in file '%s'.", \
54741                                    cimg_instance, \
54742                                    filename?filename:"(FILE*)"); }
54743       if (!file && !filename)
54744         throw CImgArgumentException(_cimg_instance
54745                                     "load_pandore(): Specified filename is (null).",
54746                                     cimg_instance);
54747 
54748       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
54749       CImg<charT> header(32);
54750       cimg::fread(header._data,12,nfile);
54751       if (cimg::strncasecmp("PANDORE",header,7)) {
54752         if (!file) cimg::fclose(nfile);
54753         throw CImgIOException(_cimg_instance
54754                               "load_pandore(): PANDORE header not found in file '%s'.",
54755                               cimg_instance,
54756                               filename?filename:"(FILE*)");
54757       }
54758       unsigned int imageid, dims[8] = { 0 };
54759       int ptbuf[4] = { 0 };
54760       cimg::fread(&imageid,1,nfile);
54761       const bool endian = imageid>255;
54762       if (endian) cimg::invert_endianness(imageid);
54763       cimg::fread(header._data,20,nfile);
54764 
54765       switch (imageid) {
54766       case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
54767       case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
54768       case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
54769       case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
54770       case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
54771       case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
54772       case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
54773       case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
54774       case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
54775       case 11 : { // Region 1D
54776         cimg::fread(dims,3,nfile);
54777         if (endian) cimg::invert_endianness(dims,3);
54778         assign(dims[1],1,1,1);
54779         const unsigned siz = size();
54780         if (dims[2]<256) {
54781           unsigned char *buffer = new unsigned char[siz];
54782           cimg::fread(buffer,siz,nfile);
54783           T *ptrd = _data;
54784           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54785           buffer-=siz;
54786           delete[] buffer;
54787         } else {
54788           if (dims[2]<65536) {
54789             unsigned short *buffer = new unsigned short[siz];
54790             cimg::fread(buffer,siz,nfile);
54791             if (endian) cimg::invert_endianness(buffer,siz);
54792             T *ptrd = _data;
54793             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54794             buffer-=siz;
54795             delete[] buffer;
54796           } else {
54797             unsigned int *buffer = new unsigned int[siz];
54798             cimg::fread(buffer,siz,nfile);
54799             if (endian) cimg::invert_endianness(buffer,siz);
54800             T *ptrd = _data;
54801             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54802             buffer-=siz;
54803             delete[] buffer;
54804           }
54805         }
54806       }
54807         break;
54808       case 12 : { // Region 2D
54809         cimg::fread(dims,4,nfile);
54810         if (endian) cimg::invert_endianness(dims,4);
54811         assign(dims[2],dims[1],1,1);
54812         const size_t siz = size();
54813         if (dims[3]<256) {
54814           unsigned char *buffer = new unsigned char[siz];
54815           cimg::fread(buffer,siz,nfile);
54816           T *ptrd = _data;
54817           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54818           buffer-=siz;
54819           delete[] buffer;
54820         } else {
54821           if (dims[3]<65536) {
54822             unsigned short *buffer = new unsigned short[siz];
54823             cimg::fread(buffer,siz,nfile);
54824             if (endian) cimg::invert_endianness(buffer,siz);
54825             T *ptrd = _data;
54826             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54827             buffer-=siz;
54828             delete[] buffer;
54829           } else {
54830             unsigned int *buffer = new unsigned int[siz];
54831             cimg::fread(buffer,siz,nfile);
54832             if (endian) cimg::invert_endianness(buffer,siz);
54833             T *ptrd = _data;
54834             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54835             buffer-=siz;
54836             delete[] buffer;
54837           }
54838         }
54839       }
54840         break;
54841       case 13 : { // Region 3D
54842         cimg::fread(dims,5,nfile);
54843         if (endian) cimg::invert_endianness(dims,5);
54844         assign(dims[3],dims[2],dims[1],1);
54845         const size_t siz = size();
54846         if (dims[4]<256) {
54847           unsigned char *buffer = new unsigned char[siz];
54848           cimg::fread(buffer,siz,nfile);
54849           T *ptrd = _data;
54850           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54851           buffer-=siz;
54852           delete[] buffer;
54853         } else {
54854           if (dims[4]<65536) {
54855             unsigned short *buffer = new unsigned short[siz];
54856             cimg::fread(buffer,siz,nfile);
54857             if (endian) cimg::invert_endianness(buffer,siz);
54858             T *ptrd = _data;
54859             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54860             buffer-=siz;
54861             delete[] buffer;
54862           } else {
54863             unsigned int *buffer = new unsigned int[siz];
54864             cimg::fread(buffer,siz,nfile);
54865             if (endian) cimg::invert_endianness(buffer,siz);
54866             T *ptrd = _data;
54867             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
54868             buffer-=siz;
54869             delete[] buffer;
54870           }
54871         }
54872       }
54873         break;
54874       case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
54875       case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
54876       case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
54877       case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
54878       case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
54879       case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
54880       case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
54881       case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
54882       case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
54883       case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
54884       case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
54885       case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
54886       case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
54887       case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
54888       case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
54889         break;
54890       case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
54891       case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
54892         break;
54893       case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
54894       case 34 : { // Points 1D
54895         cimg::fread(ptbuf,1,nfile);
54896         if (endian) cimg::invert_endianness(ptbuf,1);
54897         assign(1); (*this)(0) = (T)ptbuf[0];
54898       } break;
54899       case 35 : { // Points 2D
54900         cimg::fread(ptbuf,2,nfile);
54901         if (endian) cimg::invert_endianness(ptbuf,2);
54902         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
54903       } break;
54904       case 36 : { // Points 3D
54905         cimg::fread(ptbuf,3,nfile);
54906         if (endian) cimg::invert_endianness(ptbuf,3);
54907         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
54908       } break;
54909       default :
54910         if (!file) cimg::fclose(nfile);
54911         throw CImgIOException(_cimg_instance
54912                               "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
54913                               cimg_instance,
54914                               imageid,filename?filename:"(FILE*)");
54915       }
54916       if (!file) cimg::fclose(nfile);
54917       return *this;
54918     }
54919 
54920     //! Load image from a PAR-REC (Philips) file.
54921     /**
54922       \param filename Filename, as a C-string.
54923       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54924       \param align Appending alignment.
54925     **/
54926     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
54927       CImgList<T> list;
54928       list.load_parrec(filename);
54929       if (list._width==1) return list[0].move_to(*this);
54930       return assign(list.get_append(axis,align));
54931     }
54932 
54933     //! Load image from a PAR-REC (Philips) file \newinstance.
54934     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
54935       return CImg<T>().load_parrec(filename,axis,align);
54936     }
54937 
54938     //! Load image from a raw binary file.
54939     /**
54940       \param filename Filename, as a C-string.
54941       \param size_x Width of the image buffer.
54942       \param size_y Height of the image buffer.
54943       \param size_z Depth of the image buffer.
54944       \param size_c Spectrum of the image buffer.
54945       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
54946       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
54947       \param offset Starting offset of the read in the specified file.
54948     **/
54949     CImg<T>& load_raw(const char *const filename,
54950                       const unsigned int size_x=0, const unsigned int size_y=1,
54951                       const unsigned int size_z=1, const unsigned int size_c=1,
54952                       const bool is_multiplexed=false, const bool invert_endianness=false,
54953                       const ulongT offset=0) {
54954       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54955     }
54956 
54957     //! Load image from a raw binary file \newinstance.
54958     static CImg<T> get_load_raw(const char *const filename,
54959                                 const unsigned int size_x=0, const unsigned int size_y=1,
54960                                 const unsigned int size_z=1, const unsigned int size_c=1,
54961                                 const bool is_multiplexed=false, const bool invert_endianness=false,
54962                                 const ulongT offset=0) {
54963       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54964     }
54965 
54966     //! Load image from a raw binary file \overloading.
54967     CImg<T>& load_raw(std::FILE *const file,
54968                       const unsigned int size_x=0, const unsigned int size_y=1,
54969                       const unsigned int size_z=1, const unsigned int size_c=1,
54970                       const bool is_multiplexed=false, const bool invert_endianness=false,
54971                       const ulongT offset=0) {
54972       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54973     }
54974 
54975     //! Load image from a raw binary file \newinstance.
54976     static CImg<T> get_load_raw(std::FILE *const file,
54977                                 const unsigned int size_x=0, const unsigned int size_y=1,
54978                                 const unsigned int size_z=1, const unsigned int size_c=1,
54979                                 const bool is_multiplexed=false, const bool invert_endianness=false,
54980                                 const ulongT offset=0) {
54981       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
54982     }
54983 
54984     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
54985                        const unsigned int size_x, const unsigned int size_y,
54986                        const unsigned int size_z, const unsigned int size_c,
54987                        const bool is_multiplexed, const bool invert_endianness,
54988                        const ulongT offset) {
54989       if (!file && !filename)
54990         throw CImgArgumentException(_cimg_instance
54991                                     "load_raw(): Specified filename is (null).",
54992                                     cimg_instance);
54993       if (cimg::is_directory(filename))
54994         throw CImgArgumentException(_cimg_instance
54995                                     "load_raw(): Specified filename '%s' is a directory.",
54996                                     cimg_instance,filename);
54997       const bool is_bool = pixel_type()==cimg::type<bool>::string();
54998       ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
54999       unsigned int
55000         _size_x = size_x,
55001         _size_y = size_y,
55002         _size_z = size_z,
55003         _size_c = size_c;
55004       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
55005       if (!siz) {  // Retrieve file size
55006         const longT fpos = cimg::ftell(nfile);
55007         if (fpos<0) throw CImgArgumentException(_cimg_instance
55008                                                 "load_raw(): Cannot determine size of input file '%s'.",
55009                                                 cimg_instance,filename?filename:"(FILE*)");
55010         cimg::fseek(nfile,0,SEEK_END);
55011         siz = (ulongT)cimg::ftell(nfile);
55012         if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; }
55013         else _size_y = (unsigned int)(siz*8);
55014         _size_x = _size_z = _size_c = 1;
55015         cimg::fseek(nfile,fpos,SEEK_SET);
55016       }
55017       cimg::fseek(nfile,(longT)offset,SEEK_SET);
55018       assign(_size_x,_size_y,_size_z,_size_c,0);
55019 
55020       if (is_bool) { // Boolean data (bitwise)
55021         unsigned char *const buf = new unsigned char[siz];
55022         cimg::fread(buf,siz,nfile);
55023         _uchar2bool(buf,siz,is_multiplexed);
55024         delete[] buf;
55025       } else { // Non-boolean data
55026         if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed
55027           cimg::fread(_data,siz,nfile);
55028           if (invert_endianness) cimg::invert_endianness(_data,siz);
55029         } else if (siz) { // Multiplexed
55030           CImg<T> buf(1,1,1,_size_c);
55031           cimg_forXYZ(*this,x,y,z) {
55032             cimg::fread(buf._data,_size_c,nfile);
55033             if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
55034             set_vector_at(buf,x,y,z);
55035           }
55036         }
55037       }
55038       if (!file) cimg::fclose(nfile);
55039       return *this;
55040     }
55041 
55042     //! Load image sequence from a YUV file.
55043     /**
55044       \param filename Filename, as a C-string.
55045       \param size_x Width of the frames.
55046       \param size_y Height of the frames.
55047       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
55048       \param first_frame Index of the first frame to read.
55049       \param last_frame Index of the last frame to read.
55050       \param step_frame Step value for frame reading.
55051       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
55052       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
55053     **/
55054     CImg<T>& load_yuv(const char *const filename,
55055                       const unsigned int size_x, const unsigned int size_y=1,
55056                       const unsigned int chroma_subsampling=444,
55057                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55058                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
55059       return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
55060                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
55061     }
55062 
55063     //! Load image sequence from a YUV file \newinstance.
55064     static CImg<T> get_load_yuv(const char *const filename,
55065                                 const unsigned int size_x, const unsigned int size_y=1,
55066                                 const unsigned int chroma_subsampling=444,
55067                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55068                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
55069       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
55070                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
55071     }
55072 
55073     //! Load image sequence from a YUV file \overloading.
55074     CImg<T>& load_yuv(std::FILE *const file,
55075                       const unsigned int size_x, const unsigned int size_y=1,
55076                       const unsigned int chroma_subsampling=444,
55077                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55078                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
55079       return get_load_yuv(file,size_x,size_y,chroma_subsampling,
55080                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
55081     }
55082 
55083     //! Load image sequence from a YUV file \newinstance.
55084     static CImg<T> get_load_yuv(std::FILE *const file,
55085                                 const unsigned int size_x, const unsigned int size_y=1,
55086                                 const unsigned int chroma_subsampling=444,
55087                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55088                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
55089       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
55090                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
55091     }
55092 
55093     //! Load 3D object from a .OFF file.
55094     /**
55095         \param[out] primitives Primitives data of the 3D object.
55096         \param[out] colors Colors data of the 3D object.
55097         \param filename Filename, as a C-string.
55098     **/
55099     template<typename tf, typename tc>
55100     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
55101       return _load_off(primitives,colors,0,filename);
55102     }
55103 
55104     //! Load 3D object from a .OFF file \newinstance.
55105     template<typename tf, typename tc>
55106     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
55107       return CImg<T>().load_off(primitives,colors,filename);
55108     }
55109 
55110     //! Load 3D object from a .OFF file \overloading.
55111     template<typename tf, typename tc>
55112     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
55113       return _load_off(primitives,colors,file,0);
55114     }
55115 
55116     //! Load 3D object from a .OFF file \newinstance.
55117     template<typename tf, typename tc>
55118     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
55119       return CImg<T>().load_off(primitives,colors,file);
55120     }
55121 
55122     template<typename tf, typename tc>
55123     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
55124                        std::FILE *const file, const char *const filename) {
55125       if (!file && !filename)
55126         throw CImgArgumentException(_cimg_instance
55127                                     "load_off(): Specified filename is (null).",
55128                                     cimg_instance);
55129 
55130       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
55131       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
55132       CImg<charT> line(256); *line = 0;
55133       int err;
55134 
55135       // Skip comments, and read magic string OFF
55136       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
55137       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
55138         if (!file) cimg::fclose(nfile);
55139         throw CImgIOException(_cimg_instance
55140                               "load_off(): OFF header not found in file '%s'.",
55141                               cimg_instance,
55142                               filename?filename:"(FILE*)");
55143       }
55144       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
55145       if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
55146         if (!file) cimg::fclose(nfile);
55147         throw CImgIOException(_cimg_instance
55148                               "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
55149                               cimg_instance,
55150                               filename?filename:"(FILE*)");
55151       }
55152 
55153       // Read points data
55154       assign(nb_points,3);
55155       float X = 0, Y = 0, Z = 0;
55156       cimg_forX(*this,l) {
55157         do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
55158         if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
55159           if (!file) cimg::fclose(nfile);
55160           throw CImgIOException(_cimg_instance
55161                                 "load_off(): Failed to read vertex %u/%u in file '%s'.",
55162                                 cimg_instance,
55163                                 l + 1,nb_points,filename?filename:"(FILE*)");
55164         }
55165         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
55166       }
55167 
55168       // Read primitive data
55169       primitives.assign();
55170       colors.assign();
55171       bool stop_flag = false;
55172       while (!stop_flag) {
55173         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
55174         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
55175         *line = 0;
55176         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
55177         else {
55178           ++nb_read;
55179           switch (prim) {
55180           case 1 : {
55181             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
55182               cimg::warn(_cimg_instance
55183                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55184                          cimg_instance,
55185                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55186 
55187               err = std::fscanf(nfile,"%*[^\n] ");
55188             } else {
55189               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55190               CImg<tf>::vector(i0).move_to(primitives);
55191               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
55192             }
55193           } break;
55194           case 2 : {
55195             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
55196               cimg::warn(_cimg_instance
55197                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55198                          cimg_instance,
55199                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55200 
55201               err = std::fscanf(nfile,"%*[^\n] ");
55202             } else {
55203               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55204               CImg<tf>::vector(i0,i1).move_to(primitives);
55205               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
55206             }
55207           } break;
55208           case 3 : {
55209             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
55210               cimg::warn(_cimg_instance
55211                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55212                          cimg_instance,
55213                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55214 
55215               err = std::fscanf(nfile,"%*[^\n] ");
55216             } else {
55217               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55218               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
55219               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
55220             }
55221           } break;
55222           case 4 : {
55223             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
55224               cimg::warn(_cimg_instance
55225                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55226                          cimg_instance,
55227                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55228 
55229               err = std::fscanf(nfile,"%*[^\n] ");
55230             } else {
55231               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55232               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
55233               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
55234             }
55235           } break;
55236           case 5 : {
55237             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
55238               cimg::warn(_cimg_instance
55239                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55240                          cimg_instance,
55241                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55242 
55243               err = std::fscanf(nfile,"%*[^\n] ");
55244             } else {
55245               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55246               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
55247               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
55248               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
55249               ++nb_primitives;
55250             }
55251           } break;
55252           case 6 : {
55253             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
55254               cimg::warn(_cimg_instance
55255                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55256                          cimg_instance,
55257                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55258 
55259               err = std::fscanf(nfile,"%*[^\n] ");
55260             } else {
55261               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55262               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
55263               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
55264               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
55265               ++nb_primitives;
55266             }
55267           } break;
55268           case 7 : {
55269             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
55270               cimg::warn(_cimg_instance
55271                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55272                          cimg_instance,
55273                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55274 
55275               err = std::fscanf(nfile,"%*[^\n] ");
55276             } else {
55277               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55278               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
55279               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
55280               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
55281               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
55282               ++(++nb_primitives);
55283             }
55284           } break;
55285           case 8 : {
55286             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) {
55287               cimg::warn(_cimg_instance
55288                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
55289                          cimg_instance,
55290                          nb_read,nb_primitives,filename?filename:"(FILE*)");
55291 
55292               err = std::fscanf(nfile,"%*[^\n] ");
55293             } else {
55294               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
55295               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
55296               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
55297               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
55298               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
55299               ++(++nb_primitives);
55300             }
55301           } break;
55302           default :
55303             cimg::warn(_cimg_instance
55304                        "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
55305                        cimg_instance,
55306                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
55307 
55308             err = std::fscanf(nfile,"%*[^\n] ");
55309           }
55310         }
55311       }
55312       if (!file) cimg::fclose(nfile);
55313       if (primitives._width!=nb_primitives)
55314         cimg::warn(_cimg_instance
55315                    "load_off(): Only %u/%u primitives read from file '%s'.",
55316                    cimg_instance,
55317                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
55318       return *this;
55319     }
55320 
55321     //! Load image sequence from a video file, using OpenCV library.
55322     /**
55323       \param filename Filename, as a C-string.
55324       \param first_frame Index of the first frame to read.
55325       \param last_frame Index of the last frame to read.
55326       \param step_frame Step value for frame reading.
55327       \param axis Alignment axis.
55328       \param align Appending alignment.
55329     **/
55330     CImg<T>& load_video(const char *const filename,
55331                         const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55332                         const unsigned int step_frame=1,
55333                         const char axis='z', const float align=0) {
55334       return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
55335     }
55336 
55337     //! Load image sequence from a video file, using OpenCV library \newinstance.
55338     static CImg<T> get_load_video(const char *const filename,
55339                                   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
55340                                   const unsigned int step_frame=1,
55341                                   const char axis='z', const float align=0) {
55342       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
55343     }
55344 
55345     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
55346     /**
55347       \param filename Filename, as a C-string.
55348       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
55349       \param align Appending alignment.
55350     **/
55351     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
55352       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
55353     }
55354 
55355     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
55356     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
55357       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
55358     }
55359 
55360     //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
55361     /**
55362       \param filename Filename, as a C-string.
55363       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
55364       \param align Appending alignment.
55365     **/
55366     CImg<T>& load_gif_external(const char *const filename,
55367                                const char axis='z', const float align=0) {
55368       return get_load_gif_external(filename,axis,align).move_to(*this);
55369     }
55370 
55371     //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
55372     static CImg<T> get_load_gif_external(const char *const filename,
55373                                          const char axis='z', const float align=0) {
55374       return CImgList<T>().load_gif_external(filename).get_append(axis,align);
55375     }
55376 
55377     //! Load image from a HEIC file.
55378     /**
55379        \param filename Filename, as a C-string.
55380     **/
55381     CImg<T>& load_heif(const char *const filename) {
55382       return _load_heif(filename);
55383     }
55384 
55385     //! Load image from a HEIC file \newinstance.
55386     static CImg<T> get_load_heif(const char *const filename) {
55387       return CImg<T>().load_heif(filename);
55388     }
55389 
55390     CImg<T>& _load_heif(const char *const filename) {
55391 #ifndef cimg_use_heif
55392       return load_other(filename);
55393 #else
55394       try {
55395         heif::Context ctx;
55396         ctx.read_from_file(filename);
55397 
55398         heif::ImageHandle handle = ctx.get_primary_image_handle();
55399         const heif::Image image =
55400           handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA:
55401                               heif_chroma_interleaved_RGB);
55402         const int
55403           W = image.get_width(heif_channel_interleaved),
55404           H = image.get_height(heif_channel_interleaved),
55405           S = handle.has_alpha_channel()?4:3;
55406         assign(W,H,1,S);
55407 
55408         int stride;
55409         const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride);
55410         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;
55411         cimg_forY(*this,y) {
55412           const unsigned char *ptrs = buffer + y*stride;
55413           if (ptr_a) cimg_forX(*this,x) { // RGBA
55414               *(ptr_r++) = (T)*(ptrs++);
55415               *(ptr_g++) = (T)*(ptrs++);
55416               *(ptr_b++) = (T)*(ptrs++);
55417               *(ptr_a++) = (T)*(ptrs++);
55418             }
55419           else cimg_forX(*this,x) { // RGB
55420               *(ptr_r++) = (T)*(ptrs++);
55421               *(ptr_g++) = (T)*(ptrs++);
55422               *(ptr_b++) = (T)*(ptrs++);
55423             }
55424         }
55425       } catch (const heif::Error& e) {
55426         throw CImgInstanceException(_cimg_instance
55427                                     "load_heif(): Unable to decode image: %s",
55428                                     cimg_instance,
55429                                     e.get_message().c_str());
55430       } catch (...) {
55431         throw;
55432       }
55433       return *this;
55434 #endif
55435     }
55436 
55437     //! Load image using GraphicsMagick's external tool 'gm'.
55438     /**
55439        \param filename Filename, as a C-string.
55440     **/
55441     CImg<T>& load_graphicsmagick_external(const char *const filename) {
55442       if (!filename)
55443         throw CImgArgumentException(_cimg_instance
55444                                     "load_graphicsmagick_external(): Specified filename is (null).",
55445                                     cimg_instance);
55446       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55447       CImg<charT> command(1024), filename_tmp(256);
55448       std::FILE *file = 0;
55449       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55450 #if cimg_OS==1
55451       if (!cimg::system("which gm")) {
55452         cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-",
55453                       cimg::graphicsmagick_path(),
55454                       s_filename.data(),
55455 #ifdef cimg_use_png
55456                       "png"
55457 #else
55458                       "pnm"
55459 #endif
55460                       );
55461         file = popen(command,"r");
55462         if (file) {
55463           const unsigned int omode = cimg::exception_mode();
55464           cimg::exception_mode(0);
55465           try {
55466 #ifdef cimg_use_png
55467             load_png(file);
55468 #else
55469             load_pnm(file);
55470 #endif
55471           } catch (...) {
55472             pclose(file);
55473             cimg::exception_mode(omode);
55474             throw CImgIOException(_cimg_instance
55475                                   "load_graphicsmagick_external(): Failed to load file '%s' "
55476                                   "with external command 'gm'.",
55477                                   cimg_instance,
55478                                   filename);
55479           }
55480           pclose(file);
55481           return *this;
55482         }
55483       }
55484 #endif
55485       do {
55486         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
55487                       cimg::temporary_path(),
55488                       cimg_file_separator,
55489                       cimg::filenamerand(),
55490 #ifdef cimg_use_png
55491                       "png"
55492 #else
55493                       "pnm"
55494 #endif
55495                       );
55496         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55497       } while (file);
55498       cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"",
55499                     cimg::graphicsmagick_path(),
55500                     s_filename.data(),
55501                     CImg<charT>::string(filename_tmp)._system_strescape().data());
55502       cimg::system(command,cimg::graphicsmagick_path());
55503       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55504         cimg::fclose(cimg::fopen(filename,"r"));
55505         throw CImgIOException(_cimg_instance
55506                               "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
55507                               cimg_instance,
55508                               filename);
55509 
55510       } else cimg::fclose(file);
55511 #ifdef cimg_use_png
55512       load_png(filename_tmp);
55513 #else
55514       load_pnm(filename_tmp);
55515 #endif
55516       std::remove(filename_tmp);
55517       return *this;
55518     }
55519 
55520     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
55521     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
55522       return CImg<T>().load_graphicsmagick_external(filename);
55523     }
55524 
55525     //! Load gzipped image file, using external tool 'gunzip'.
55526     /**
55527        \param filename Filename, as a C-string.
55528     **/
55529     CImg<T>& load_gzip_external(const char *const filename) {
55530       if (!filename)
55531         throw CImgIOException(_cimg_instance
55532                               "load_gzip_external(): Specified filename is (null).",
55533                               cimg_instance);
55534       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55535       CImg<charT> command(1024), filename_tmp(256), body(256);
55536       const char
55537         *const ext = cimg::split_filename(filename,body),
55538         *const ext2 = cimg::split_filename(body,0);
55539 
55540       std::FILE *file = 0;
55541       do {
55542         if (!cimg::strcasecmp(ext,"gz")) {
55543           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
55544                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
55545           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
55546                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55547         } else {
55548           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
55549                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
55550           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
55551                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55552         }
55553         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55554       } while (file);
55555       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
55556                     cimg::gunzip_path(),
55557                     CImg<charT>::string(filename)._system_strescape().data(),
55558                     CImg<charT>::string(filename_tmp)._system_strescape().data());
55559       cimg::system(command);
55560       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55561         cimg::fclose(cimg::fopen(filename,"r"));
55562         throw CImgIOException(_cimg_instance
55563                               "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
55564                               cimg_instance,
55565                               filename);
55566 
55567       } else cimg::fclose(file);
55568       load(filename_tmp);
55569       std::remove(filename_tmp);
55570       return *this;
55571     }
55572 
55573     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
55574     static CImg<T> get_load_gzip_external(const char *const filename) {
55575       return CImg<T>().load_gzip_external(filename);
55576     }
55577 
55578     //! Load image using ImageMagick's external tool 'convert'.
55579     /**
55580        \param filename Filename, as a C-string.
55581     **/
55582     CImg<T>& load_imagemagick_external(const char *const filename) {
55583       if (!filename)
55584         throw CImgArgumentException(_cimg_instance
55585                                     "load_imagemagick_external(): Specified filename is (null).",
55586                                     cimg_instance);
55587       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55588       CImg<charT> command(1024), filename_tmp(256);
55589       std::FILE *file = 0;
55590       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55591 #if cimg_OS==1
55592       if (!cimg::system("which convert")) {
55593         cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-",
55594                       cimg::imagemagick_path(),
55595                       !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
55596                       s_filename.data(),
55597 #ifdef cimg_use_png
55598                       "png"
55599 #else
55600                       "pnm"
55601 #endif
55602                       );
55603         file = popen(command,"r");
55604         if (file) {
55605           const unsigned int omode = cimg::exception_mode();
55606           cimg::exception_mode(0);
55607           try {
55608 #ifdef cimg_use_png
55609             load_png(file);
55610 #else
55611             load_pnm(file);
55612 #endif
55613           } catch (...) {
55614             pclose(file);
55615             cimg::exception_mode(omode);
55616             throw CImgIOException(_cimg_instance
55617                                   "load_imagemagick_external(): Failed to load file '%s' with "
55618                                   "external command 'magick/convert'.",
55619                                   cimg_instance,
55620                                   filename);
55621           }
55622           pclose(file);
55623           return *this;
55624         }
55625       }
55626 #endif
55627       do {
55628         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
55629                       cimg::temporary_path(),
55630                       cimg_file_separator,
55631                       cimg::filenamerand(),
55632 #ifdef cimg_use_png
55633                       "png"
55634 #else
55635                       "pnm"
55636 #endif
55637                       );
55638         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55639       } while (file);
55640       cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"",
55641                     cimg::imagemagick_path(),
55642                     !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
55643                     s_filename.data(),
55644                     CImg<charT>::string(filename_tmp)._system_strescape().data());
55645       cimg::system(command,cimg::imagemagick_path());
55646       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55647         cimg::fclose(cimg::fopen(filename,"r"));
55648         throw CImgIOException(_cimg_instance
55649                               "load_imagemagick_external(): Failed to load file '%s' with "
55650                               "external command 'magick/convert'.",
55651                               cimg_instance,
55652                               filename);
55653 
55654       } else cimg::fclose(file);
55655 #ifdef cimg_use_png
55656       load_png(filename_tmp);
55657 #else
55658       load_pnm(filename_tmp);
55659 #endif
55660       std::remove(filename_tmp);
55661       return *this;
55662     }
55663 
55664     //! Load image using ImageMagick's external tool 'convert' \newinstance.
55665     static CImg<T> get_load_imagemagick_external(const char *const filename) {
55666       return CImg<T>().load_imagemagick_external(filename);
55667     }
55668 
55669     //! Load image from a DICOM file, using Medcon's external tool 'medcon'.
55670     /**
55671        \param filename Filename, as a C-string.
55672     **/
55673     CImg<T>& load_medcon_external(const char *const filename) {
55674       if (!filename)
55675         throw CImgArgumentException(_cimg_instance
55676                                     "load_medcon_external(): Specified filename is (null).",
55677                                     cimg_instance);
55678       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55679       CImg<charT> command(1024), filename_tmp(256), body(256);
55680       cimg::fclose(cimg::fopen(filename,"r"));
55681       std::FILE *file = 0;
55682       do {
55683         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
55684         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55685       } while (file);
55686       cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"",
55687                     cimg::medcon_path(),
55688                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
55689                     CImg<charT>::string(filename)._system_strescape().data());
55690       cimg::system(command, cimg::medcon_path());
55691       cimg::split_filename(filename_tmp,body);
55692 
55693       cimg_snprintf(command,command._width,"%s.hdr",body._data);
55694       file = cimg::std_fopen(command,"rb");
55695       if (!file) {
55696         cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
55697         file = cimg::std_fopen(command,"rb");
55698         if (!file) {
55699           throw CImgIOException(_cimg_instance
55700                                 "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
55701                                 cimg_instance,
55702                                 filename);
55703         }
55704       }
55705       cimg::fclose(file);
55706       load_analyze(command);
55707       std::remove(command);
55708       cimg::split_filename(command,body);
55709       cimg_snprintf(command,command._width,"%s.img",body._data);
55710       std::remove(command);
55711       return *this;
55712     }
55713 
55714     //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance.
55715     static CImg<T> get_load_medcon_external(const char *const filename) {
55716       return CImg<T>().load_medcon_external(filename);
55717     }
55718 
55719     //! Load image from a .pdf file.
55720     /**
55721        \param filename Filename, as a C-string.
55722        \param resolution Image resolution.
55723     **/
55724     CImg<T>& load_pdf_external(const char *const filename, const unsigned int resolution=400) {
55725       if (!filename)
55726         throw CImgArgumentException(_cimg_instance
55727                                     "load_pdf_external(): Specified filename is (null).",
55728                                     cimg_instance);
55729       CImg<charT> command(1024), filename_tmp(256);
55730       std::FILE *file = 0;
55731       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55732 #if cimg_OS==1
55733       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"",
55734                     resolution,s_filename.data());
55735       file = popen(command,"r");
55736       if (file) {
55737         const unsigned int omode = cimg::exception_mode();
55738         cimg::exception_mode(0);
55739         try { load_pnm(file); } catch (...) {
55740           pclose(file);
55741           cimg::exception_mode(omode);
55742           throw CImgIOException(_cimg_instance
55743                                 "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
55744                                 cimg_instance,
55745                                 filename);
55746         }
55747         pclose(file);
55748         return *this;
55749       }
55750 #endif
55751       do {
55752         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
55753                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55754         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55755       } while (file);
55756       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"",
55757                     CImg<charT>::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data());
55758       cimg::system(command,"gs");
55759       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55760         cimg::fclose(cimg::fopen(filename,"r"));
55761         throw CImgIOException(_cimg_instance
55762                               "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
55763                               cimg_instance,
55764                               filename);
55765       } else cimg::fclose(file);
55766       load_pnm(filename_tmp);
55767       std::remove(filename_tmp);
55768       return *this;
55769     }
55770 
55771     //! Load image from a .pdf file \newinstance.
55772     static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
55773       return CImg<T>().load_pdf_external(filename,resolution);
55774     }
55775 
55776     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
55777     /**
55778        \param filename Filename, as a C-string.
55779     **/
55780     CImg<T>& load_dcraw_external(const char *const filename) {
55781       if (!filename)
55782         throw CImgArgumentException(_cimg_instance
55783                                     "load_dcraw_external(): Specified filename is (null).",
55784                                     cimg_instance);
55785       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
55786       CImg<charT> command(1024), filename_tmp(256);
55787       std::FILE *file = 0;
55788       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
55789 #if cimg_OS==1
55790       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
55791                     cimg::dcraw_path(),s_filename.data());
55792       file = popen(command,"r");
55793       if (file) {
55794         const unsigned int omode = cimg::exception_mode();
55795         cimg::exception_mode(0);
55796         try { load_pnm(file); } catch (...) {
55797           pclose(file);
55798           cimg::exception_mode(omode);
55799           throw CImgIOException(_cimg_instance
55800                                 "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
55801                                 cimg_instance,
55802                                 filename);
55803         }
55804         pclose(file);
55805         return *this;
55806       }
55807 #endif
55808       do {
55809         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
55810                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
55811         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
55812       } while (file);
55813       cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"",
55814                     cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
55815       cimg::system(command,cimg::dcraw_path());
55816       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
55817         cimg::fclose(cimg::fopen(filename,"r"));
55818         throw CImgIOException(_cimg_instance
55819                               "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
55820                               cimg_instance,
55821                               filename);
55822 
55823       } else cimg::fclose(file);
55824       load_pnm(filename_tmp);
55825       std::remove(filename_tmp);
55826       return *this;
55827     }
55828 
55829     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
55830     static CImg<T> get_load_dcraw_external(const char *const filename) {
55831       return CImg<T>().load_dcraw_external(filename);
55832     }
55833 
55834 #ifdef cimg_use_opencv
55835 
55836     // Convert a continuous cv::Mat<uchar> to a CImg<uchar>.
55837     static CImg<ucharT> _cvmat2cimg(const cv::Mat &src) {
55838       if (src.channels()==1) return CImg<ucharT>(src.ptr(),src.cols,src.rows,1,1);
55839       else if (src.channels()==3) { // BGR
55840         CImg<ucharT> res(src.cols,src.rows,1,src.channels());
55841         const unsigned char *ptrs = src.ptr();
55842         unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2);
55843         cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); }
55844         return res;
55845       }
55846       return CImg<ucharT>(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx");
55847     }
55848 
55849     // Convert a CImg<T> to a cv::Mat.
55850     cv::Mat _cimg2cvmat() const {
55851       if (is_empty())
55852         throw CImgInstanceException(_cimg_instance
55853                                     "_cimg2cvmat() : Instance image is empty.",
55854                                     cimg_instance);
55855       if (_spectrum==2)
55856         throw CImgInstanceException(_cimg_instance
55857                                     "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').",
55858                                     cimg_instance);
55859       if (_depth!=1)
55860         throw CImgInstanceException(_cimg_instance
55861                                     "_cimg2cvmat() : Invalid number of slices (should be '1').",
55862                                     cimg_instance);
55863       int mat_type = -1;
55864       if (pixel_type()==cimg::type<unsigned char>::string()) mat_type = CV_8UC1;
55865       if (pixel_type()==cimg::type<char>::string()) mat_type = CV_8SC1;
55866       if (pixel_type()==cimg::type<unsigned short>::string()) mat_type = CV_16UC1;
55867       if (pixel_type()==cimg::type<short>::string()) mat_type = CV_16SC1;
55868       if (pixel_type()==cimg::type<int>::string()) mat_type = CV_32SC1;
55869       if (pixel_type()==cimg::type<float>::string()) mat_type = CV_32FC1;
55870       if (pixel_type()==cimg::type<double>::string()) mat_type = CV_64FC1;
55871       if (mat_type<0)
55872         throw CImgInstanceException(_cimg_instance
55873                                     "_cvmat2cimg() : pixel type '%s' is not supported.",
55874                                     cimg_instance,pixel_type());
55875       cv::Mat res;
55876       std::vector<cv::Mat> channels(_spectrum);
55877       if (_spectrum>1) {
55878         cimg_forC(*this,c)
55879           channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c));
55880         cv::merge(channels,res);
55881       } else res = cv::Mat(_height,_width,mat_type,_data).clone();
55882       return res;
55883     }
55884 
55885 #endif
55886 
55887     //! Load image from a camera stream, using OpenCV.
55888     /**
55889        \param index Index of the camera to capture images from (from 0 to 63).
55890        \param capture_width Width of the desired image ('0' stands for default value).
55891        \param capture_height Height of the desired image ('0' stands for default value).
55892        \param skip_frames Number of frames to skip before the capture.
55893        \param release_camera Tells if the camera resource must be released at the end of the method.
55894     **/
55895     CImg<T>& load_camera(const unsigned int camera_index=0,
55896                          const unsigned int capture_width=0, const unsigned int capture_height=0,
55897                          const unsigned int skip_frames=0, const bool release_camera=true) {
55898 #ifdef cimg_use_opencv
55899       if (camera_index>=64)
55900         throw CImgArgumentException(_cimg_instance
55901                                     "load_camera(): Invalid request for camera #%u "
55902                                     "(no more than 100 cameras can be managed simultaneously).",
55903                                     cimg_instance,
55904                                     camera_index);
55905       static cv::VideoCapture *captures[64] = { 0 };
55906       static unsigned int captures_w[64], captures_h[64];
55907       if (release_camera) {
55908         cimg::mutex(9);
55909         if (captures[camera_index]) captures[camera_index]->release();
55910         delete captures[camera_index];
55911         captures[camera_index] = 0;
55912         captures_w[camera_index] = captures_h[camera_index] = 0;
55913         cimg::mutex(9,0);
55914         return *this;
55915       }
55916       if (!captures[camera_index]) {
55917         cimg::mutex(9);
55918         captures[camera_index] = new cv::VideoCapture(camera_index);
55919         captures_w[camera_index] = captures_h[camera_index] = 0;
55920         if (!captures[camera_index]->isOpened()) {
55921           delete captures[camera_index];
55922           captures[camera_index] = 0;
55923           cimg::mutex(9,0);
55924           throw CImgIOException(_cimg_instance
55925                                 "load_camera(): Failed to initialize camera #%u.",
55926                                 cimg_instance,
55927                                 camera_index);
55928         }
55929         cimg::mutex(9,0);
55930       }
55931       cimg::mutex(9);
55932       if (capture_width!=captures_w[camera_index]) {
55933         captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width);
55934         captures_w[camera_index] = capture_width;
55935       }
55936       if (capture_height!=captures_h[camera_index]) {
55937         captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height);
55938         captures_h[camera_index] = capture_height;
55939       }
55940       for (unsigned int i = 0; i<skip_frames; ++i) captures[camera_index]->grab();
55941       cv::Mat cvimg;
55942       captures[camera_index]->read(cvimg);
55943       if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this);
55944       cimg::mutex(9,0);
55945       return *this;
55946 #else
55947       cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
55948       throw CImgIOException(_cimg_instance
55949                             "load_camera(): This function requires features from the OpenCV library "
55950                             "('-Dcimg_use_opencv' must be defined).",
55951                             cimg_instance);
55952 #endif
55953     }
55954 
55955     //! Load image from a camera stream, using OpenCV \newinstance.
55956     static CImg<T> get_load_camera(const unsigned int camera_index=0,
55957                                    const unsigned int capture_width=0, const unsigned int capture_height=0,
55958                                    const unsigned int skip_frames=0, const bool release_camera=true) {
55959       return CImg<T>().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera);
55960     }
55961 
55962     //! Load image using various non-native ways.
55963     /**
55964        \param filename Filename, as a C-string.
55965     **/
55966     CImg<T>& load_other(const char *const filename) {
55967       if (!filename)
55968         throw CImgArgumentException(_cimg_instance
55969                                     "load_other(): Specified filename is (null).",
55970                                     cimg_instance);
55971 
55972       const unsigned int omode = cimg::exception_mode();
55973       cimg::exception_mode(0);
55974       try { load_magick(filename); }
55975       catch (CImgException&) {
55976         try { load_imagemagick_external(filename); }
55977         catch (CImgException&) {
55978           try { load_graphicsmagick_external(filename); }
55979           catch (CImgException&) {
55980             try { load_cimg(filename); }
55981             catch (CImgException&) {
55982               try {
55983                 cimg::fclose(cimg::fopen(filename,"rb"));
55984               } catch (CImgException&) {
55985                 cimg::exception_mode(omode);
55986                 throw CImgIOException(_cimg_instance
55987                                       "load_other(): Failed to open file '%s'.",
55988                                       cimg_instance,
55989                                       filename);
55990               }
55991               cimg::exception_mode(omode);
55992               throw CImgIOException(_cimg_instance
55993                                     "load_other(): Failed to recognize format of file '%s'.",
55994                                     cimg_instance,
55995                                     filename);
55996             }
55997           }
55998         }
55999       }
56000       cimg::exception_mode(omode);
56001       return *this;
56002     }
56003 
56004     //! Load image using various non-native ways \newinstance.
56005     static CImg<T> get_load_other(const char *const filename) {
56006       return CImg<T>().load_other(filename);
56007     }
56008 
56009     //@}
56010     //---------------------------
56011     //
56012     //! \name Data Output
56013     //@{
56014     //---------------------------
56015 
56016     //! Display information about the image data.
56017     /**
56018        \param title Name for the considered image.
56019        \param display_stats Tells to compute and display image statistics.
56020     **/
56021     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
56022 
56023       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
56024       CImg<doubleT> st;
56025       if (!is_empty() && display_stats) {
56026         st = get_stats();
56027         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
56028         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
56029       }
56030 
56031       const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
56032         mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
56033 
56034       CImg<charT> _title(64);
56035       if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
56036 
56037       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
56038                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
56039                    cimg::t_bold,cimg::t_normal,(void*)this,
56040                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
56041                    (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
56042                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
56043                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
56044       if (_data)
56045         std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
56046       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
56047 
56048       if (!is_empty()) cimg_foroff(*this,off) {
56049         std::fprintf(cimg::output(),"%g",(double)_data[off]);
56050         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
56051         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
56052       }
56053       if (!is_empty() && display_stats)
56054         std::fprintf(cimg::output(),
56055                      " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
56056                      "%scoords_max%s = (%u,%u,%u,%u).\n",
56057                      cimg::t_bold,cimg::t_normal,st[0],
56058                      cimg::t_bold,cimg::t_normal,st[1],
56059                      cimg::t_bold,cimg::t_normal,st[2],
56060                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
56061                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
56062                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
56063       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
56064       std::fflush(cimg::output());
56065       return *this;
56066     }
56067 
56068     //! Display image into a CImgDisplay window.
56069     /**
56070        \param disp Display window.
56071     **/
56072     const CImg<T>& display(CImgDisplay& disp) const {
56073       disp.display(*this);
56074       return *this;
56075     }
56076 
56077     //! Display image into a CImgDisplay window, in an interactive way.
56078     /**
56079         \param disp Display window.
56080         \param display_info Tells if image information are displayed on the standard output.
56081         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
56082         \param exit_on_anykey Exit function when any key is pressed.
56083     **/
56084     const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
56085                            const bool exit_on_anykey=false) const {
56086       return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
56087     }
56088 
56089     //! Display image into an interactive window.
56090     /**
56091         \param title Window title
56092         \param display_info Tells if image information are displayed on the standard output.
56093         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
56094         \param exit_on_anykey Exit function when any key is pressed.
56095     **/
56096     const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
56097                            const bool exit_on_anykey=false) const {
56098       CImgDisplay disp;
56099       return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
56100     }
56101 
56102     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
56103                             unsigned int *const XYZ, const bool exit_on_anykey,
56104                             const bool exit_on_singleclick) const {
56105       unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
56106       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
56107         old_mouse_x = -1, old_mouse_y = -1;
56108 
56109       if (!disp) {
56110         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
56111         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
56112         else disp.set_title("%s",title);
56113       } else if (title) disp.set_title("%s",title);
56114       disp.show().flush();
56115 
56116       const CImg<char> dtitle = CImg<char>::string(disp.title());
56117       if (display_info) print(dtitle);
56118 
56119       CImg<T> zoom;
56120       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
56121         if (reset_view) {
56122           if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
56123           else {
56124             _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2;
56125             _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2;
56126             _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2;
56127           }
56128           x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
56129           disp.resize(cimg_fitscreen(_width,_height,_depth),false);
56130           oldw = disp._width; oldh = disp._height;
56131           resize_disp = true;
56132           reset_view = false;
56133         }
56134         if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
56135           if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
56136         } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
56137 
56138         const CImg<T>& visu = zoom?zoom:*this;
56139         const unsigned int
56140           dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
56141           tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
56142         if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
56143           const float
56144             ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh,
56145             dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height());
56146           const unsigned int
56147             imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM);
56148           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
56149           resize_disp = false;
56150         }
56151         oldw = tw; oldh = th;
56152 
56153         bool
56154           go_up = false, go_down = false, go_left = false, go_right = false,
56155           go_inc = false, go_dec = false, go_in = false, go_out = false,
56156           go_in_center = false;
56157 
56158         disp.set_title("%s",dtitle._data);
56159         if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
56160         if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
56161         if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
56162 
56163         disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
56164         CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
56165         old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
56166         is_first_select = false;
56167 
56168         if (disp.wheel()) {
56169           if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
56170               (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
56171             go_left = !(go_right = disp.wheel()>0);
56172           } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
56173             go_down = !(go_up = disp.wheel()>0);
56174           } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56175             go_out = !(go_in = disp.wheel()>0); go_in_center = false;
56176           }
56177           disp.set_wheel();
56178         }
56179 
56180         const int
56181           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
56182           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
56183         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
56184           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
56185           x0+=sx0; y0+=sy0; z0+=sz0;
56186           if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
56187             if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true;
56188           }
56189           resize_disp = true;
56190         } else switch (key = disp.key()) {
56191 #if cimg_OS!=2
56192           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
56193 #endif
56194           case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
56195           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
56196               // Special mode: play stack of frames
56197               const unsigned int
56198                 w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
56199                 h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
56200               float frame_timing = 5;
56201               bool is_stopped = false;
56202               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
56203               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
56204                 if (disp.is_resized()) disp.resize(false);
56205                 if (!timer) {
56206                   visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
56207                   (++_XYZ[2])%=visu._depth;
56208                 }
56209                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
56210                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); }
56211                 switch (key = disp.key()) {
56212 #if cimg_OS!=2
56213                 case cimg::keyCTRLRIGHT :
56214 #endif
56215                 case cimg::keyCTRLLEFT : key = 0; break;
56216                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
56217                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
56218                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
56219                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
56220                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
56221                   (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
56222                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56223                     disp.set_fullscreen(false).
56224                       resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
56225                              CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
56226                     disp.set_key(key,false); key = 0;
56227                   } break;
56228                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56229                     disp.set_fullscreen(false).
56230                       resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
56231                   } break;
56232                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56233                     disp.set_fullscreen(false).
56234                       resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
56235                   } break;
56236                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56237                     disp.resize(disp.screen_width(),disp.screen_height(),false).
56238                       toggle_fullscreen().set_key(key,false); key = 0;
56239                   } break;
56240                 }
56241                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
56242                 disp.wait(20);
56243               }
56244               const unsigned int
56245                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
56246                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
56247               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
56248               key = 0;
56249             } break;
56250           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
56251           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
56252           case cimg::keyPADSUB : go_out = true; key = 0; break;
56253           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
56254           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
56255           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
56256           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
56257           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
56258           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
56259           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
56260           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
56261           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
56262           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
56263           }
56264         if (go_in) {
56265           const int
56266             mx = go_in_center?disp.width()/2:disp.mouse_x(),
56267             my = go_in_center?disp.height()/2:disp.mouse_y(),
56268             mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
56269             mY = my*(height() + (depth()>1?depth():0))/disp.height();
56270           int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
56271           if (mX<width() && mY<height())  {
56272             X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
56273           }
56274           if (mX<width() && mY>=height()) {
56275             X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
56276           }
56277           if (mX>=width() && mY<height()) {
56278             Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
56279           }
56280           if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
56281           if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
56282           if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
56283         }
56284         if (go_out) {
56285           const int
56286             delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
56287             ndelta_x = delta_x?delta_x:(_width>1),
56288             ndelta_y = delta_y?delta_y:(_height>1),
56289             ndelta_z = delta_z?delta_z:(_depth>1);
56290           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
56291           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
56292           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
56293           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
56294           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
56295           if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
56296           if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
56297           if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
56298           const float
56299             ratio = (float)(x1-x0)/(y1-y0),
56300             ratiow = (float)disp._width/disp._height,
56301             sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
56302           if (sub>0.01) resize_disp = true;
56303         }
56304         if (go_left) {
56305           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
56306           if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
56307           else { x1-=x0; x0 = 0; }
56308         }
56309         if (go_right) {
56310           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
56311           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
56312           else { x0+=(width() - 1 - x1); x1 = width() - 1; }
56313         }
56314         if (go_up) {
56315           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
56316           if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
56317           else { y1-=y0; y0 = 0; }
56318         }
56319         if (go_down) {
56320           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
56321           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
56322           else { y0+=(height() - 1 - y1); y1 = height() - 1; }
56323         }
56324         if (go_inc) {
56325           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
56326           if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
56327           else { z1-=z0; z0 = 0; }
56328         }
56329         if (go_dec) {
56330           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
56331           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
56332           else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
56333         }
56334         disp.wait(100);
56335         if (!exit_on_anykey && key && key!=cimg::keyESC &&
56336             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
56337           key = 0;
56338         }
56339       }
56340       disp.set_key(key);
56341       if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
56342       return *this;
56343     }
56344 
56345     //! Display object 3D in an interactive window.
56346     /**
56347        \param disp Display window.
56348        \param vertices Vertices data of the 3D object.
56349        \param primitives Primitives data of the 3D object.
56350        \param colors Colors data of the 3D object.
56351        \param opacities Opacities data of the 3D object.
56352        \param centering Tells if the 3D object must be centered for the display.
56353        \param render_static Rendering mode.
56354        \param render_motion Rendering mode, when the 3D object is moved.
56355        \param is_double_sided Tells if the object primitives are double-sided.
56356        \param focale Focale
56357        \param light_x X-coordinate of the light source.
56358        \param light_y Y-coordinate of the light source.
56359        \param light_z Z-coordinate of the light source.
56360        \param specular_lightness Amount of specular light.
56361        \param specular_shininess Shininess of the object material.
56362        \param display_axes Tells if the 3D axes are displayed.
56363        \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix).
56364        \param exit_on_anykey Exit function when any key is pressed.
56365     **/
56366     template<typename tp, typename tf, typename tc, typename to>
56367     const CImg<T>& display_object3d(CImgDisplay& disp,
56368                                     const CImg<tp>& vertices,
56369                                     const CImgList<tf>& primitives,
56370                                     const CImgList<tc>& colors,
56371                                     const to& opacities,
56372                                     const bool centering=true,
56373                                     const int render_static=4, const int render_motion=1,
56374                                     const bool is_double_sided=true, const float focale=700,
56375                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56376                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56377                                     const bool display_axes=true, float *const pose_matrix=0,
56378                                     const bool exit_on_anykey=false) const {
56379       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
56380                                render_motion,is_double_sided,focale,
56381                                light_x,light_y,light_z,specular_lightness,specular_shininess,
56382                                display_axes,pose_matrix,exit_on_anykey);
56383     }
56384 
56385     //! Display object 3D in an interactive window \simplification.
56386     template<typename tp, typename tf, typename tc, typename to>
56387     const CImg<T>& display_object3d(const char *const title,
56388                                     const CImg<tp>& vertices,
56389                                     const CImgList<tf>& primitives,
56390                                     const CImgList<tc>& colors,
56391                                     const to& opacities,
56392                                     const bool centering=true,
56393                                     const int render_static=4, const int render_motion=1,
56394                                     const bool is_double_sided=true, const float focale=700,
56395                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56396                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56397                                     const bool display_axes=true, float *const pose_matrix=0,
56398                                     const bool exit_on_anykey=false) const {
56399       CImgDisplay disp;
56400       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
56401                                render_motion,is_double_sided,focale,
56402                                light_x,light_y,light_z,specular_lightness,specular_shininess,
56403                                display_axes,pose_matrix,exit_on_anykey);
56404     }
56405 
56406     //! Display object 3D in an interactive window \simplification.
56407     template<typename tp, typename tf, typename tc>
56408     const CImg<T>& display_object3d(CImgDisplay &disp,
56409                                     const CImg<tp>& vertices,
56410                                     const CImgList<tf>& primitives,
56411                                     const CImgList<tc>& colors,
56412                                     const bool centering=true,
56413                                     const int render_static=4, const int render_motion=1,
56414                                     const bool is_double_sided=true, const float focale=700,
56415                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56416                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56417                                     const bool display_axes=true, float *const pose_matrix=0,
56418                                     const bool exit_on_anykey=false) const {
56419       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
56420                               render_static,render_motion,is_double_sided,focale,
56421                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56422                               display_axes,pose_matrix,exit_on_anykey);
56423     }
56424 
56425     //! Display object 3D in an interactive window \simplification.
56426     template<typename tp, typename tf, typename tc>
56427     const CImg<T>& display_object3d(const char *const title,
56428                                     const CImg<tp>& vertices,
56429                                     const CImgList<tf>& primitives,
56430                                     const CImgList<tc>& colors,
56431                                     const bool centering=true,
56432                                     const int render_static=4, const int render_motion=1,
56433                                     const bool is_double_sided=true, const float focale=700,
56434                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56435                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56436                                     const bool display_axes=true, float *const pose_matrix=0,
56437                                     const bool exit_on_anykey=false) const {
56438       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
56439                               render_static,render_motion,is_double_sided,focale,
56440                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56441                               display_axes,pose_matrix,exit_on_anykey);
56442     }
56443 
56444     //! Display object 3D in an interactive window \simplification.
56445     template<typename tp, typename tf>
56446     const CImg<T>& display_object3d(CImgDisplay &disp,
56447                                     const CImg<tp>& vertices,
56448                                     const CImgList<tf>& primitives,
56449                                     const bool centering=true,
56450                                     const int render_static=4, const int render_motion=1,
56451                                     const bool is_double_sided=true, const float focale=700,
56452                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56453                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56454                                     const bool display_axes=true, float *const pose_matrix=0,
56455                                     const bool exit_on_anykey=false) const {
56456       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
56457                               render_static,render_motion,is_double_sided,focale,
56458                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56459                               display_axes,pose_matrix,exit_on_anykey);
56460     }
56461 
56462 
56463     //! Display object 3D in an interactive window \simplification.
56464     template<typename tp, typename tf>
56465     const CImg<T>& display_object3d(const char *const title,
56466                                     const CImg<tp>& vertices,
56467                                     const CImgList<tf>& primitives,
56468                                     const bool centering=true,
56469                                     const int render_static=4, const int render_motion=1,
56470                                     const bool is_double_sided=true, const float focale=700,
56471                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56472                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56473                                     const bool display_axes=true, float *const pose_matrix=0,
56474                                     const bool exit_on_anykey=false) const {
56475       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
56476                               render_static,render_motion,is_double_sided,focale,
56477                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56478                               display_axes,pose_matrix,exit_on_anykey);
56479     }
56480 
56481     //! Display object 3D in an interactive window \simplification.
56482     template<typename tp>
56483     const CImg<T>& display_object3d(CImgDisplay &disp,
56484                                     const CImg<tp>& vertices,
56485                                     const bool centering=true,
56486                                     const int render_static=4, const int render_motion=1,
56487                                     const bool is_double_sided=true, const float focale=700,
56488                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56489                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56490                                     const bool display_axes=true, float *const pose_matrix=0,
56491                                     const bool exit_on_anykey=false) const {
56492       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
56493                               render_static,render_motion,is_double_sided,focale,
56494                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56495                               display_axes,pose_matrix,exit_on_anykey);
56496     }
56497 
56498     //! Display object 3D in an interactive window \simplification.
56499     template<typename tp>
56500     const CImg<T>& display_object3d(const char *const title,
56501                                     const CImg<tp>& vertices,
56502                                     const bool centering=true,
56503                                     const int render_static=4, const int render_motion=1,
56504                                     const bool is_double_sided=true, const float focale=700,
56505                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
56506                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
56507                                     const bool display_axes=true, float *const pose_matrix=0,
56508                                     const bool exit_on_anykey=false) const {
56509       return display_object3d(title,vertices,CImgList<uintT>(),centering,
56510                               render_static,render_motion,is_double_sided,focale,
56511                               light_x,light_y,light_z,specular_lightness,specular_shininess,
56512                               display_axes,pose_matrix,exit_on_anykey);
56513     }
56514 
56515     template<typename tp, typename tf, typename tc, typename to>
56516     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
56517                                      const CImg<tp>& vertices,
56518                                      const CImgList<tf>& primitives,
56519                                      const CImgList<tc>& colors,
56520                                      const to& opacities,
56521                                      const bool centering,
56522                                      const int render_static, const int render_motion,
56523                                      const bool is_double_sided, const float focale,
56524                                      const float light_x, const float light_y, const float light_z,
56525                                      const float specular_lightness, const float specular_shininess,
56526                                      const bool display_axes, float *const pose_matrix,
56527                                      const bool exit_on_anykey) const {
56528       typedef typename cimg::superset<tp,float>::type tpfloat;
56529 
56530       // Check input arguments
56531       if (is_empty()) {
56532         CImg<T> background;
56533         if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128);
56534         else background.assign(1,2,1,3,32,64,32,116,64,96);
56535         if (disp) background.resize(disp.width(),disp.height(),1,-100,3);
56536         else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
56537                                               CImgDisplay::screen_height()/2,1),1,-100,3);
56538         return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
56539                                             render_static,render_motion,is_double_sided,focale,
56540                                             light_x,light_y,light_z,specular_lightness,specular_shininess,
56541                                             display_axes,pose_matrix,exit_on_anykey);
56542       } else { if (disp) disp.resize(*this,false); }
56543       CImg<charT> error_message(1024);
56544       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
56545         throw CImgArgumentException(_cimg_instance
56546                                     "display_object3d(): Invalid specified 3D object (%u,%u) (%s).",
56547                                     cimg_instance,vertices._width,primitives._width,error_message.data());
56548       if (vertices._width && !primitives) {
56549         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
56550         cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
56551         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
56552                                  render_static,render_motion,is_double_sided,focale,
56553                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
56554                                  display_axes,pose_matrix,exit_on_anykey);
56555       }
56556       if (!disp) {
56557         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
56558         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
56559                                    pixel_type(),vertices._width,primitives._width);
56560       } else if (title) disp.set_title("%s",title);
56561 
56562       // Init 3D objects and compute object statistics
56563       CImg<floatT>
56564         pose,
56565         rotated_vertices(vertices._width,3),
56566         bbox_vertices, rotated_bbox_vertices,
56567         axes_vertices, rotated_axes_vertices,
56568         bbox_opacities, axes_opacities;
56569       CImgList<uintT> bbox_primitives, axes_primitives;
56570       CImgList<tf> reverse_primitives;
56571       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
56572       unsigned int ns_width = 0, ns_height = 0;
56573       int _is_double_sided = (int)is_double_sided;
56574       bool ndisplay_axes = display_axes;
56575       const CImg<T>
56576         background_color(1,1,1,_spectrum,0),
56577         foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type<T>::max(),255));
56578       float
56579         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
56580         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
56581         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
56582         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
56583       const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
56584 
56585       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
56586                                                    xm,xM,xM,xm,xm,xM,xM,xm,
56587                                                    ym,ym,yM,yM,ym,ym,yM,yM,
56588                                                    zm,zm,zm,zm,zM,zM,zM,zM);
56589       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);
56590       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
56591       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
56592       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
56593 
56594       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
56595                                                    0,20,0,0,22,-6,-6,
56596                                                    0,0,20,0,-6,22,-6,
56597                                                    0,0,0,20,0,0,22);
56598       axes_opacities.assign(3,1,1,1,1);
56599       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
56600       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
56601 
56602       // Begin user interaction loop
56603       CImg<T> visu0(*this,false), visu;
56604       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
56605       bool init_pose = true, clicked = false, redraw = true;
56606       unsigned int key = 0, font_size = 32;
56607       int
56608         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
56609         nrender_static = render_static,
56610         nrender_motion = render_motion;
56611       disp.show().flush();
56612 
56613       while (!disp.is_closed() && !key) {
56614 
56615         // Init object pose
56616         if (init_pose) {
56617           const float
56618             ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1,
56619             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
56620           if (centering)
56621             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
56622           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
56623           if (pose_matrix) {
56624             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
56625             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
56626             pose0(3,3) = pose(3,3) = 1;
56627             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
56628             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
56629           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
56630           init_pose = false;
56631           redraw = true;
56632         }
56633 
56634         // Rotate and draw 3D object
56635         if (redraw) {
56636           const float
56637             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
56638             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
56639             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
56640           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
56641             const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2);
56642             float
56643               *const prv0 = rotated_vertices.data(),
56644               *const prv1 = rotated_vertices.data(0,1),
56645               *const prv2 = rotated_vertices.data(0,2);
56646             cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024))
56647             cimg_forX(vertices,l) {
56648               const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l];
56649               prv0[l] = r00*x + r10*y + r20*z + r30;
56650               prv1[l] = r01*x + r11*y + r21*z + r31;
56651               prv2[l] = r02*x + r12*y + r22*z + r32;
56652             }
56653           }
56654           else cimg_forX(bbox_vertices,l) {
56655               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
56656               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
56657               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
56658               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
56659             }
56660 
56661           // Draw objects
56662           const bool render_with_zbuffer = !clicked && nrender_static>0;
56663           visu = visu0;
56664           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
56665             visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56666                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
56667               draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56668                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
56669           else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
56670                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56671                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56672                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
56673                                    width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff,
56674                                    specular_lightness,specular_shininess,1,sprite_scale);
56675           // Draw axes
56676           if (ndisplay_axes) {
56677             const float
56678               n = 1e-8f + cimg::hypot(r00,r01,r02),
56679               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
56680               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
56681               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
56682               Xaxes = 25, Yaxes = visu._height - 38.f;
56683             cimg_forX(axes_vertices,l) {
56684               const float
56685                 x = axes_vertices(l,0),
56686                 y = axes_vertices(l,1),
56687                 z = axes_vertices(l,2);
56688               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
56689               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
56690               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
56691             }
56692             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f;
56693             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f;
56694             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f;
56695             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
56696                                axes_colors,axes_opacities,1,false,focale).
56697               draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
56698                         (int)(Yaxes + rotated_axes_vertices(4,1)),
56699                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
56700               draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
56701                         (int)(Yaxes + rotated_axes_vertices(5,1)),
56702                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
56703               draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
56704                         (int)(Yaxes + rotated_axes_vertices(6,1)),
56705                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
56706           }
56707           visu.display(disp);
56708           if (!clicked || nrender_motion==nrender_static) redraw = false;
56709         }
56710 
56711         // Handle user interaction
56712         if (!redraw) disp.wait();
56713         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
56714           redraw = true;
56715           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
56716           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
56717           const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT();
56718           if (disp.button()&1 && !is_keyCTRL) {
56719             const float
56720               R = 0.45f*std::min(disp.width(),disp.height()),
56721               R2 = R*R,
56722               u0 = (float)(x0 - disp.width()/2),
56723               v0 = (float)(y0 - disp.height()/2),
56724               u1 = (float)(x1 - disp.width()/2),
56725               v1 = (float)(y1 - disp.height()/2),
56726               n0 = cimg::hypot(u0,v0),
56727               n1 = cimg::hypot(u1,v1),
56728               nu0 = n0>R?(u0*R/n0):u0,
56729               nv0 = n0>R?(v0*R/n0):v0,
56730               nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
56731               nu1 = n1>R?(u1*R/n1):u1,
56732               nv1 = n1>R?(v1*R/n1):v1,
56733               nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
56734               u = nv0*nw1 - nw0*nv1,
56735               v = nw0*nu1 - nu0*nw1,
56736               w = nv0*nu1 - nu0*nv1,
56737               n = cimg::hypot(u,v,w),
56738               alpha = (float)std::asin(n/R2)*180/cimg::PI;
56739             (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
56740             x0 = x1; y0 = y1;
56741           }
56742           if (disp.button()&2 && !is_keyCTRL) {
56743             if (focale>0) Zoff-=(y0 - y1)*focale/400;
56744             else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; }
56745             x0 = x1; y0 = y1;
56746           }
56747           if (disp.wheel()) {
56748             if (focale>0) Zoff-=disp.wheel()*focale/20;
56749             else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; }
56750             disp.set_wheel();
56751           }
56752           if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) {
56753             Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1;
56754           }
56755           if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) {
56756             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
56757             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
56758           }
56759         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
56760 
56761         CImg<charT> filename(32);
56762         switch (key = disp.key()) {
56763 #if cimg_OS!=2
56764         case cimg::keyCTRLRIGHT :
56765 #endif
56766         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
56767         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56768             disp.set_fullscreen(false).
56769               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
56770                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
56771               _is_resized = true;
56772             disp.set_key(key,false); key = 0;
56773           } break;
56774         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56775             disp.set_fullscreen(false).
56776               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
56777             disp.set_key(key,false); key = 0;
56778           } break;
56779         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56780             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
56781             disp.set_key(key,false); key = 0;
56782           } break;
56783         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56784             if (!ns_width || !ns_height ||
56785                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
56786               ns_width = disp.screen_width()*3U/4;
56787               ns_height = disp.screen_height()*3U/4;
56788             }
56789             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
56790             else {
56791               ns_width = disp._width; ns_height = disp._height;
56792               disp.resize(disp.screen_width(),disp.screen_height(),false);
56793             }
56794             disp.toggle_fullscreen()._is_resized = true;
56795             disp.set_key(key,false); key = 0;
56796           } break;
56797         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56798             // Switch single/double-sided primitives.
56799             if (--_is_double_sided==-2) _is_double_sided = 1;
56800             if (_is_double_sided>=0) reverse_primitives.assign();
56801             else primitives.get_reverse_object3d().move_to(reverse_primitives);
56802             disp.set_key(key,false); key = 0; redraw = true;
56803           } break;
56804         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
56805             if (zbuffer) zbuffer.assign();
56806             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
56807             disp.set_key(key,false); key = 0; redraw = true;
56808           } break;
56809         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes
56810             ndisplay_axes = !ndisplay_axes;
56811             disp.set_key(key,false); key = 0; redraw = true;
56812           } break;
56813         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points
56814             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
56815             disp.set_key(key,false); key = 0; redraw = true;
56816           } break;
56817         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines
56818             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
56819             disp.set_key(key,false); key = 0; redraw = true;
56820           } break;
56821         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat
56822             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
56823             disp.set_key(key,false); key = 0; redraw = true;
56824           } break;
56825         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded
56826             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
56827             disp.set_key(key,false); key = 0; redraw = true;
56828           } break;
56829         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
56830             // Set rendering mode to gouraud-shaded.
56831             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
56832             disp.set_key(key,false); key = 0; redraw = true;
56833           } break;
56834         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded
56835             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
56836             disp.set_key(key,false); key = 0; redraw = true;
56837           } break;
56838         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
56839             static unsigned int snap_number = 0;
56840             std::FILE *file;
56841             do {
56842               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
56843               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56844             } while (file);
56845             (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
56846             visu.save(filename);
56847             (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
56848             disp.set_key(key,false); key = 0;
56849           } break;
56850         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
56851             static unsigned int snap_number = 0;
56852             std::FILE *file;
56853             do {
56854               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
56855               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56856             } while (file);
56857             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
56858             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
56859             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56860             disp.set_key(key,false); key = 0;
56861           } break;
56862         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
56863             static unsigned int snap_number = 0;
56864             std::FILE *file;
56865             do {
56866 
56867 #ifdef cimg_use_zlib
56868               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
56869 #else
56870               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
56871 #endif
56872               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56873             } while (file);
56874             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
56875             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
56876               save(filename);
56877             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56878             disp.set_key(key,false); key = 0;
56879           } break;
56880 
56881 #ifdef cimg_use_board
56882         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
56883             static unsigned int snap_number = 0;
56884             std::FILE *file;
56885             do {
56886               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
56887               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56888             } while (file);
56889             (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
56890             LibBoard::Board board;
56891             (+visu)._draw_object3d(&board,zbuffer.fill(0),
56892                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56893                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56894                                    colors,opacities,clicked?nrender_motion:nrender_static,
56895                                    _is_double_sided==1,focale,
56896                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
56897                                    specular_lightness,specular_shininess,1,
56898                                    sprite_scale);
56899             board.saveEPS(filename);
56900             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56901             disp.set_key(key,false); key = 0;
56902           } break;
56903         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
56904             static unsigned int snap_number = 0;
56905             std::FILE *file;
56906             do {
56907               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
56908               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
56909             } while (file);
56910             (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
56911             LibBoard::Board board;
56912             (+visu)._draw_object3d(&board,zbuffer.fill(0),
56913                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
56914                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
56915                                    colors,opacities,clicked?nrender_motion:nrender_static,
56916                                    _is_double_sided==1,focale,
56917                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
56918                                    specular_lightness,specular_shininess,1,
56919                                    sprite_scale);
56920             board.saveSVG(filename);
56921             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
56922             disp.set_key(key,false); key = 0;
56923           } break;
56924 #endif
56925         }
56926         if (disp.is_resized()) {
56927           disp.resize(false); visu0 = get_resize(disp,1);
56928           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
56929           redraw = true;
56930         }
56931         if (!exit_on_anykey && key && key!=cimg::keyESC &&
56932             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
56933           key = 0;
56934         }
56935       }
56936       if (pose_matrix) {
56937         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
56938         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
56939       }
56940       disp.set_button().set_key(key);
56941       return *this;
56942     }
56943 
56944     //! Display 1D graph in an interactive window.
56945     /**
56946        \param disp Display window.
56947        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
56948        \param vertex_type Vertex type.
56949        \param labelx Title for the horizontal axis, as a C-string.
56950        \param xmin Minimum value along the X-axis.
56951        \param xmax Maximum value along the X-axis.
56952        \param labely Title for the vertical axis, as a C-string.
56953        \param ymin Minimum value along the X-axis.
56954        \param ymax Maximum value along the X-axis.
56955        \param exit_on_anykey Exit function when any key is pressed.
56956     **/
56957     const CImg<T>& display_graph(CImgDisplay &disp,
56958                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
56959                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
56960                                  const char *const labely=0, const double ymin=0, const double ymax=0,
56961                                  const bool exit_on_anykey=false) const {
56962       return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
56963     }
56964 
56965     //! Display 1D graph in an interactive window \overloading.
56966     const CImg<T>& display_graph(const char *const title=0,
56967                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
56968                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
56969                                  const char *const labely=0, const double ymin=0, const double ymax=0,
56970                                  const bool exit_on_anykey=false) const {
56971       CImgDisplay disp;
56972       return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
56973     }
56974 
56975     const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
56976                                   const unsigned int plot_type=1, const unsigned int vertex_type=1,
56977                                   const char *const labelx=0, const double xmin=0, const double xmax=0,
56978                                   const char *const labely=0, const double ymin=0, const double ymax=0,
56979                                   const bool exit_on_anykey=false) const {
56980       if (is_empty())
56981         throw CImgInstanceException(_cimg_instance
56982                                     "display_graph(): Empty instance.",
56983                                     cimg_instance);
56984       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
56985                    set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
56986       const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
56987       const unsigned int old_normalization = disp.normalization();
56988       disp.show().flush()._normalization = 0;
56989 
56990       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
56991       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
56992       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
56993 
56994       for (bool reset_view = true; !key && !disp.is_closed(); ) {
56995         if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
56996         CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
56997         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
56998         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
56999         if (y0==y1) { --y0; ++y1; }
57000 
57001         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
57002                                                            labelx,
57003                                                            nxmin + x0*(nxmax - nxmin)/siz1,
57004                                                            nxmin + x1*(nxmax - nxmin)/siz1,
57005                                                            labely,y0,y1,true);
57006         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
57007         if (selection[0]>=0) {
57008           if (selection[2]<0) reset_view = true;
57009           else {
57010             x1 = x0 + selection[2]; x0+=selection[0];
57011             if (selection[1]>=0 && selection[3]>=0) {
57012               y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
57013               y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
57014             }
57015           }
57016         } else {
57017           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
57018           switch (key = (int)disp.key()) {
57019           case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
57020           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
57021           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
57022           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
57023             break;
57024           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
57025             break;
57026           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
57027           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
57028           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
57029           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
57030           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
57031           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
57032           }
57033           if (disp.wheel()) {
57034             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
57035             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
57036             else go_out = !(go_in = disp.wheel()>0);
57037             key = 0;
57038           }
57039 
57040           if (go_in) {
57041             const int
57042               xsiz = x1 - x0,
57043               mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
57044               cx = x0 + cimg::cut(mx,0,xsiz);
57045             if (x1 - x0>4) {
57046               x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
57047               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57048                 const double
57049                   ysiz = y1 - y0,
57050                   my = (mouse_y - 16)*ysiz/(disp.height() - 32),
57051                   cy = y1 - cimg::cut(my,0.,ysiz);
57052                 y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
57053               } else y0 = y1 = 0;
57054             }
57055           }
57056           if (go_out) {
57057             if (x0>0 || x1<(int)siz1) {
57058               const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
57059               const double ndelta_y = (y1 - y0)/8;
57060               x0-=ndelta_x; x1+=ndelta_x;
57061               y0-=ndelta_y; y1+=ndelta_y;
57062               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
57063               if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
57064             }
57065           }
57066           if (go_left) {
57067             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
57068             if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
57069             else { x1-=x0; x0 = 0; }
57070             go_left = false;
57071           }
57072           if (go_right) {
57073             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
57074             if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
57075             else { x0+=(siz1 - x1); x1 = (int)siz1; }
57076             go_right = false;
57077           }
57078           if (go_up) {
57079             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
57080             y0+=ndelta; y1+=ndelta;
57081             go_up = false;
57082           }
57083           if (go_down) {
57084             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
57085             y0-=ndelta; y1-=ndelta;
57086             go_down = false;
57087           }
57088         }
57089         if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
57090             (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
57091           disp.set_key(key,false);
57092           key = 0;
57093         }
57094       }
57095       disp._normalization = old_normalization;
57096       return *this;
57097     }
57098 
57099     //! Save image as a file.
57100     /**
57101        \param filename Filename, as a C-string.
57102        \param number When positive, represents an index added to the filename. Otherwise, no number is added.
57103        \param digits Number of digits used for adding the number to the filename.
57104        \note
57105        - The used file format is defined by the file extension in the filename \p filename.
57106        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
57107 
57108     **/
57109     const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
57110       if (!filename)
57111         throw CImgArgumentException(_cimg_instance
57112                                     "save(): Specified filename is (null).",
57113                                     cimg_instance);
57114       // Do not test for empty instances, since .cimg format is able to manage empty instances.
57115       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
57116       const char *const ext = cimg::split_filename(filename);
57117       CImg<charT> nfilename(1024);
57118       const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
57119         filename;
57120 
57121 #ifdef cimg_save_plugin
57122       cimg_save_plugin(fn);
57123 #endif
57124 #ifdef cimg_save_plugin1
57125       cimg_save_plugin1(fn);
57126 #endif
57127 #ifdef cimg_save_plugin2
57128       cimg_save_plugin2(fn);
57129 #endif
57130 #ifdef cimg_save_plugin3
57131       cimg_save_plugin3(fn);
57132 #endif
57133 #ifdef cimg_save_plugin4
57134       cimg_save_plugin4(fn);
57135 #endif
57136 #ifdef cimg_save_plugin5
57137       cimg_save_plugin5(fn);
57138 #endif
57139 #ifdef cimg_save_plugin6
57140       cimg_save_plugin6(fn);
57141 #endif
57142 #ifdef cimg_save_plugin7
57143       cimg_save_plugin7(fn);
57144 #endif
57145 #ifdef cimg_save_plugin8
57146       cimg_save_plugin8(fn);
57147 #endif
57148       // Text formats
57149       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
57150       else if (!cimg::strcasecmp(ext,"csv") ||
57151                !cimg::strcasecmp(ext,"dlm") ||
57152                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
57153       else if (!cimg::strcasecmp(ext,"cpp") ||
57154                !cimg::strcasecmp(ext,"hpp") ||
57155                !cimg::strcasecmp(ext,"h") ||
57156                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
57157 
57158       // 2D binary formats
57159       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
57160       else if (!cimg::strcasecmp(ext,"jpg") ||
57161                !cimg::strcasecmp(ext,"jpeg") ||
57162                !cimg::strcasecmp(ext,"jpe") ||
57163                !cimg::strcasecmp(ext,"jfif") ||
57164                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
57165       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
57166       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
57167       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
57168       else if (!cimg::strcasecmp(ext,"pgm") ||
57169                !cimg::strcasecmp(ext,"ppm") ||
57170                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
57171       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
57172       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
57173       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
57174       else if (!cimg::strcasecmp(ext,"tif") ||
57175                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
57176 
57177       // 3D binary formats
57178       else if (!*ext) {
57179 #ifdef cimg_use_zlib
57180         return save_cimg(fn,true);
57181 #else
57182         return save_cimg(fn,false);
57183 #endif
57184       } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
57185       else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
57186       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
57187       else if (!cimg::strcasecmp(ext,"hdr") ||
57188                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
57189       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
57190       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
57191       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
57192       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
57193 
57194       // Archive files
57195       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
57196 
57197       // Image sequences
57198       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
57199       else if (!cimg::strcasecmp(ext,"avi") ||
57200                !cimg::strcasecmp(ext,"mov") ||
57201                !cimg::strcasecmp(ext,"asf") ||
57202                !cimg::strcasecmp(ext,"divx") ||
57203                !cimg::strcasecmp(ext,"flv") ||
57204                !cimg::strcasecmp(ext,"mpg") ||
57205                !cimg::strcasecmp(ext,"m1v") ||
57206                !cimg::strcasecmp(ext,"m2v") ||
57207                !cimg::strcasecmp(ext,"m4v") ||
57208                !cimg::strcasecmp(ext,"mjp") ||
57209                !cimg::strcasecmp(ext,"mp4") ||
57210                !cimg::strcasecmp(ext,"mkv") ||
57211                !cimg::strcasecmp(ext,"mpe") ||
57212                !cimg::strcasecmp(ext,"movie") ||
57213                !cimg::strcasecmp(ext,"ogm") ||
57214                !cimg::strcasecmp(ext,"ogg") ||
57215                !cimg::strcasecmp(ext,"ogv") ||
57216                !cimg::strcasecmp(ext,"qt") ||
57217                !cimg::strcasecmp(ext,"rm") ||
57218                !cimg::strcasecmp(ext,"vob") ||
57219                !cimg::strcasecmp(ext,"webm") ||
57220                !cimg::strcasecmp(ext,"wmv") ||
57221                !cimg::strcasecmp(ext,"xvid") ||
57222                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
57223       return save_other(fn);
57224     }
57225 
57226     //! Save image as an ascii file.
57227     /**
57228       \param filename Filename, as a C-string.
57229     **/
57230     const CImg<T>& save_ascii(const char *const filename) const {
57231       return _save_ascii(0,filename);
57232     }
57233 
57234     //! Save image as an Ascii file \overloading.
57235     const CImg<T>& save_ascii(std::FILE *const file) const {
57236       return _save_ascii(file,0);
57237     }
57238 
57239     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
57240       if (!file && !filename)
57241         throw CImgArgumentException(_cimg_instance
57242                                     "save_ascii(): Specified filename is (null).",
57243                                     cimg_instance);
57244       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
57245       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
57246       const T* ptrs = _data;
57247       cimg_forYZC(*this,y,z,c) {
57248         cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
57249         std::fputc('\n',nfile);
57250       }
57251       if (!file) cimg::fclose(nfile);
57252       return *this;
57253     }
57254 
57255     //! Save image as a .cpp source file.
57256     /**
57257       \param filename Filename, as a C-string.
57258     **/
57259     const CImg<T>& save_cpp(const char *const filename) const {
57260       return _save_cpp(0,filename);
57261     }
57262 
57263     //! Save image as a .cpp source file \overloading.
57264     const CImg<T>& save_cpp(std::FILE *const file) const {
57265       return _save_cpp(file,0);
57266     }
57267 
57268     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
57269       if (!file && !filename)
57270         throw CImgArgumentException(_cimg_instance
57271                                     "save_cpp(): Specified filename is (null).",
57272                                     cimg_instance);
57273       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
57274       CImg<charT> varname(1024); *varname = 0;
57275       if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
57276       if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
57277       std::fprintf(nfile,
57278                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
57279                    "%s data_%s[] = { %s\n  ",
57280                    varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
57281                    is_empty()?"};":"");
57282       if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
57283         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
57284         if (off==siz) std::fprintf(nfile," };\n");
57285         else if (!((off + 1)%16)) std::fprintf(nfile,",\n  ");
57286         else std::fprintf(nfile,", ");
57287       }
57288       if (!file) cimg::fclose(nfile);
57289       return *this;
57290     }
57291 
57292     //! Save image as a DLM file.
57293     /**
57294        \param filename Filename, as a C-string.
57295     **/
57296     const CImg<T>& save_dlm(const char *const filename) const {
57297       return _save_dlm(0,filename);
57298     }
57299 
57300     //! Save image as a DLM file \overloading.
57301     const CImg<T>& save_dlm(std::FILE *const file) const {
57302       return _save_dlm(file,0);
57303     }
57304 
57305     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
57306       if (!file && !filename)
57307         throw CImgArgumentException(_cimg_instance
57308                                     "save_dlm(): Specified filename is (null).",
57309                                     cimg_instance);
57310       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57311       if (_depth>1)
57312         cimg::warn(_cimg_instance
57313                    "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
57314                    cimg_instance,
57315                    filename?filename:"(FILE*)");
57316       if (_spectrum>1)
57317         cimg::warn(_cimg_instance
57318                    "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
57319                    cimg_instance,
57320                    filename?filename:"(FILE*)");
57321 
57322       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
57323       const T* ptrs = _data;
57324       cimg_forYZC(*this,y,z,c) {
57325         cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
57326         std::fputc('\n',nfile);
57327       }
57328       if (!file) cimg::fclose(nfile);
57329       return *this;
57330     }
57331 
57332     //! Save image as a BMP file.
57333     /**
57334       \param filename Filename, as a C-string.
57335     **/
57336     const CImg<T>& save_bmp(const char *const filename) const {
57337       return _save_bmp(0,filename);
57338     }
57339 
57340     //! Save image as a BMP file \overloading.
57341     const CImg<T>& save_bmp(std::FILE *const file) const {
57342       return _save_bmp(file,0);
57343     }
57344 
57345     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
57346       if (!file && !filename)
57347         throw CImgArgumentException(_cimg_instance
57348                                     "save_bmp(): Specified filename is (null).",
57349                                     cimg_instance);
57350       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57351       if (_depth>1)
57352         cimg::warn(_cimg_instance
57353                    "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57354                    cimg_instance,
57355                    filename?filename:"(FILE*)");
57356       if (_spectrum>3)
57357         cimg::warn(_cimg_instance
57358                    "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
57359                    cimg_instance,
57360                    filename?filename:"(FILE*)");
57361 
57362       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57363       CImg<ucharT> header(54,1,1,1,0);
57364       unsigned char align_buf[4] = { 0 };
57365       const unsigned int
57366         align = (4 - (3*_width)%4)%4,
57367         buf_size = (3*_width + align)*height(),
57368         file_size = 54 + buf_size;
57369       header[0] = 'B'; header[1] = 'M';
57370       header[0x02] = file_size&0xFF;
57371       header[0x03] = (file_size>>8)&0xFF;
57372       header[0x04] = (file_size>>16)&0xFF;
57373       header[0x05] = (file_size>>24)&0xFF;
57374       header[0x0A] = 0x36;
57375       header[0x0E] = 0x28;
57376       header[0x12] = _width&0xFF;
57377       header[0x13] = (_width>>8)&0xFF;
57378       header[0x14] = (_width>>16)&0xFF;
57379       header[0x15] = (_width>>24)&0xFF;
57380       header[0x16] = _height&0xFF;
57381       header[0x17] = (_height>>8)&0xFF;
57382       header[0x18] = (_height>>16)&0xFF;
57383       header[0x19] = (_height>>24)&0xFF;
57384       header[0x1A] = 1;
57385       header[0x1B] = 0;
57386       header[0x1C] = 24;
57387       header[0x1D] = 0;
57388       header[0x22] = buf_size&0xFF;
57389       header[0x23] = (buf_size>>8)&0xFF;
57390       header[0x24] = (buf_size>>16)&0xFF;
57391       header[0x25] = (buf_size>>24)&0xFF;
57392       header[0x27] = 0x1;
57393       header[0x2B] = 0x1;
57394       cimg::fwrite(header._data,54,nfile);
57395 
57396       const T
57397         *ptr_r = data(0,_height - 1,0,0),
57398         *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
57399         *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
57400 
57401       switch (_spectrum) {
57402       case 1 : {
57403         cimg_forY(*this,y) {
57404           cimg_forX(*this,x) {
57405             const unsigned char val = (unsigned char)*(ptr_r++);
57406             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
57407           }
57408           cimg::fwrite(align_buf,align,nfile);
57409           ptr_r-=2*_width;
57410         }
57411       } break;
57412       case 2 : {
57413         cimg_forY(*this,y) {
57414           cimg_forX(*this,x) {
57415             std::fputc(0,nfile);
57416             std::fputc((unsigned char)(*(ptr_g++)),nfile);
57417             std::fputc((unsigned char)(*(ptr_r++)),nfile);
57418           }
57419           cimg::fwrite(align_buf,align,nfile);
57420           ptr_r-=2*_width; ptr_g-=2*_width;
57421         }
57422       } break;
57423       default : {
57424         cimg_forY(*this,y) {
57425           cimg_forX(*this,x) {
57426             std::fputc((unsigned char)(*(ptr_b++)),nfile);
57427             std::fputc((unsigned char)(*(ptr_g++)),nfile);
57428             std::fputc((unsigned char)(*(ptr_r++)),nfile);
57429           }
57430           cimg::fwrite(align_buf,align,nfile);
57431           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
57432         }
57433       }
57434       }
57435       if (!file) cimg::fclose(nfile);
57436       return *this;
57437     }
57438 
57439     //! Save image as a JPEG file.
57440     /**
57441       \param filename Filename, as a C-string.
57442       \param quality Image quality (in %)
57443     **/
57444     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
57445       return _save_jpeg(0,filename,quality);
57446     }
57447 
57448     //! Save image as a JPEG file \overloading.
57449     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
57450       return _save_jpeg(file,0,quality);
57451     }
57452 
57453     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
57454       if (!file && !filename)
57455         throw CImgArgumentException(_cimg_instance
57456                                     "save_jpeg(): Specified filename is (null).",
57457                                     cimg_instance);
57458       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57459       if (_depth>1)
57460         cimg::warn(_cimg_instance
57461                    "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57462                    cimg_instance,
57463                    filename?filename:"(FILE*)");
57464 
57465 #ifndef cimg_use_jpeg
57466       if (!file) return save_other(filename,quality);
57467       else throw CImgIOException(_cimg_instance
57468                                  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
57469                                  cimg_instance);
57470 #else
57471       unsigned int dimbuf = 0;
57472       J_COLOR_SPACE colortype = JCS_RGB;
57473 
57474       switch (_spectrum) {
57475       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
57476       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
57477       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
57478       default : dimbuf = 4; colortype = JCS_CMYK; break;
57479       }
57480 
57481       // Call libjpeg functions
57482       struct jpeg_compress_struct cinfo;
57483       struct jpeg_error_mgr jerr;
57484       cinfo.err = jpeg_std_error(&jerr);
57485       jpeg_create_compress(&cinfo);
57486       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57487       jpeg_stdio_dest(&cinfo,nfile);
57488       cinfo.image_width = _width;
57489       cinfo.image_height = _height;
57490       cinfo.input_components = dimbuf;
57491       cinfo.in_color_space = colortype;
57492       jpeg_set_defaults(&cinfo);
57493       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
57494       jpeg_start_compress(&cinfo,TRUE);
57495 
57496       JSAMPROW row_pointer[1];
57497       CImg<ucharT> buffer(_width*dimbuf);
57498 
57499       while (cinfo.next_scanline<cinfo.image_height) {
57500         unsigned char *ptrd = buffer._data;
57501 
57502         // Fill pixel buffer
57503         switch (_spectrum) {
57504         case 1 : { // Greyscale images
57505           const T *ptr_g = data(0, cinfo.next_scanline);
57506           for (unsigned int b = 0; b<cinfo.image_width; b++)
57507             *(ptrd++) = (unsigned char)*(ptr_g++);
57508         } break;
57509         case 2 : { // RG images
57510           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
57511             *ptr_g = data(0,cinfo.next_scanline,0,1);
57512           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
57513             *(ptrd++) = (unsigned char)*(ptr_r++);
57514             *(ptrd++) = (unsigned char)*(ptr_g++);
57515             *(ptrd++) = 0;
57516           }
57517         } break;
57518         case 3 : { // RGB images
57519           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
57520             *ptr_g = data(0,cinfo.next_scanline,0,1),
57521             *ptr_b = data(0,cinfo.next_scanline,0,2);
57522           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
57523             *(ptrd++) = (unsigned char)*(ptr_r++);
57524             *(ptrd++) = (unsigned char)*(ptr_g++);
57525             *(ptrd++) = (unsigned char)*(ptr_b++);
57526           }
57527         } break;
57528         default : { // CMYK images
57529           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
57530             *ptr_g = data(0,cinfo.next_scanline,0,1),
57531             *ptr_b = data(0,cinfo.next_scanline,0,2),
57532             *ptr_a = data(0,cinfo.next_scanline,0,3);
57533           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
57534             *(ptrd++) = (unsigned char)*(ptr_r++);
57535             *(ptrd++) = (unsigned char)*(ptr_g++);
57536             *(ptrd++) = (unsigned char)*(ptr_b++);
57537             *(ptrd++) = (unsigned char)*(ptr_a++);
57538           }
57539         }
57540         }
57541         *row_pointer = buffer._data;
57542         jpeg_write_scanlines(&cinfo,row_pointer,1);
57543       }
57544       jpeg_finish_compress(&cinfo);
57545       if (!file) cimg::fclose(nfile);
57546       jpeg_destroy_compress(&cinfo);
57547       return *this;
57548 #endif
57549     }
57550 
57551     //! Save image, using built-in ImageMagick++ library.
57552     /**
57553       \param filename Filename, as a C-string.
57554       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
57555     **/
57556     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
57557       if (!filename)
57558         throw CImgArgumentException(_cimg_instance
57559                                     "save_magick(): Specified filename is (null).",
57560                                     cimg_instance);
57561       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57562 
57563 #ifdef cimg_use_magick
57564       double stmin, stmax = (double)max_min(stmin);
57565       if (_depth>1)
57566         cimg::warn(_cimg_instance
57567                    "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57568                    cimg_instance,
57569                    filename);
57570 
57571       if (_spectrum>3)
57572         cimg::warn(_cimg_instance
57573                    "save_magick(): Instance is multispectral, only the three first channels will be "
57574                    "saved in file '%s'.",
57575                    cimg_instance,
57576                    filename);
57577 
57578       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57579         cimg::warn(_cimg_instance
57580                    "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57581                    cimg_instance,
57582                    filename,stmin,stmax);
57583 
57584       Magick::Image image(Magick::Geometry(_width,_height),"black");
57585       image.type(Magick::TrueColorType);
57586       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
57587       const T
57588         *ptr_r = data(0,0,0,0),
57589         *ptr_g = _spectrum>1?data(0,0,0,1):0,
57590         *ptr_b = _spectrum>2?data(0,0,0,2):0;
57591       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
57592       switch (_spectrum) {
57593       case 1 : // Scalar images
57594         for (ulongT off = (ulongT)_width*_height; off; --off) {
57595           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
57596           ++pixels;
57597         }
57598         break;
57599       case 2 : // RG images
57600         for (ulongT off = (ulongT)_width*_height; off; --off) {
57601           pixels->red = (Magick::Quantum)*(ptr_r++);
57602           pixels->green = (Magick::Quantum)*(ptr_g++);
57603           pixels->blue = 0; ++pixels;
57604         }
57605         break;
57606       default : // RGB images
57607         for (ulongT off = (ulongT)_width*_height; off; --off) {
57608           pixels->red = (Magick::Quantum)*(ptr_r++);
57609           pixels->green = (Magick::Quantum)*(ptr_g++);
57610           pixels->blue = (Magick::Quantum)*(ptr_b++);
57611           ++pixels;
57612         }
57613       }
57614       image.syncPixels();
57615       image.write(filename);
57616       return *this;
57617 #else
57618       cimg::unused(bytes_per_pixel);
57619       throw CImgIOException(_cimg_instance
57620                             "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
57621                             cimg_instance,
57622                             filename);
57623 #endif
57624     }
57625 
57626     //! Save image as a PNG file.
57627     /**
57628        \param filename Filename, as a C-string.
57629        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
57630     **/
57631     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
57632       return _save_png(0,filename,bytes_per_pixel);
57633     }
57634 
57635     //! Save image as a PNG file \overloading.
57636     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
57637       return _save_png(file,0,bytes_per_pixel);
57638     }
57639 
57640     const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
57641                              const unsigned int bytes_per_pixel=0) const {
57642       if (!file && !filename)
57643         throw CImgArgumentException(_cimg_instance
57644                                     "save_png(): Specified filename is (null).",
57645                                     cimg_instance);
57646       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57647 
57648 #ifndef cimg_use_png
57649       cimg::unused(bytes_per_pixel);
57650       if (!file) return save_other(filename);
57651       else throw CImgIOException(_cimg_instance
57652                                  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
57653                                  cimg_instance);
57654 #else
57655 
57656 #if defined __GNUC__
57657       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
57658       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
57659       volatile double stmin, stmax = (double)max_min(stmin);
57660 #else
57661       const char *nfilename = filename;
57662       std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
57663       double stmin, stmax = (double)max_min(stmin);
57664 #endif
57665 
57666       if (_depth>1)
57667         cimg::warn(_cimg_instance
57668                    "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57669                    cimg_instance,
57670                    filename);
57671 
57672       if (_spectrum>4)
57673         cimg::warn(_cimg_instance
57674                    "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
57675                    cimg_instance,
57676                    filename);
57677 
57678       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57679         cimg::warn(_cimg_instance
57680                    "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57681                    cimg_instance,
57682                    filename,stmin,stmax);
57683 
57684       // Setup PNG structures for write
57685       png_voidp user_error_ptr = 0;
57686       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
57687       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
57688                                                     user_warning_fn);
57689       if (!png_ptr){
57690         if (!file) cimg::fclose(nfile);
57691         throw CImgIOException(_cimg_instance
57692                               "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
57693                               cimg_instance,
57694                               nfilename?nfilename:"(FILE*)");
57695       }
57696       png_infop info_ptr = png_create_info_struct(png_ptr);
57697       if (!info_ptr) {
57698         png_destroy_write_struct(&png_ptr,(png_infopp)0);
57699         if (!file) cimg::fclose(nfile);
57700         throw CImgIOException(_cimg_instance
57701                               "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
57702                               cimg_instance,
57703                               nfilename?nfilename:"(FILE*)");
57704       }
57705       if (setjmp(png_jmpbuf(png_ptr))) {
57706         png_destroy_write_struct(&png_ptr, &info_ptr);
57707         if (!file) cimg::fclose(nfile);
57708         throw CImgIOException(_cimg_instance
57709                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
57710                               cimg_instance,
57711                               nfilename?nfilename:"(FILE*)");
57712       }
57713       png_init_io(png_ptr, nfile);
57714 
57715       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
57716 
57717       int color_type;
57718       switch (spectrum()) {
57719       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
57720       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
57721       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
57722       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
57723       }
57724       const int interlace_type = PNG_INTERLACE_NONE;
57725       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
57726       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
57727       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
57728       png_write_info(png_ptr,info_ptr);
57729       const int byte_depth = bit_depth>>3;
57730       const int numChan = spectrum()>4?4:spectrum();
57731       const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
57732 
57733       // Allocate Memory for Image Save and Fill pixel data
57734       png_bytep *const imgData = new png_byte*[_height];
57735       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
57736       const T *pC0 = data(0,0,0,0);
57737       switch (pixel_bit_depth_flag) {
57738       case 7 :  { // Gray 8-bit
57739         cimg_forY(*this,y) {
57740           unsigned char *ptrd = imgData[y];
57741           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
57742         }
57743       } break;
57744       case 14 : { // Gray w/ Alpha 8-bit
57745         const T *pC1 = data(0,0,0,1);
57746         cimg_forY(*this,y) {
57747           unsigned char *ptrd = imgData[y];
57748           cimg_forX(*this,x) {
57749             *(ptrd++) = (unsigned char)*(pC0++);
57750             *(ptrd++) = (unsigned char)*(pC1++);
57751           }
57752         }
57753       } break;
57754       case 21 :  { // RGB 8-bit
57755         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
57756         cimg_forY(*this,y) {
57757           unsigned char *ptrd = imgData[y];
57758           cimg_forX(*this,x) {
57759             *(ptrd++) = (unsigned char)*(pC0++);
57760             *(ptrd++) = (unsigned char)*(pC1++);
57761             *(ptrd++) = (unsigned char)*(pC2++);
57762           }
57763         }
57764       } break;
57765       case 28 : { // RGB x/ Alpha 8-bit
57766         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
57767         cimg_forY(*this,y){
57768           unsigned char *ptrd = imgData[y];
57769           cimg_forX(*this,x){
57770             *(ptrd++) = (unsigned char)*(pC0++);
57771             *(ptrd++) = (unsigned char)*(pC1++);
57772             *(ptrd++) = (unsigned char)*(pC2++);
57773             *(ptrd++) = (unsigned char)*(pC3++);
57774           }
57775         }
57776       } break;
57777       case 15 : { // Gray 16-bit
57778         cimg_forY(*this,y){
57779           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57780           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
57781           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
57782         }
57783       } break;
57784       case 30 : { // Gray w/ Alpha 16-bit
57785         const T *pC1 = data(0,0,0,1);
57786         cimg_forY(*this,y){
57787           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57788           cimg_forX(*this,x) {
57789             *(ptrd++) = (unsigned short)*(pC0++);
57790             *(ptrd++) = (unsigned short)*(pC1++);
57791           }
57792           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
57793         }
57794       } break;
57795       case 45 : { // RGB 16-bit
57796         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
57797         cimg_forY(*this,y) {
57798           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57799           cimg_forX(*this,x) {
57800             *(ptrd++) = (unsigned short)*(pC0++);
57801             *(ptrd++) = (unsigned short)*(pC1++);
57802             *(ptrd++) = (unsigned short)*(pC2++);
57803           }
57804           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
57805         }
57806       } break;
57807       case 60 : { // RGB w/ Alpha 16-bit
57808         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
57809         cimg_forY(*this,y) {
57810           unsigned short *ptrd = (unsigned short*)(imgData[y]);
57811           cimg_forX(*this,x) {
57812             *(ptrd++) = (unsigned short)*(pC0++);
57813             *(ptrd++) = (unsigned short)*(pC1++);
57814             *(ptrd++) = (unsigned short)*(pC2++);
57815             *(ptrd++) = (unsigned short)*(pC3++);
57816           }
57817           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
57818         }
57819       } break;
57820       default :
57821         if (!file) cimg::fclose(nfile);
57822         throw CImgIOException(_cimg_instance
57823                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
57824                               cimg_instance,
57825                               nfilename?nfilename:"(FILE*)");
57826       }
57827       png_write_image(png_ptr,imgData);
57828       png_write_end(png_ptr,info_ptr);
57829       png_destroy_write_struct(&png_ptr, &info_ptr);
57830 
57831       // Deallocate Image Write Memory
57832       cimg_forY(*this,n) delete[] imgData[n];
57833       delete[] imgData;
57834 
57835       if (!file) cimg::fclose(nfile);
57836       return *this;
57837 #endif
57838     }
57839 
57840     //! Save image as a PNM file.
57841     /**
57842       \param filename Filename, as a C-string.
57843       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
57844     **/
57845     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
57846       return _save_pnm(0,filename,bytes_per_pixel);
57847     }
57848 
57849     //! Save image as a PNM file \overloading.
57850     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
57851       return _save_pnm(file,0,bytes_per_pixel);
57852     }
57853 
57854     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
57855                              const unsigned int bytes_per_pixel=0) const {
57856       if (!file && !filename)
57857         throw CImgArgumentException(_cimg_instance
57858                                     "save_pnm(): Specified filename is (null).",
57859                                     cimg_instance);
57860       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57861 
57862       double stmin, stmax = (double)max_min(stmin);
57863       if (_depth>1)
57864         cimg::warn(_cimg_instance
57865                    "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57866                    cimg_instance,
57867                    filename?filename:"(FILE*)");
57868       if (_spectrum>3)
57869         cimg::warn(_cimg_instance
57870                    "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
57871                    cimg_instance,
57872                    filename?filename:"(FILE*)");
57873       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
57874         cimg::warn(_cimg_instance
57875                    "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
57876                    cimg_instance,
57877                    stmin,stmax,filename?filename:"(FILE*)");
57878 
57879       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57880       const T
57881         *ptr_r = data(0,0,0,0),
57882         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
57883         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
57884       const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
57885 
57886       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
57887                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
57888 
57889       switch (_spectrum) {
57890       case 1 : { // Scalar image
57891         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
57892           CImg<ucharT> buf((unsigned int)buf_size);
57893           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57894             const ulongT N = std::min((ulongT)to_write,buf_size);
57895             unsigned char *ptrd = buf._data;
57896             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
57897             cimg::fwrite(buf._data,N,nfile);
57898             to_write-=N;
57899           }
57900         } else { // Binary PGM 16 bits
57901           CImg<ushortT> buf((unsigned int)buf_size);
57902           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57903             const ulongT N = std::min((ulongT)to_write,buf_size);
57904             unsigned short *ptrd = buf._data;
57905             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
57906             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57907             cimg::fwrite(buf._data,N,nfile);
57908             to_write-=N;
57909           }
57910         }
57911       } break;
57912       case 2 : { // RG image
57913         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
57914           CImg<ucharT> buf((unsigned int)buf_size);
57915           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57916             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57917             unsigned char *ptrd = buf._data;
57918             for (ulongT i = N; i>0; --i) {
57919               *(ptrd++) = (unsigned char)*(ptr_r++);
57920               *(ptrd++) = (unsigned char)*(ptr_g++);
57921               *(ptrd++) = 0;
57922             }
57923             cimg::fwrite(buf._data,3*N,nfile);
57924             to_write-=N;
57925           }
57926         } else {             // Binary PPM 16 bits
57927           CImg<ushortT> buf((unsigned int)buf_size);
57928           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57929             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57930             unsigned short *ptrd = buf._data;
57931             for (ulongT i = N; i>0; --i) {
57932               *(ptrd++) = (unsigned short)*(ptr_r++);
57933               *(ptrd++) = (unsigned short)*(ptr_g++);
57934               *(ptrd++) = 0;
57935             }
57936             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57937             cimg::fwrite(buf._data,3*N,nfile);
57938             to_write-=N;
57939           }
57940         }
57941       } break;
57942       default : { // RGB image
57943         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
57944           CImg<ucharT> buf((unsigned int)buf_size);
57945           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57946             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57947             unsigned char *ptrd = buf._data;
57948             for (ulongT i = N; i>0; --i) {
57949               *(ptrd++) = (unsigned char)*(ptr_r++);
57950               *(ptrd++) = (unsigned char)*(ptr_g++);
57951               *(ptrd++) = (unsigned char)*(ptr_b++);
57952             }
57953             cimg::fwrite(buf._data,3*N,nfile);
57954             to_write-=N;
57955           }
57956         } else { // Binary PPM 16 bits
57957           CImg<ushortT> buf((unsigned int)buf_size);
57958           for (longT to_write = (longT)width()*height(); to_write>0; ) {
57959             const ulongT N = std::min((ulongT)to_write,buf_size/3);
57960             unsigned short *ptrd = buf._data;
57961             for (ulongT i = N; i>0; --i) {
57962               *(ptrd++) = (unsigned short)*(ptr_r++);
57963               *(ptrd++) = (unsigned short)*(ptr_g++);
57964               *(ptrd++) = (unsigned short)*(ptr_b++);
57965             }
57966             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
57967             cimg::fwrite(buf._data,3*N,nfile);
57968             to_write-=N;
57969           }
57970         }
57971       }
57972       }
57973       if (!file) cimg::fclose(nfile);
57974       return *this;
57975     }
57976 
57977     //! Save image as a PNK file.
57978     /**
57979       \param filename Filename, as a C-string.
57980     **/
57981     const CImg<T>& save_pnk(const char *const filename) const {
57982       return _save_pnk(0,filename);
57983     }
57984 
57985     //! Save image as a PNK file \overloading.
57986     const CImg<T>& save_pnk(std::FILE *const file) const {
57987       return _save_pnk(file,0);
57988     }
57989 
57990     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
57991       if (!file && !filename)
57992         throw CImgArgumentException(_cimg_instance
57993                                     "save_pnk(): Specified filename is (null).",
57994                                     cimg_instance);
57995       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57996       if (_spectrum>1)
57997         cimg::warn(_cimg_instance
57998                    "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
57999                    cimg_instance,
58000                    filename?filename:"(FILE*)");
58001 
58002       const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
58003       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58004       const T *ptr = data(0,0,0,0);
58005 
58006       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file
58007         _save_pnm(file,filename,0);
58008       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D
58009         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
58010         CImg<ucharT> buf((unsigned int)buf_size);
58011         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
58012           const ulongT N = std::min((ulongT)to_write,buf_size);
58013           unsigned char *ptrd = buf._data;
58014           for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
58015           cimg::fwrite(buf._data,N,nfile);
58016           to_write-=N;
58017         }
58018       } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D
58019         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
58020         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
58021         CImg<intT> buf((unsigned int)buf_size);
58022         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
58023           const ulongT N = std::min((ulongT)to_write,buf_size);
58024           int *ptrd = buf._data;
58025           for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
58026           cimg::fwrite(buf._data,N,nfile);
58027           to_write-=N;
58028         }
58029       } else { // Save as P9: Binary float-valued 3D
58030         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
58031         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
58032         CImg<floatT> buf((unsigned int)buf_size);
58033         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
58034           const ulongT N = std::min((ulongT)to_write,buf_size);
58035           float *ptrd = buf._data;
58036           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
58037           cimg::fwrite(buf._data,N,nfile);
58038           to_write-=N;
58039         }
58040       }
58041 
58042       if (!file) cimg::fclose(nfile);
58043       return *this;
58044     }
58045 
58046     //! Save image as a PFM file.
58047     /**
58048       \param filename Filename, as a C-string.
58049     **/
58050     const CImg<T>& save_pfm(const char *const filename) const {
58051       get_mirror('y')._save_pfm(0,filename);
58052       return *this;
58053     }
58054 
58055     //! Save image as a PFM file \overloading.
58056     const CImg<T>& save_pfm(std::FILE *const file) const {
58057       get_mirror('y')._save_pfm(file,0);
58058       return *this;
58059     }
58060 
58061     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
58062       if (!file && !filename)
58063         throw CImgArgumentException(_cimg_instance
58064                                     "save_pfm(): Specified filename is (null).",
58065                                     cimg_instance);
58066       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58067       if (_depth>1)
58068         cimg::warn(_cimg_instance
58069                    "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
58070                    cimg_instance,
58071                    filename?filename:"(FILE*)");
58072       if (_spectrum>3)
58073         cimg::warn(_cimg_instance
58074                    "save_pfm(): image instance is multispectral, only the three first channels will be saved "
58075                    "in file '%s'.",
58076                    cimg_instance,
58077                    filename?filename:"(FILE*)");
58078 
58079       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58080       const T
58081         *ptr_r = data(0,0,0,0),
58082         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
58083         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
58084       const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
58085 
58086       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
58087                    (_spectrum==1?'f':'F'),_width,_height);
58088 
58089       switch (_spectrum) {
58090       case 1 : { // Scalar image
58091         CImg<floatT> buf(buf_size);
58092         for (longT to_write = (longT)width()*height(); to_write>0; ) {
58093           const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
58094           float *ptrd = buf._data;
58095           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
58096           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
58097           cimg::fwrite(buf._data,N,nfile);
58098           to_write-=N;
58099         }
58100       } break;
58101       case 2 : { // RG image
58102         CImg<floatT> buf(buf_size);
58103         for (longT to_write = (longT)width()*height(); to_write>0; ) {
58104           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
58105           float *ptrd = buf._data;
58106           for (ulongT i = N; i>0; --i) {
58107             *(ptrd++) = (float)*(ptr_r++);
58108             *(ptrd++) = (float)*(ptr_g++);
58109             *(ptrd++) = 0;
58110           }
58111           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
58112           cimg::fwrite(buf._data,3*N,nfile);
58113           to_write-=N;
58114         }
58115       } break;
58116       default : { // RGB image
58117         CImg<floatT> buf(buf_size);
58118         for (longT to_write = (longT)width()*height(); to_write>0; ) {
58119           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
58120           float *ptrd = buf._data;
58121           for (ulongT i = N; i>0; --i) {
58122             *(ptrd++) = (float)*(ptr_r++);
58123             *(ptrd++) = (float)*(ptr_g++);
58124             *(ptrd++) = (float)*(ptr_b++);
58125           }
58126           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
58127           cimg::fwrite(buf._data,3*N,nfile);
58128           to_write-=N;
58129         }
58130       }
58131       }
58132       if (!file) cimg::fclose(nfile);
58133       return *this;
58134     }
58135 
58136     //! Save image as a RGB file.
58137     /**
58138       \param filename Filename, as a C-string.
58139     **/
58140     const CImg<T>& save_rgb(const char *const filename) const {
58141       return _save_rgb(0,filename);
58142     }
58143 
58144     //! Save image as a RGB file \overloading.
58145     const CImg<T>& save_rgb(std::FILE *const file) const {
58146       return _save_rgb(file,0);
58147     }
58148 
58149     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
58150       if (!file && !filename)
58151         throw CImgArgumentException(_cimg_instance
58152                                     "save_rgb(): Specified filename is (null).",
58153                                     cimg_instance);
58154       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58155       if (_spectrum!=3)
58156         cimg::warn(_cimg_instance
58157                    "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
58158                    cimg_instance,
58159                    filename?filename:"(FILE*)");
58160 
58161       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58162       const ulongT wh = (ulongT)_width*_height;
58163       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
58164       const T
58165         *ptr1 = data(0,0,0,0),
58166         *ptr2 = _spectrum>1?data(0,0,0,1):0,
58167         *ptr3 = _spectrum>2?data(0,0,0,2):0;
58168       switch (_spectrum) {
58169       case 1 : { // Scalar image
58170         for (ulongT k = 0; k<wh; ++k) {
58171           const unsigned char val = (unsigned char)*(ptr1++);
58172           *(nbuffer++) = val;
58173           *(nbuffer++) = val;
58174           *(nbuffer++) = val;
58175         }
58176       } break;
58177       case 2 : { // RG image
58178         for (ulongT k = 0; k<wh; ++k) {
58179           *(nbuffer++) = (unsigned char)(*(ptr1++));
58180           *(nbuffer++) = (unsigned char)(*(ptr2++));
58181           *(nbuffer++) = 0;
58182         }
58183       } break;
58184       default : { // RGB image
58185         for (ulongT k = 0; k<wh; ++k) {
58186           *(nbuffer++) = (unsigned char)(*(ptr1++));
58187           *(nbuffer++) = (unsigned char)(*(ptr2++));
58188           *(nbuffer++) = (unsigned char)(*(ptr3++));
58189         }
58190       }
58191       }
58192       cimg::fwrite(buffer,3*wh,nfile);
58193       if (!file) cimg::fclose(nfile);
58194       delete[] buffer;
58195       return *this;
58196     }
58197 
58198     //! Save image as a RGBA file.
58199     /**
58200        \param filename Filename, as a C-string.
58201     **/
58202     const CImg<T>& save_rgba(const char *const filename) const {
58203       return _save_rgba(0,filename);
58204     }
58205 
58206     //! Save image as a RGBA file \overloading.
58207     const CImg<T>& save_rgba(std::FILE *const file) const {
58208       return _save_rgba(file,0);
58209     }
58210 
58211     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
58212       if (!file && !filename)
58213         throw CImgArgumentException(_cimg_instance
58214                                     "save_rgba(): Specified filename is (null).",
58215                                     cimg_instance);
58216       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58217       if (_spectrum!=4)
58218         cimg::warn(_cimg_instance
58219                    "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
58220                    cimg_instance,
58221                    filename?filename:"(FILE*)");
58222 
58223       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58224       const ulongT wh = (ulongT)_width*_height;
58225       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
58226       const T
58227         *ptr1 = data(0,0,0,0),
58228         *ptr2 = _spectrum>1?data(0,0,0,1):0,
58229         *ptr3 = _spectrum>2?data(0,0,0,2):0,
58230         *ptr4 = _spectrum>3?data(0,0,0,3):0;
58231       switch (_spectrum) {
58232       case 1 : { // Scalar images
58233         for (ulongT k = 0; k<wh; ++k) {
58234           const unsigned char val = (unsigned char)*(ptr1++);
58235           *(nbuffer++) = val;
58236           *(nbuffer++) = val;
58237           *(nbuffer++) = val;
58238           *(nbuffer++) = 255;
58239         }
58240       } break;
58241       case 2 : { // RG images
58242         for (ulongT k = 0; k<wh; ++k) {
58243           *(nbuffer++) = (unsigned char)(*(ptr1++));
58244           *(nbuffer++) = (unsigned char)(*(ptr2++));
58245           *(nbuffer++) = 0;
58246           *(nbuffer++) = 255;
58247         }
58248       } break;
58249       case 3 : { // RGB images
58250         for (ulongT k = 0; k<wh; ++k) {
58251           *(nbuffer++) = (unsigned char)(*(ptr1++));
58252           *(nbuffer++) = (unsigned char)(*(ptr2++));
58253           *(nbuffer++) = (unsigned char)(*(ptr3++));
58254           *(nbuffer++) = 255;
58255         }
58256       } break;
58257       default : { // RGBA images
58258         for (ulongT k = 0; k<wh; ++k) {
58259           *(nbuffer++) = (unsigned char)(*(ptr1++));
58260           *(nbuffer++) = (unsigned char)(*(ptr2++));
58261           *(nbuffer++) = (unsigned char)(*(ptr3++));
58262           *(nbuffer++) = (unsigned char)(*(ptr4++));
58263         }
58264       }
58265       }
58266       cimg::fwrite(buffer,4*wh,nfile);
58267       if (!file) cimg::fclose(nfile);
58268       delete[] buffer;
58269       return *this;
58270     }
58271 
58272     //! Save image as a TIFF file.
58273     /**
58274        \param filename Filename, as a C-string.
58275        \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
58276        \param[out] voxel_size Voxel size, to be stored in the filename.
58277        \param[out] description Description, to be stored in the filename.
58278        \param use_bigtiff Allow to save big tiff files (>4Gb).
58279        \note
58280        - libtiff support is enabled by defining the precompilation
58281         directive \c cimg_use_tif.
58282        - When libtiff is enabled, 2D and 3D (multipage) several
58283         channel per pixel are supported for
58284         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
58285        - If \c cimg_use_tiff is not defined at compile time the
58286         function uses CImg<T>&save_other(const char*).
58287      **/
58288     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
58289 
58290                              const float *const voxel_size=0, const char *const description=0,
58291                              const bool use_bigtiff=true) const {
58292       if (!filename)
58293         throw CImgArgumentException(_cimg_instance
58294                                     "save_tiff(): Specified filename is (null).",
58295                                     cimg_instance);
58296       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58297 
58298 #ifdef cimg_use_tiff
58299       const bool
58300         _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images
58301       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
58302       if (tif) {
58303         cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
58304         TIFFClose(tif);
58305       } else throw CImgIOException(_cimg_instance
58306                                    "save_tiff(): Failed to open file '%s' for writing.",
58307                                    cimg_instance,
58308                                    filename);
58309       return *this;
58310 #else
58311       cimg::unused(compression_type,voxel_size,description,use_bigtiff);
58312       return save_other(filename);
58313 #endif
58314     }
58315 
58316 #ifdef cimg_use_tiff
58317 
58318 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
58319       const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
58320 
58321     // [internal] Save a plane into a tiff file
58322     template<typename t>
58323     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
58324                               const unsigned int compression_type, const float *const voxel_size,
58325                               const char *const description) const {
58326       if (is_empty() || !tif || pixel_t) return *this;
58327       const char *const filename = TIFFFileName(tif);
58328       uint32_t rowsperstrip = (uint32_t)-1;
58329       uint16_t spp = _spectrum, bpp = sizeof(t)*8, photometric;
58330       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
58331       else photometric = PHOTOMETRIC_MINISBLACK;
58332       TIFFSetDirectory(tif,directory);
58333       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
58334       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
58335       if (voxel_size) {
58336         const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
58337         TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
58338         TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx);
58339         TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy);
58340         CImg<charT> s_description(256);
58341         cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
58342         TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
58343       }
58344       if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
58345       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
58346       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
58347       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
58348       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
58349       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
58350       double valm, valM = max_min(valm);
58351       TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
58352       TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
58353       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
58354       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
58355       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
58356       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
58357                    compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
58358       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
58359       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
58360       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
58361       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
58362 
58363       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
58364       if (buf) {
58365         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
58366           uint32_t nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
58367           tstrip_t strip = TIFFComputeStrip(tif,row,0);
58368           tsize_t i = 0;
58369           for (unsigned int rr = 0; rr<nrow; ++rr)
58370             for (unsigned int cc = 0; cc<_width; ++cc)
58371               for (unsigned int vv = 0; vv<spp; ++vv)
58372                 buf[i++] = (t)(*this)(cc,row + rr,z,vv);
58373           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
58374             throw CImgIOException(_cimg_instance
58375                                   "save_tiff(): Invalid strip writing when saving file '%s'.",
58376                                   cimg_instance,
58377                                   filename?filename:"(FILE*)");
58378         }
58379         _TIFFfree(buf);
58380       }
58381       TIFFWriteDirectory(tif);
58382       return *this;
58383     }
58384 
58385     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
58386                               const unsigned int compression_type, const float *const voxel_size,
58387                               const char *const description) const {
58388       _cimg_save_tiff("bool",unsigned char,compression_type);
58389       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
58390       _cimg_save_tiff("char",char,compression_type);
58391       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
58392       _cimg_save_tiff("short",short,compression_type);
58393       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
58394       _cimg_save_tiff("int",int,compression_type);
58395       _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
58396       _cimg_save_tiff("int64",int,compression_type);
58397       _cimg_save_tiff("float",float,compression_type);
58398       _cimg_save_tiff("double",float,compression_type);
58399       const char *const filename = TIFFFileName(tif);
58400       throw CImgInstanceException(_cimg_instance
58401                                   "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
58402                                   cimg_instance,
58403                                   pixel_type(),filename?filename:"(FILE*)");
58404       return *this;
58405     }
58406 #endif
58407 
58408     //! Save image as a MINC2 file.
58409     /**
58410        \param filename Filename, as a C-string.
58411        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
58412     **/
58413     const CImg<T>& save_minc2(const char *const filename,
58414                               const char *const imitate_file=0) const {
58415       if (!filename)
58416         throw CImgArgumentException(_cimg_instance
58417                                    "save_minc2(): Specified filename is (null).",
58418                                    cimg_instance);
58419       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58420 
58421 #ifndef cimg_use_minc2
58422      cimg::unused(imitate_file);
58423      return save_other(filename);
58424 #else
58425      minc::minc_1_writer wtr;
58426      if (imitate_file)
58427        wtr.open(filename, imitate_file);
58428      else {
58429        minc::minc_info di;
58430        if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
58431        if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
58432        if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
58433        if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
58434        wtr.open(filename,di,1,NC_FLOAT,0);
58435      }
58436      if (pixel_type()==cimg::type<unsigned char>::string())
58437        wtr.setup_write_byte();
58438      else if (pixel_type()==cimg::type<int>::string())
58439        wtr.setup_write_int();
58440      else if (pixel_type()==cimg::type<double>::string())
58441        wtr.setup_write_double();
58442      else
58443        wtr.setup_write_float();
58444      minc::save_standard_volume(wtr, this->_data);
58445      return *this;
58446 #endif
58447     }
58448 
58449     //! Save image as an ANALYZE7.5 or NIFTI file.
58450     /**
58451       \param filename Filename, as a C-string.
58452       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
58453     **/
58454     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
58455       if (!filename)
58456         throw CImgArgumentException(_cimg_instance
58457                                     "save_analyze(): Specified filename is (null).",
58458                                     cimg_instance);
58459       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58460 
58461       std::FILE *file;
58462       CImg<charT> hname(1024), iname(1024);
58463       const char *const ext = cimg::split_filename(filename);
58464       short datatype = -1;
58465       if (!*ext) {
58466         cimg_snprintf(hname,hname._width,"%s.hdr",filename);
58467         cimg_snprintf(iname,iname._width,"%s.img",filename);
58468       }
58469       if (!cimg::strncasecmp(ext,"hdr",3)) {
58470         std::strcpy(hname,filename);
58471         std::strncpy(iname,filename,iname._width - 1);
58472         cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
58473       }
58474       if (!cimg::strncasecmp(ext,"img",3)) {
58475         std::strcpy(hname,filename);
58476         std::strncpy(iname,filename,iname._width - 1);
58477         cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
58478       }
58479       if (!cimg::strncasecmp(ext,"nii",3)) {
58480         std::strncpy(hname,filename,hname._width - 1); *iname = 0;
58481       }
58482 
58483       CImg<charT> header(*iname?348:352,1,1,1,0);
58484       int *const iheader = (int*)header._data;
58485       *iheader = 348;
58486       std::strcpy(header._data + 4,"CImg");
58487       std::strcpy(header._data + 14," ");
58488       ((short*)&(header[36]))[0] = 4096;
58489       ((char*)&(header[38]))[0] = 114;
58490       ((short*)&(header[40]))[0] = 4;
58491       ((short*)&(header[40]))[1] = (short)_width;
58492       ((short*)&(header[40]))[2] = (short)_height;
58493       ((short*)&(header[40]))[3] = (short)_depth;
58494       ((short*)&(header[40]))[4] = (short)_spectrum;
58495       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
58496       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
58497       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
58498       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
58499       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
58500       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
58501       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
58502       if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
58503       if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
58504       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
58505       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
58506       if (datatype<0)
58507         throw CImgIOException(_cimg_instance
58508                               "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
58509                               cimg_instance,
58510                               pixel_type(),filename);
58511 
58512       ((short*)&(header[70]))[0] = datatype;
58513       ((short*)&(header[72]))[0] = sizeof(T);
58514       ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
58515       ((float*)&(header[112]))[0] = 1;
58516       ((float*)&(header[76]))[0] = 0;
58517       if (voxel_size) {
58518         ((float*)&(header[76]))[1] = voxel_size[0];
58519         ((float*)&(header[76]))[2] = voxel_size[1];
58520         ((float*)&(header[76]))[3] = voxel_size[2];
58521       } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
58522       file = cimg::fopen(hname,"wb");
58523       cimg::fwrite(header._data,header.width(),file);
58524       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
58525       cimg::fwrite(_data,size(),file);
58526       cimg::fclose(file);
58527       return *this;
58528     }
58529 
58530     //! Save image as a .cimg file.
58531     /**
58532       \param filename Filename, as a C-string.
58533       \param is_compressed Tells if the file contains compressed image data.
58534     **/
58535     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
58536       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
58537       return *this;
58538     }
58539 
58540     //! Save image as a .cimg file \overloading.
58541     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
58542       CImgList<T>(*this,true).save_cimg(file,is_compressed);
58543       return *this;
58544     }
58545 
58546     //! Save image as a sub-image into an existing .cimg file.
58547     /**
58548       \param filename Filename, as a C-string.
58549       \param n0 Index of the image inside the file.
58550       \param x0 X-coordinate of the sub-image location.
58551       \param y0 Y-coordinate of the sub-image location.
58552       \param z0 Z-coordinate of the sub-image location.
58553       \param c0 C-coordinate of the sub-image location.
58554     **/
58555     const CImg<T>& save_cimg(const char *const filename,
58556                              const unsigned int n0,
58557                              const unsigned int x0, const unsigned int y0,
58558                              const unsigned int z0, const unsigned int c0) const {
58559       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
58560       return *this;
58561     }
58562 
58563     //! Save image as a sub-image into an existing .cimg file \overloading.
58564     const CImg<T>& save_cimg(std::FILE *const file,
58565                              const unsigned int n0,
58566                              const unsigned int x0, const unsigned int y0,
58567                              const unsigned int z0, const unsigned int c0) const {
58568       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
58569       return *this;
58570     }
58571 
58572     //! Save blank image as a .cimg file.
58573     /**
58574         \param filename Filename, as a C-string.
58575         \param dx Width of the image.
58576         \param dy Height of the image.
58577         \param dz Depth of the image.
58578         \param dc Number of channels of the image.
58579         \note
58580         - All pixel values of the saved image are set to \c 0.
58581         - Use this method to save large images without having to instantiate and allocate them.
58582     **/
58583     static void save_empty_cimg(const char *const filename,
58584                                 const unsigned int dx, const unsigned int dy=1,
58585                                 const unsigned int dz=1, const unsigned int dc=1) {
58586       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
58587     }
58588 
58589     //! Save blank image as a .cimg file \overloading.
58590     /**
58591        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
58592        with a file stream argument instead of a filename string.
58593     **/
58594     static void save_empty_cimg(std::FILE *const file,
58595                                 const unsigned int dx, const unsigned int dy=1,
58596                                 const unsigned int dz=1, const unsigned int dc=1) {
58597       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
58598     }
58599 
58600     //! Save image as an INRIMAGE-4 file.
58601     /**
58602       \param filename Filename, as a C-string.
58603       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
58604     **/
58605     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
58606       return _save_inr(0,filename,voxel_size);
58607     }
58608 
58609     //! Save image as an INRIMAGE-4 file \overloading.
58610     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
58611       return _save_inr(file,0,voxel_size);
58612     }
58613 
58614     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
58615       if (!file && !filename)
58616         throw CImgArgumentException(_cimg_instance
58617                                     "save_inr(): Specified filename is (null).",
58618                                     cimg_instance);
58619       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58620 
58621       int inrpixsize = -1;
58622       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
58623       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
58624         inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
58625       }
58626       if (!cimg::strcasecmp(pixel_type(),"char")) {
58627         inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
58628       }
58629       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
58630         inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
58631       }
58632       if (!cimg::strcasecmp(pixel_type(),"short")) {
58633         inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
58634       }
58635       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
58636         inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
58637       }
58638       if (!cimg::strcasecmp(pixel_type(),"int")) {
58639         inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
58640       }
58641       if (!cimg::strcasecmp(pixel_type(),"float")) {
58642         inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
58643       }
58644       if (!cimg::strcasecmp(pixel_type(),"double")) {
58645         inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
58646       }
58647       if (inrpixsize<=0)
58648         throw CImgIOException(_cimg_instance
58649                               "save_inr(): Unsupported pixel type '%s' for file '%s'",
58650                               cimg_instance,
58651                               pixel_type(),filename?filename:"(FILE*)");
58652 
58653       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58654       CImg<charT> header(257);
58655       int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
58656                               _width,_height,_depth,_spectrum);
58657       if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
58658                                         voxel_size[0],voxel_size[1],voxel_size[2]);
58659       err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
58660       std::memset(header._data + err,'\n',252 - err);
58661       std::memcpy(header._data + 252,"##}\n",4);
58662       cimg::fwrite(header._data,256,nfile);
58663       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
58664       if (!file) cimg::fclose(nfile);
58665       return *this;
58666     }
58667 
58668     //! Save image as an OpenEXR file.
58669     /**
58670        \param filename Filename, as a C-string.
58671        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
58672     **/
58673     const CImg<T>& save_exr(const char *const filename) const {
58674       if (!filename)
58675         throw CImgArgumentException(_cimg_instance
58676                                     "save_exr(): Specified filename is (null).",
58677                                     cimg_instance);
58678       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58679       if (_depth>1)
58680         cimg::warn(_cimg_instance
58681                    "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
58682                    cimg_instance,
58683                    filename);
58684 
58685 #ifndef cimg_use_openexr
58686       return save_other(filename);
58687 #else
58688       Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
58689       switch (_spectrum) {
58690       case 1 : { // Grayscale image
58691         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58692           rgba.r = (half)(*(ptr_r));
58693           rgba.g = (half)(*(ptr_r));
58694           rgba.b = (half)(*(ptr_r++));
58695           rgba.a = (half)1;
58696           *(ptrd++) = rgba;
58697         }
58698       } break;
58699       case 2 : { // RG image
58700         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
58701                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
58702           rgba.r = (half)(*(ptr_r++));
58703           rgba.g = (half)(*(ptr_g++));
58704           rgba.b = (half)0;
58705           rgba.a = (half)1;
58706           *(ptrd++) = rgba;
58707         }
58708       } break;
58709       case 3 : { // RGB image
58710         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
58711                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58712           rgba.r = (half)(*(ptr_r++));
58713           rgba.g = (half)(*(ptr_g++));
58714           rgba.b = (half)(*(ptr_b++));
58715           rgba.a = (half)1;
58716           *(ptrd++) = rgba;
58717         }
58718       } break;
58719       default : { // RGBA image
58720         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),
58721                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
58722           rgba.r = (half)(*(ptr_r++));
58723           rgba.g = (half)(*(ptr_g++));
58724           rgba.b = (half)(*(ptr_b++));
58725           rgba.a = (half)(*(ptr_a++));
58726           *(ptrd++) = rgba;
58727         }
58728       } break;
58729       }
58730       Imf::RgbaOutputFile outFile(filename,_width,_height,
58731                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
58732                                   Imf::WRITE_RGB:Imf::WRITE_RGBA);
58733       outFile.setFrameBuffer(ptrd0,1,_width);
58734       outFile.writePixels(_height);
58735       delete[] ptrd0;
58736       return *this;
58737 #endif
58738     }
58739 
58740     //! Save image as a Pandore-5 file.
58741     /**
58742        \param filename Filename, as a C-string.
58743        \param colorspace Colorspace data field in output file
58744        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
58745        for more information).
58746     **/
58747     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
58748       return _save_pandore(0,filename,colorspace);
58749     }
58750 
58751     //! Save image as a Pandore-5 file \overloading.
58752     /**
58753         Same as save_pandore(const char *,unsigned int) const
58754         with a file stream argument instead of a filename string.
58755     **/
58756     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
58757       return _save_pandore(file,0,colorspace);
58758     }
58759 
58760     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
58761       unsigned int nbdims = 0;
58762       if (id==2 || id==3 || id==4) {
58763         dims[0] = 1; dims[1] = _width; nbdims = 2;
58764       }
58765       if (id==5 || id==6 || id==7) {
58766         dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
58767       }
58768       if (id==8 || id==9 || id==10) {
58769         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
58770       }
58771       if (id==16 || id==17 || id==18) {
58772         dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
58773       }
58774       if (id==19 || id==20 || id==21) {
58775         dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
58776       }
58777       if (id==22 || id==23 || id==25) {
58778         dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
58779       }
58780       if (id==26 || id==27 || id==29) {
58781         dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
58782       }
58783       if (id==30 || id==31 || id==33) {
58784         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
58785       }
58786       return nbdims;
58787     }
58788 
58789     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
58790                                  const unsigned int colorspace) const {
58791 
58792 #define __cimg_save_pandore_case(dtype) \
58793        dtype *buffer = new dtype[size()]; \
58794        const T *ptrs = _data; \
58795        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
58796        buffer-=size(); \
58797        cimg::fwrite(buffer,size(),nfile); \
58798        delete[] buffer
58799 
58800 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
58801       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
58802           (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
58803         unsigned int *iheader = (unsigned int*)(header + 12); \
58804         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
58805         cimg::fwrite(header,36,nfile); \
58806         if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
58807           for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \
58808           cimg::fwrite(ndims._data,nbdims,nfile); } \
58809         else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
58810           for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \
58811           cimg::fwrite(ndims._data,nbdims,nfile); } \
58812         else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
58813           for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \
58814           cimg::fwrite(ndims._data,nbdims,nfile); } \
58815         else throw CImgIOException(_cimg_instance \
58816                                    "save_pandore(): Unsupported datatype for file '%s'.",\
58817                                    cimg_instance, \
58818                                    filename?filename:"(FILE*)"); \
58819         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
58820           __cimg_save_pandore_case(unsigned char); \
58821         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
58822           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
58823           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
58824           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
58825           else throw CImgIOException(_cimg_instance \
58826                                      "save_pandore(): Unsupported datatype for file '%s'.",\
58827                                      cimg_instance, \
58828                                      filename?filename:"(FILE*)"); \
58829         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
58830           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
58831           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
58832           else throw CImgIOException(_cimg_instance \
58833                                      "save_pandore(): Unsupported datatype for file '%s'.",\
58834                                      cimg_instance, \
58835                                      filename?filename:"(FILE*)"); \
58836         } \
58837         saved = true; \
58838       }
58839 
58840       if (!file && !filename)
58841         throw CImgArgumentException(_cimg_instance
58842                                     "save_pandore(): Specified filename is (null).",
58843                                     cimg_instance);
58844       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58845 
58846       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58847       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
58848                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,
58849                                    'N','o',' ','d','a','t','e',0,0,0,0 };
58850       unsigned int nbdims, dims[5] = { 0 };
58851       bool saved = false;
58852       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
58853       _cimg_save_pandore_case(1,1,1,"char",3);
58854       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
58855       _cimg_save_pandore_case(1,1,1,"short",3);
58856       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
58857       _cimg_save_pandore_case(1,1,1,"int",3);
58858       _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
58859       _cimg_save_pandore_case(1,1,1,"int64",3);
58860       _cimg_save_pandore_case(1,1,1,"float",4);
58861       _cimg_save_pandore_case(1,1,1,"double",4);
58862 
58863       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
58864       _cimg_save_pandore_case(0,1,1,"char",6);
58865       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
58866       _cimg_save_pandore_case(0,1,1,"short",6);
58867       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
58868       _cimg_save_pandore_case(0,1,1,"int",6);
58869       _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
58870       _cimg_save_pandore_case(0,1,1,"int64",6);
58871       _cimg_save_pandore_case(0,1,1,"float",7);
58872       _cimg_save_pandore_case(0,1,1,"double",7);
58873 
58874       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
58875       _cimg_save_pandore_case(0,0,1,"char",9);
58876       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
58877       _cimg_save_pandore_case(0,0,1,"short",9);
58878       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
58879       _cimg_save_pandore_case(0,0,1,"int",9);
58880       _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
58881       _cimg_save_pandore_case(0,0,1,"int64",9);
58882       _cimg_save_pandore_case(0,0,1,"float",10);
58883       _cimg_save_pandore_case(0,0,1,"double",10);
58884 
58885       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
58886       _cimg_save_pandore_case(0,1,3,"char",17);
58887       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
58888       _cimg_save_pandore_case(0,1,3,"short",17);
58889       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
58890       _cimg_save_pandore_case(0,1,3,"int",17);
58891       _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
58892       _cimg_save_pandore_case(0,1,3,"int64",17);
58893       _cimg_save_pandore_case(0,1,3,"float",18);
58894       _cimg_save_pandore_case(0,1,3,"double",18);
58895 
58896       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
58897       _cimg_save_pandore_case(0,0,3,"char",20);
58898       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
58899       _cimg_save_pandore_case(0,0,3,"short",20);
58900       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
58901       _cimg_save_pandore_case(0,0,3,"int",20);
58902       _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
58903       _cimg_save_pandore_case(0,0,3,"int64",20);
58904       _cimg_save_pandore_case(0,0,3,"float",21);
58905       _cimg_save_pandore_case(0,0,3,"double",21);
58906 
58907       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
58908       _cimg_save_pandore_case(1,1,0,"char",23);
58909       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
58910       _cimg_save_pandore_case(1,1,0,"short",23);
58911       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
58912       _cimg_save_pandore_case(1,1,0,"int",23);
58913       _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
58914       _cimg_save_pandore_case(1,1,0,"int64",23);
58915       _cimg_save_pandore_case(1,1,0,"float",25);
58916       _cimg_save_pandore_case(1,1,0,"double",25);
58917 
58918       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
58919       _cimg_save_pandore_case(0,1,0,"char",27);
58920       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
58921       _cimg_save_pandore_case(0,1,0,"short",27);
58922       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
58923       _cimg_save_pandore_case(0,1,0,"int",27);
58924       _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
58925       _cimg_save_pandore_case(0,1,0,"int64",27);
58926       _cimg_save_pandore_case(0,1,0,"float",29);
58927       _cimg_save_pandore_case(0,1,0,"double",29);
58928 
58929       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
58930       _cimg_save_pandore_case(0,0,0,"char",31);
58931       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
58932       _cimg_save_pandore_case(0,0,0,"short",31);
58933       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
58934       _cimg_save_pandore_case(0,0,0,"int",31);
58935       _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
58936       _cimg_save_pandore_case(0,0,0,"int64",31);
58937       _cimg_save_pandore_case(0,0,0,"float",33);
58938       _cimg_save_pandore_case(0,0,0,"double",33);
58939 
58940       if (!file) cimg::fclose(nfile);
58941       return *this;
58942     }
58943 
58944     //! Save image as a raw data file.
58945     /**
58946        \param filename Filename, as a C-string.
58947        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
58948        \note The .raw format does not store the image dimensions in the output file,
58949        so you have to keep track of them somewhere to be able to read the file correctly afterwards.
58950     **/
58951     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
58952       return _save_raw(0,filename,is_multiplexed);
58953     }
58954 
58955     //! Save image as a raw data file \overloading.
58956     /**
58957        Same as save_raw(const char *,bool) const
58958        with a file stream argument instead of a filename string.
58959     **/
58960     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
58961       return _save_raw(file,0,is_multiplexed);
58962     }
58963 
58964     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
58965       if (!file && !filename)
58966         throw CImgArgumentException(_cimg_instance
58967                                     "save_raw(): Specified filename is (null).",
58968                                     cimg_instance);
58969       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58970 
58971       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58972       if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
58973         ulongT siz;
58974         const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
58975         cimg::fwrite(buf,siz,nfile);
58976         delete[] buf;
58977       } else { // Non boolean data
58978         if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
58979         else { // Multiplexed
58980           CImg<T> buf(_spectrum);
58981           cimg_forXYZ(*this,x,y,z) {
58982             cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
58983             cimg::fwrite(buf._data,_spectrum,nfile);
58984           }
58985         }
58986       }
58987       if (!file) cimg::fclose(nfile);
58988       return *this;
58989     }
58990 
58991     // Return unsigned char buffer that encodes data of a CImg<bool> instance bitwise.
58992     // (buffer needs to be deallocated afterwards, with delete[]).
58993     const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const {
58994       const ulongT _siz = size();
58995       siz = _siz/8 + (_siz%8?1:0);
58996       unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0;
58997 
58998       if (!is_multiplexed || _spectrum==1) // Non-multiplexed
58999         cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }}
59000       else // Multiplexed
59001         cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) {
59002           (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }
59003         }
59004       if (bit) *ptrd = val;
59005       return buf;
59006     }
59007 
59008     // Fill CImg<T> instance from bitwise data encoded in an unsigned char buffer.
59009     void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) {
59010       const ulongT S = std::min(siz*8,size());
59011       const unsigned char *ptrs = buf;
59012       unsigned char val = 0, mask = 0;
59013       T *ptrd = _data;
59014       if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed
59015         for (ulongT off = 0; off<S; ++off) {
59016           if (!(mask>>=1)) { val = *(ptrs++); mask = 128; }
59017           *(ptrd++) = (T)((val&mask)?1:0);
59018         }
59019       else if (S) { // Multiplexed
59020         ulongT off = 0;
59021         for (int z = 0; z<depth() && off<=S; ++z)
59022           for (int y = 0; y<height() && off<=S; ++y)
59023             for (int x = 0; x<width() && off<=S; ++x)
59024               for (int c = 0; c<spectrum() && off<=S; ++c) {
59025                 if (!(mask>>=1)) { val = *(ptrs++); ++off; mask = 128; }
59026                 (*this)(x,y,z,c) = (T)((val&mask)?1:0);
59027               }
59028       }
59029     }
59030 
59031     //! Save image as a .yuv video file.
59032     /**
59033        \param filename Filename, as a C-string.
59034        \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
59035        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
59036        \note Each slice of the instance image is considered to be a single frame of the output video file.
59037     **/
59038     const CImg<T>& save_yuv(const char *const filename,
59039                             const unsigned int chroma_subsampling=444,
59040                             const bool is_rgb=true) const {
59041       CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
59042       return *this;
59043     }
59044 
59045     //! Save image as a .yuv video file \overloading.
59046     /**
59047        Same as save_yuv(const char*,const unsigned int,const bool) const
59048        with a file stream argument instead of a filename string.
59049     **/
59050     const CImg<T>& save_yuv(std::FILE *const file,
59051                             const unsigned int chroma_subsampling=444,
59052                             const bool is_rgb=true) const {
59053       CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
59054       return *this;
59055     }
59056 
59057     //! Save 3D object as an Object File Format (.off) file.
59058     /**
59059        \param filename Filename, as a C-string.
59060        \param primitives List of 3D object primitives.
59061        \param colors List of 3D object colors.
59062        \note
59063        - Instance image contains the vertices data of the 3D object.
59064        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
59065        Such primitives will be lost or simplified during file saving.
59066        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
59067     **/
59068     template<typename tf, typename tc>
59069     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
59070                             const char *const filename) const {
59071       return _save_off(primitives,colors,0,filename);
59072     }
59073 
59074     //! Save 3D object as an Object File Format (.off) file \overloading.
59075     /**
59076        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
59077        with a file stream argument instead of a filename string.
59078     **/
59079     template<typename tf, typename tc>
59080     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
59081                             std::FILE *const file) const {
59082       return _save_off(primitives,colors,file,0);
59083     }
59084 
59085     template<typename tf, typename tc>
59086     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
59087                              std::FILE *const file, const char *const filename) const {
59088       if (!file && !filename)
59089         throw CImgArgumentException(_cimg_instance
59090                                     "save_off(): Specified filename is (null).",
59091                                     cimg_instance);
59092       if (is_empty())
59093         throw CImgInstanceException(_cimg_instance
59094                                     "save_off(): Empty instance, for file '%s'.",
59095                                     cimg_instance,
59096                                     filename?filename:"(FILE*)");
59097 
59098       CImgList<T> opacities;
59099       CImg<charT> error_message(1024);
59100       if (!is_object3d(primitives,colors,opacities,true,error_message))
59101         throw CImgInstanceException(_cimg_instance
59102                                     "save_off(): Invalid specified 3D object, for file '%s' (%s).",
59103                                     cimg_instance,
59104                                     filename?filename:"(FILE*)",error_message.data());
59105 
59106       const CImg<tc> default_color(1,3,1,1,(tc)std::min((int)cimg::type<tc>::max(),200));
59107       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
59108       unsigned int supported_primitives = 0;
59109       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
59110       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
59111       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
59112                                       (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
59113       cimglist_for(primitives,l) {
59114         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
59115         const unsigned int psiz = primitives[l].size(), csiz = color.size();
59116         const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f;
59117         switch (psiz) {
59118         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
59119                               (unsigned int)primitives(l,0),r,g,b); break;
59120         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
59121                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
59122         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
59123                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
59124                               (unsigned int)primitives(l,1),r,g,b); break;
59125         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
59126                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
59127                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
59128         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
59129                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
59130         case 6 : {
59131           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
59132           const float
59133             rt = color.atXY(xt,yt,0)/255.f,
59134             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
59135             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
59136           std::fprintf(nfile,"2 %u %u %f %f %f\n",
59137                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
59138         } break;
59139         case 9 : {
59140           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
59141           const float
59142             rt = color.atXY(xt,yt,0)/255.f,
59143             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
59144             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
59145           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
59146                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
59147                        (unsigned int)primitives(l,1),rt,gt,bt);
59148         } break;
59149         case 12 : {
59150           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
59151           const float
59152             rt = color.atXY(xt,yt,0)/255.f,
59153             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
59154             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
59155           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
59156                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
59157                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
59158         } break;
59159         }
59160       }
59161       if (!file) cimg::fclose(nfile);
59162       return *this;
59163     }
59164 
59165     //! Save volumetric image as a video (using the OpenCV library when available).
59166     /**
59167       \param filename Filename to write data to.
59168       \param fps Number of frames per second.
59169       \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
59170       \param keep_open Tells if the video writer associated to the specified filename
59171         must be kept open or not (to allow frames to be added in the same file afterwards).
59172     **/
59173     const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
59174                               const char *codec=0, const bool keep_open=false) const {
59175       if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
59176       CImgList<T> list;
59177       get_split('z').move_to(list);
59178       list.save_video(filename,fps,codec,keep_open);
59179       return *this;
59180     }
59181 
59182     //! Save volumetric image as a video, using ffmpeg external binary.
59183     /**
59184        \param filename Filename, as a C-string.
59185        \param fps Video framerate.
59186        \param codec Video codec, as a C-string.
59187        \param bitrate Video bitrate.
59188        \note
59189        - Each slice of the instance image is considered to be a single frame of the output video file.
59190        - This method uses \c ffmpeg, an external executable binary provided by
59191          <a href="http://www.ffmpeg.org">FFmpeg</a>.
59192        It must be installed for the method to succeed.
59193     **/
59194     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
59195                                         const char *const codec=0, const unsigned int bitrate=2048) const {
59196       if (!filename)
59197         throw CImgArgumentException(_cimg_instance
59198                                     "save_ffmpeg_external(): Specified filename is (null).",
59199                                     cimg_instance);
59200       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59201 
59202       CImgList<T> list;
59203       get_split('z').move_to(list);
59204       list.save_ffmpeg_external(filename,fps,codec,bitrate);
59205       return *this;
59206     }
59207 
59208     //! Save image using gzip external binary.
59209     /**
59210        \param filename Filename, as a C-string.
59211        \note This method uses \c gzip, an external executable binary provided by
59212          <a href="//http://www.gzip.org">gzip</a>.
59213        It must be installed for the method to succeed.
59214     **/
59215     const CImg<T>& save_gzip_external(const char *const filename) const {
59216       if (!filename)
59217         throw CImgArgumentException(_cimg_instance
59218                                     "save_gzip_external(): Specified filename is (null).",
59219                                     cimg_instance);
59220       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59221 
59222       CImg<charT> command(1024), filename_tmp(256), body(256);
59223       const char
59224         *ext = cimg::split_filename(filename,body),
59225         *ext2 = cimg::split_filename(body,0);
59226       std::FILE *file;
59227       do {
59228         if (!cimg::strcasecmp(ext,"gz")) {
59229           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59230                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
59231           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59232                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59233         } else {
59234           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59235                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
59236           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59237                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59238         }
59239         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59240       } while (file);
59241       save(filename_tmp);
59242       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
59243                     cimg::gzip_path(),
59244                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
59245                     CImg<charT>::string(filename)._system_strescape().data());
59246       cimg::system(command, cimg::gzip_path());
59247       file = cimg::std_fopen(filename,"rb");
59248       if (!file)
59249         throw CImgIOException(_cimg_instance
59250                               "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
59251                               cimg_instance,
59252                               filename);
59253 
59254       else cimg::fclose(file);
59255       std::remove(filename_tmp);
59256       return *this;
59257     }
59258 
59259     //! Save image using GraphicsMagick's external binary.
59260     /**
59261        \param filename Filename, as a C-string.
59262        \param quality Image quality (expressed in percent), when the file format supports it.
59263        \note This method uses \c gm, an external executable binary provided by
59264          <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
59265        It must be installed for the method to succeed.
59266     **/
59267     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
59268       if (!filename)
59269         throw CImgArgumentException(_cimg_instance
59270                                     "save_graphicsmagick_external(): Specified filename is (null).",
59271                                     cimg_instance);
59272       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59273       if (_depth>1)
59274         cimg::warn(_cimg_instance
59275                    "save_other(): File '%s', saving a volumetric image with an external call to "
59276                    "GraphicsMagick only writes the first image slice.",
59277                    cimg_instance,filename);
59278 
59279 #ifdef cimg_use_png
59280 #define _cimg_sge_extension1 "png"
59281 #define _cimg_sge_extension2 "png"
59282 #else
59283 #define _cimg_sge_extension1 "pgm"
59284 #define _cimg_sge_extension2 "ppm"
59285 #endif
59286       CImg<charT> command(1024), filename_tmp(256);
59287       std::FILE *file;
59288       do {
59289         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59290                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
59291                       _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2);
59292         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59293       } while (file);
59294 
59295 #ifdef cimg_use_png
59296       save_png(filename_tmp);
59297 #else
59298       save_pnm(filename_tmp);
59299 #endif
59300       cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"",
59301                     cimg::graphicsmagick_path(),quality,
59302                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
59303                     CImg<charT>::string(filename)._system_strescape().data());
59304       cimg::system(command, cimg::graphicsmagick_path());
59305       file = cimg::std_fopen(filename,"rb");
59306       if (!file)
59307         throw CImgIOException(_cimg_instance
59308                               "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
59309                               cimg_instance,
59310                               filename);
59311 
59312       if (file) cimg::fclose(file);
59313       std::remove(filename_tmp);
59314       return *this;
59315     }
59316 
59317     //! Save image using ImageMagick's external binary.
59318     /**
59319        \param filename Filename, as a C-string.
59320        \param quality Image quality (expressed in percent), when the file format supports it.
59321        \note This method uses \c convert, an external executable binary provided by
59322        <a href="http://www.imagemagick.org">ImageMagick</a>.
59323        It must be installed for the method to succeed.
59324     **/
59325     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
59326       if (!filename)
59327         throw CImgArgumentException(_cimg_instance
59328                                     "save_imagemagick_external(): Specified filename is (null).",
59329                                     cimg_instance);
59330       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59331       if (_depth>1)
59332         cimg::warn(_cimg_instance
59333                    "save_other(): File '%s', saving a volumetric image with an external call to "
59334                    "ImageMagick only writes the first image slice.",
59335                    cimg_instance,filename);
59336 #ifdef cimg_use_png
59337 #define _cimg_sie_extension1 "png"
59338 #define _cimg_sie_extension2 "png"
59339 #else
59340 #define _cimg_sie_extension1 "pgm"
59341 #define _cimg_sie_extension2 "ppm"
59342 #endif
59343       CImg<charT> command(1024), filename_tmp(256);
59344       std::FILE *file;
59345       do {
59346         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
59347                       cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2);
59348         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59349       } while (file);
59350 #ifdef cimg_use_png
59351       save_png(filename_tmp);
59352 #else
59353       save_pnm(filename_tmp);
59354 #endif
59355       cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"",
59356                     cimg::imagemagick_path(),quality,
59357                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
59358                     CImg<charT>::string(filename)._system_strescape().data());
59359       cimg::system(command, cimg::imagemagick_path());
59360       file = cimg::std_fopen(filename,"rb");
59361       if (!file)
59362         throw CImgIOException(_cimg_instance
59363                               "save_imagemagick_external(): Failed to save file '%s' with "
59364                               "external command 'magick/convert'.",
59365                               cimg_instance,
59366                               filename);
59367 
59368       if (file) cimg::fclose(file);
59369       std::remove(filename_tmp);
59370       return *this;
59371     }
59372 
59373     //! Save image as a Dicom file.
59374     /**
59375        \param filename Filename, as a C-string.
59376        \note This method uses \c medcon, an external executable binary provided by
59377          <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
59378        It must be installed for the method to succeed.
59379     **/
59380     const CImg<T>& save_medcon_external(const char *const filename) const {
59381       if (!filename)
59382         throw CImgArgumentException(_cimg_instance
59383                                     "save_medcon_external(): Specified filename is (null).",
59384                                     cimg_instance);
59385       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59386 
59387       CImg<charT> command(1024), filename_tmp(256), body(256);
59388       std::FILE *file;
59389       do {
59390         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
59391         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59392       } while (file);
59393       save_analyze(filename_tmp);
59394       cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"",
59395                     cimg::medcon_path(),
59396                     CImg<charT>::string(filename)._system_strescape().data(),
59397                     CImg<charT>::string(filename_tmp)._system_strescape().data());
59398       cimg::system(command, cimg::medcon_path());
59399       std::remove(filename_tmp);
59400       cimg::split_filename(filename_tmp,body);
59401       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
59402       std::remove(filename_tmp);
59403 
59404       file = cimg::std_fopen(filename,"rb");
59405       if (!file) {
59406         cimg_snprintf(command,command._width,"m000-%s",filename);
59407         file = cimg::std_fopen(command,"rb");
59408         if (!file) {
59409           cimg::fclose(cimg::fopen(filename,"r"));
59410           throw CImgIOException(_cimg_instance
59411                                 "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
59412                                 cimg_instance,
59413                                 filename);
59414         }
59415       }
59416       cimg::fclose(file);
59417       std::rename(command,filename);
59418       return *this;
59419     }
59420 
59421     // Save image for non natively supported formats.
59422     /**
59423        \param filename Filename, as a C-string.
59424        \param quality Image quality (expressed in percent), when the file format supports it.
59425        \note
59426        - The filename extension tells about the desired file format.
59427        - This method tries to save the instance image as a file, using external tools from
59428        <a href="http://www.imagemagick.org">ImageMagick</a> or
59429        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
59430          At least one of these tool must be installed for the method to succeed.
59431        - It is recommended to use the generic method save(const char*, int) const instead,
59432          as it can handle some file formats natively.
59433     **/
59434     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
59435       if (!filename)
59436         throw CImgArgumentException(_cimg_instance
59437                                     "save_other(): Specified filename is (null).",
59438                                     cimg_instance);
59439       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59440       if (_depth>1)
59441         cimg::warn(_cimg_instance
59442                    "save_other(): File '%s', saving a volumetric image with an external call to "
59443                    "ImageMagick or GraphicsMagick only writes the first image slice.",
59444                    cimg_instance,filename);
59445 
59446       const unsigned int omode = cimg::exception_mode();
59447       bool is_saved = true;
59448       cimg::exception_mode(0);
59449       try { save_magick(filename); }
59450       catch (CImgException&) {
59451         try { save_imagemagick_external(filename,quality); }
59452         catch (CImgException&) {
59453           try { save_graphicsmagick_external(filename,quality); }
59454           catch (CImgException&) {
59455             is_saved = false;
59456           }
59457         }
59458       }
59459       cimg::exception_mode(omode);
59460       if (!is_saved)
59461         throw CImgIOException(_cimg_instance
59462                               "save_other(): Failed to save file '%s'. Format is not natively supported, "
59463                               "and no external commands succeeded.",
59464                               cimg_instance,
59465                               filename);
59466       return *this;
59467     }
59468 
59469     //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
59470     /**
59471        \param is_compressed tells if zlib compression must be used for serialization
59472        (this requires 'cimg_use_zlib' been enabled).
59473     **/
59474     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
59475       return CImgList<T>(*this,true).get_serialize(is_compressed);
59476     }
59477 
59478     // [internal] Return a 40x38 color logo of a 'danger' item.
59479     static CImg<T> _logo40x38() {
59480       CImg<T> res(40,38,1,3);
59481       const unsigned char *ptrs = cimg::logo40x38;
59482       T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
59483       for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
59484         const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
59485         for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
59486       }
59487       return res;
59488     }
59489 
59490     //@}
59491   }; // struct CImg { ...
59492 
59493   /*
59494    #-----------------------------------------
59495    #
59496    #
59497    #
59498    # Definition of the CImgList<T> structure
59499    #
59500    #
59501    #
59502    #------------------------------------------
59503    */
59504   //! Represent a list of images CImg<T>.
59505   template<typename T>
59506   struct CImgList {
59507     unsigned int _width, _allocated_width;
59508     CImg<T> *_data;
59509 
59510     //! Simple iterator type, to loop through each image of a list.
59511     /**
59512        \note
59513        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
59514        - You may use it like this:
59515        \code
59516        CImgList<> list;   // Assuming this image list is not empty
59517        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
59518        \endcode
59519        - Using the loop macro \c cimglist_for is another (more concise) alternative:
59520        \code
59521        cimglist_for(list,l) list[l].mirror('x');
59522        \endcode
59523     **/
59524     typedef CImg<T>* iterator;
59525 
59526     //! Simple const iterator type, to loop through each image of a \c const list instance.
59527     /**
59528        \note
59529        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
59530        - Similar to CImgList<T>::iterator, but for constant list instances.
59531     **/
59532     typedef const CImg<T>* const_iterator;
59533 
59534     //! Pixel value type.
59535     /**
59536        Refer to the pixels value type of the images in the list.
59537        \note
59538        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
59539          It is then similar to CImg<T>::value_type.
59540        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
59541          compatibility with STL naming conventions.
59542     **/
59543     typedef T value_type;
59544 
59545     // Define common types related to template type T.
59546     typedef typename cimg::superset<T,bool>::type Tbool;
59547     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
59548     typedef typename cimg::superset<T,char>::type Tchar;
59549     typedef typename cimg::superset<T,unsigned short>::type Tushort;
59550     typedef typename cimg::superset<T,short>::type Tshort;
59551     typedef typename cimg::superset<T,unsigned int>::type Tuint;
59552     typedef typename cimg::superset<T,int>::type Tint;
59553     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
59554     typedef typename cimg::superset<T,cimg_long>::type Tlong;
59555     typedef typename cimg::superset<T,float>::type Tfloat;
59556     typedef typename cimg::superset<T,double>::type Tdouble;
59557     typedef typename cimg::last<T,bool>::type boolT;
59558     typedef typename cimg::last<T,unsigned char>::type ucharT;
59559     typedef typename cimg::last<T,char>::type charT;
59560     typedef typename cimg::last<T,unsigned short>::type ushortT;
59561     typedef typename cimg::last<T,short>::type shortT;
59562     typedef typename cimg::last<T,unsigned int>::type uintT;
59563     typedef typename cimg::last<T,int>::type intT;
59564     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
59565     typedef typename cimg::last<T,cimg_long>::type longT;
59566     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
59567     typedef typename cimg::last<T,cimg_int64>::type int64T;
59568     typedef typename cimg::last<T,float>::type floatT;
59569     typedef typename cimg::last<T,double>::type doubleT;
59570 
59571     //@}
59572     //---------------------------
59573     //
59574     //! \name Plugins
59575     //@{
59576     //---------------------------
59577 #ifdef cimglist_plugin
59578 #include cimglist_plugin
59579 #endif
59580 #ifdef cimglist_plugin1
59581 #include cimglist_plugin1
59582 #endif
59583 #ifdef cimglist_plugin2
59584 #include cimglist_plugin2
59585 #endif
59586 #ifdef cimglist_plugin3
59587 #include cimglist_plugin3
59588 #endif
59589 #ifdef cimglist_plugin4
59590 #include cimglist_plugin4
59591 #endif
59592 #ifdef cimglist_plugin5
59593 #include cimglist_plugin5
59594 #endif
59595 #ifdef cimglist_plugin6
59596 #include cimglist_plugin6
59597 #endif
59598 #ifdef cimglist_plugin7
59599 #include cimglist_plugin7
59600 #endif
59601 #ifdef cimglist_plugin8
59602 #include cimglist_plugin8
59603 #endif
59604 
59605     //@}
59606     //--------------------------------------------------------
59607     //
59608     //! \name Constructors / Destructor / Instance Management
59609     //@{
59610     //--------------------------------------------------------
59611 
59612     //! Destructor.
59613     /**
59614        Destroy current list instance.
59615        \note
59616        - Any allocated buffer is deallocated.
59617        - Destroying an empty list does nothing actually.
59618      **/
59619     ~CImgList() {
59620       delete[] _data;
59621     }
59622 
59623     //! Default constructor.
59624     /**
59625        Construct a new empty list instance.
59626        \note
59627        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
59628          image buffer pointer data().
59629        - An empty list may be reassigned afterwards, with the family of the assign() methods.
59630          In all cases, the type of pixels stays \c T.
59631      **/
59632     CImgList():
59633       _width(0),_allocated_width(0),_data(0) {}
59634 
59635     //! Construct list containing empty images.
59636     /**
59637        \param n Number of empty images.
59638        \note Useful when you know by advance the number of images you want to manage, as
59639        it will allocate the right amount of memory for the list, without needs for reallocation
59640        (that may occur when starting from an empty list and inserting several images in it).
59641     **/
59642     explicit CImgList(const unsigned int n):_width(n) {
59643       if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
59644       else { _allocated_width = 0; _data = 0; }
59645     }
59646 
59647     //! Construct list containing images of specified size.
59648     /**
59649        \param n Number of images.
59650        \param width Width of images.
59651        \param height Height of images.
59652        \param depth Depth of images.
59653        \param spectrum Number of channels of images.
59654        \note Pixel values are not initialized and may probably contain garbage.
59655     **/
59656     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
59657              const unsigned int depth=1, const unsigned int spectrum=1):
59658       _width(0),_allocated_width(0),_data(0) {
59659       assign(n);
59660       cimglist_apply(*this,assign)(width,height,depth,spectrum);
59661     }
59662 
59663     //! Construct list containing images of specified size, and initialize pixel values.
59664     /**
59665        \param n Number of images.
59666        \param width Width of images.
59667        \param height Height of images.
59668        \param depth Depth of images.
59669        \param spectrum Number of channels of images.
59670        \param val Initialization value for images pixels.
59671     **/
59672     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59673              const unsigned int depth, const unsigned int spectrum, const T& val):
59674       _width(0),_allocated_width(0),_data(0) {
59675       assign(n);
59676       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
59677     }
59678 
59679     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
59680     /**
59681        \param n Number of images.
59682        \param width Width of images.
59683        \param height Height of images.
59684        \param depth Depth of images.
59685        \param spectrum Number of channels of images.
59686        \param val0 First value of the initializing integers sequence.
59687        \param val1 Second value of the initializing integers sequence.
59688        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
59689          or you will probably segfault.
59690     **/
59691     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59692              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
59693       _width(0),_allocated_width(0),_data(0) {
59694 #define _CImgList_stdarg(t) { \
59695         assign(n,width,height,depth,spectrum); \
59696         const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
59697         T *ptrd = _data->_data; \
59698         va_list ap; \
59699         va_start(ap,val1); \
59700         for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
59701           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
59702           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
59703         } \
59704         va_end(ap); \
59705       }
59706       _CImgList_stdarg(int);
59707     }
59708 
59709     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
59710     /**
59711        \param n Number of images.
59712        \param width Width of images.
59713        \param height Height of images.
59714        \param depth Depth of images.
59715        \param spectrum Number of channels of images.
59716        \param val0 First value of the initializing doubles sequence.
59717        \param val1 Second value of the initializing doubles sequence.
59718        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
59719          or you will probably segfault.
59720     **/
59721     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
59722              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
59723       _width(0),_allocated_width(0),_data(0) {
59724       _CImgList_stdarg(double);
59725     }
59726 
59727     //! Construct list containing copies of an input image.
59728     /**
59729        \param n Number of images.
59730        \param img Input image to copy in the constructed list.
59731        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
59732     **/
59733     template<typename t>
59734     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
59735       _width(0),_allocated_width(0),_data(0) {
59736       assign(n);
59737       cimglist_apply(*this,assign)(img,is_shared);
59738     }
59739 
59740     //! Construct list from one image.
59741     /**
59742        \param img Input image to copy in the constructed list.
59743        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
59744      **/
59745     template<typename t>
59746     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
59747       _width(0),_allocated_width(0),_data(0) {
59748       assign(1);
59749       _data[0].assign(img,is_shared);
59750     }
59751 
59752     //! Construct list from two images.
59753     /**
59754        \param img1 First input image to copy in the constructed list.
59755        \param img2 Second input image to copy in the constructed list.
59756        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59757      **/
59758     template<typename t1, typename t2>
59759     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
59760       _width(0),_allocated_width(0),_data(0) {
59761       assign(2);
59762       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
59763     }
59764 
59765     //! Construct list from three images.
59766     /**
59767        \param img1 First input image to copy in the constructed list.
59768        \param img2 Second input image to copy in the constructed list.
59769        \param img3 Third input image to copy in the constructed list.
59770        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59771     **/
59772     template<typename t1, typename t2, typename t3>
59773     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
59774       _width(0),_allocated_width(0),_data(0) {
59775       assign(3);
59776       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59777     }
59778 
59779     //! Construct list from four images.
59780     /**
59781        \param img1 First input image to copy in the constructed list.
59782        \param img2 Second input image to copy in the constructed list.
59783        \param img3 Third input image to copy in the constructed list.
59784        \param img4 Fourth input image to copy in the constructed list.
59785        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59786     **/
59787     template<typename t1, typename t2, typename t3, typename t4>
59788     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59789              const bool is_shared=false):
59790       _width(0),_allocated_width(0),_data(0) {
59791       assign(4);
59792       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59793       _data[3].assign(img4,is_shared);
59794     }
59795 
59796     //! Construct list from five images.
59797     /**
59798        \param img1 First input image to copy in the constructed list.
59799        \param img2 Second input image to copy in the constructed list.
59800        \param img3 Third input image to copy in the constructed list.
59801        \param img4 Fourth input image to copy in the constructed list.
59802        \param img5 Fifth input image to copy in the constructed list.
59803        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59804     **/
59805     template<typename t1, typename t2, typename t3, typename t4, typename t5>
59806     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59807              const CImg<t5>& img5, const bool is_shared=false):
59808       _width(0),_allocated_width(0),_data(0) {
59809       assign(5);
59810       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59811       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
59812     }
59813 
59814     //! Construct list from six images.
59815     /**
59816        \param img1 First input image to copy in the constructed list.
59817        \param img2 Second input image to copy in the constructed list.
59818        \param img3 Third input image to copy in the constructed list.
59819        \param img4 Fourth input image to copy in the constructed list.
59820        \param img5 Fifth input image to copy in the constructed list.
59821        \param img6 Sixth input image to copy in the constructed list.
59822        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59823     **/
59824     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
59825     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59826              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
59827       _width(0),_allocated_width(0),_data(0) {
59828       assign(6);
59829       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59830       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59831     }
59832 
59833     //! Construct list from seven images.
59834     /**
59835        \param img1 First input image to copy in the constructed list.
59836        \param img2 Second input image to copy in the constructed list.
59837        \param img3 Third input image to copy in the constructed list.
59838        \param img4 Fourth input image to copy in the constructed list.
59839        \param img5 Fifth input image to copy in the constructed list.
59840        \param img6 Sixth input image to copy in the constructed list.
59841        \param img7 Seventh input image to copy in the constructed list.
59842        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59843     **/
59844     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
59845     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59846              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
59847       _width(0),_allocated_width(0),_data(0) {
59848       assign(7);
59849       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59850       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59851       _data[6].assign(img7,is_shared);
59852     }
59853 
59854     //! Construct list from eight images.
59855     /**
59856        \param img1 First input image to copy in the constructed list.
59857        \param img2 Second input image to copy in the constructed list.
59858        \param img3 Third input image to copy in the constructed list.
59859        \param img4 Fourth input image to copy in the constructed list.
59860        \param img5 Fifth input image to copy in the constructed list.
59861        \param img6 Sixth input image to copy in the constructed list.
59862        \param img7 Seventh input image to copy in the constructed list.
59863        \param img8 Eighth input image to copy in the constructed list.
59864        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59865     **/
59866     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
59867     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
59868              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
59869              const bool is_shared=false):
59870       _width(0),_allocated_width(0),_data(0) {
59871       assign(8);
59872       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
59873       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
59874       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
59875     }
59876 
59877     //! Construct list copy.
59878     /**
59879        \param list Input list to copy.
59880        \note The shared state of each element of the constructed list is kept the same as in \c list.
59881     **/
59882     template<typename t>
59883     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
59884       assign(list._width);
59885       cimglist_for(*this,l) _data[l].assign(list[l],false);
59886     }
59887 
59888     //! Construct list copy \specialization.
59889     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
59890       assign(list._width);
59891       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
59892     }
59893 
59894     //! Construct list copy, and force the shared state of the list elements.
59895     /**
59896        \param list Input list to copy.
59897        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
59898     **/
59899     template<typename t>
59900     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
59901       assign(list._width);
59902       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
59903     }
59904 
59905     //! Construct list by reading the content of a file.
59906     /**
59907        \param filename Filename, as a C-string.
59908     **/
59909     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
59910       assign(filename);
59911     }
59912 
59913     //! Construct list from the content of a display window.
59914     /**
59915        \param disp Display window to get content from.
59916        \note Constructed list contains a single image only.
59917     **/
59918     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
59919       assign(disp);
59920     }
59921 
59922     //! Return a list with elements being shared copies of images in the list instance.
59923     /**
59924       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
59925     **/
59926     CImgList<T> get_shared() {
59927       CImgList<T> res(_width);
59928       cimglist_for(*this,l) res[l].assign(_data[l],true);
59929       return res;
59930     }
59931 
59932     //! Return a list with elements being shared copies of images in the list instance \const.
59933     const CImgList<T> get_shared() const {
59934       CImgList<T> res(_width);
59935       cimglist_for(*this,l) res[l].assign(_data[l],true);
59936       return res;
59937     }
59938 
59939     //! Destructor \inplace.
59940     /**
59941        \see CImgList().
59942     **/
59943     CImgList<T>& assign() {
59944       delete[] _data;
59945       _width = _allocated_width = 0;
59946       _data = 0;
59947       return *this;
59948     }
59949 
59950     //! Destructor \inplace.
59951     /**
59952        Equivalent to assign().
59953        \note Only here for compatibility with STL naming conventions.
59954     **/
59955     CImgList<T>& clear() {
59956       return assign();
59957     }
59958 
59959     //! Construct list containing empty images \inplace.
59960     /**
59961        \see CImgList(unsigned int).
59962     **/
59963     CImgList<T>& assign(const unsigned int n) {
59964       if (!n) return assign();
59965       if (_allocated_width<n || _allocated_width>(n<<2)) {
59966         delete[] _data;
59967         _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
59968       }
59969       _width = n;
59970       return *this;
59971     }
59972 
59973     //! Construct list containing images of specified size \inplace.
59974     /**
59975        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
59976     **/
59977     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
59978                         const unsigned int depth=1, const unsigned int spectrum=1) {
59979       assign(n);
59980       cimglist_apply(*this,assign)(width,height,depth,spectrum);
59981       return *this;
59982     }
59983 
59984     //! Construct list containing images of specified size, and initialize pixel values \inplace.
59985     /**
59986        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
59987     **/
59988     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
59989                         const unsigned int depth, const unsigned int spectrum, const T& val) {
59990       assign(n);
59991       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
59992       return *this;
59993     }
59994 
59995     //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
59996     /**
59997        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
59998     **/
59999     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
60000                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
60001       _CImgList_stdarg(int);
60002       return *this;
60003     }
60004 
60005     //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
60006     /**
60007        \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
60008     **/
60009     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
60010                         const unsigned int depth, const unsigned int spectrum,
60011                         const double val0, const double val1, ...) {
60012       _CImgList_stdarg(double);
60013       return *this;
60014     }
60015 
60016     //! Construct list containing copies of an input image \inplace.
60017     /**
60018        \see CImgList(unsigned int, const CImg<t>&, bool).
60019     **/
60020     template<typename t>
60021     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
60022       assign(n);
60023       cimglist_apply(*this,assign)(img,is_shared);
60024       return *this;
60025     }
60026 
60027     //! Construct list from one image \inplace.
60028     /**
60029        \see CImgList(const CImg<t>&, bool).
60030     **/
60031     template<typename t>
60032     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
60033       assign(1);
60034       _data[0].assign(img,is_shared);
60035       return *this;
60036     }
60037 
60038     //! Construct list from two images \inplace.
60039     /**
60040        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
60041     **/
60042     template<typename t1, typename t2>
60043     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
60044       assign(2);
60045       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
60046       return *this;
60047     }
60048 
60049     //! Construct list from three images \inplace.
60050     /**
60051        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
60052     **/
60053     template<typename t1, typename t2, typename t3>
60054     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
60055       assign(3);
60056       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60057       return *this;
60058     }
60059 
60060     //! Construct list from four images \inplace.
60061     /**
60062        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
60063     **/
60064     template<typename t1, typename t2, typename t3, typename t4>
60065     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
60066                         const bool is_shared=false) {
60067       assign(4);
60068       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60069       _data[3].assign(img4,is_shared);
60070       return *this;
60071     }
60072 
60073     //! Construct list from five images \inplace.
60074     /**
60075        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
60076     **/
60077     template<typename t1, typename t2, typename t3, typename t4, typename t5>
60078     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
60079                         const CImg<t5>& img5, const bool is_shared=false) {
60080       assign(5);
60081       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60082       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
60083       return *this;
60084     }
60085 
60086     //! Construct list from six images \inplace.
60087     /**
60088        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
60089     **/
60090     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
60091     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
60092                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
60093       assign(6);
60094       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60095       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
60096       return *this;
60097     }
60098 
60099     //! Construct list from seven images \inplace.
60100     /**
60101        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
60102        const CImg<t>&, bool).
60103     **/
60104     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
60105     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
60106                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
60107       assign(7);
60108       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60109       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
60110       _data[6].assign(img7,is_shared);
60111       return *this;
60112     }
60113 
60114     //! Construct list from eight images \inplace.
60115     /**
60116        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
60117        const CImg<t>&, const CImg<t>&, bool).
60118     **/
60119     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
60120     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
60121                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
60122                         const bool is_shared=false) {
60123       assign(8);
60124       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
60125       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
60126       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
60127       return *this;
60128     }
60129 
60130     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
60131     /**
60132       \see CImgList(const CImgList<t>&, bool is_shared).
60133     **/
60134     template<typename t>
60135     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
60136       cimg::unused(is_shared);
60137       assign(list._width);
60138       cimglist_for(*this,l) _data[l].assign(list[l],false);
60139       return *this;
60140     }
60141 
60142     //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
60143     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
60144       if (this==&list) return *this;
60145       CImgList<T> res(list._width);
60146       cimglist_for(res,l) res[l].assign(list[l],is_shared);
60147       return res.move_to(*this);
60148     }
60149 
60150     //! Construct list by reading the content of a file \inplace.
60151     /**
60152       \see CImgList(const char *const).
60153     **/
60154     CImgList<T>& assign(const char *const filename) {
60155       return load(filename);
60156     }
60157 
60158     //! Construct list from the content of a display window \inplace.
60159     /**
60160       \see CImgList(const CImgDisplay&).
60161     **/
60162     CImgList<T>& assign(const CImgDisplay &disp) {
60163       return assign(CImg<T>(disp));
60164     }
60165 
60166     //! Transfer the content of the list instance to another list.
60167     /**
60168        \param list Destination list.
60169        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
60170     **/
60171     template<typename t>
60172     CImgList<t>& move_to(CImgList<t>& list) {
60173       list.assign(_width);
60174       bool is_one_shared_element = false;
60175       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
60176       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
60177       else cimglist_for(*this,l) _data[l].move_to(list[l]);
60178       assign();
60179       return list;
60180     }
60181 
60182     //! Transfer the content of the list instance at a specified position in another list.
60183     /**
60184        \param list Destination list.
60185        \param pos Index of the insertion in the list.
60186        \note When returning, the list instance is empty and the initial content of \c list is preserved
60187        (only images indexes may be modified).
60188      **/
60189     template<typename t>
60190     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
60191       if (is_empty()) return list;
60192       const unsigned int npos = pos>list._width?list._width:pos;
60193       list.insert(_width,npos);
60194       bool is_one_shared_element = false;
60195       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
60196       if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
60197       else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
60198       assign();
60199       return list;
60200     }
60201 
60202     //! Swap all fields between two list instances.
60203     /**
60204        \param list List to swap fields with.
60205        \note Can be used to exchange the content of two lists in a fast way.
60206     **/
60207     CImgList<T>& swap(CImgList<T>& list) {
60208       cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
60209       cimg::swap(_data,list._data);
60210       return list;
60211     }
60212 
60213     //! Return a reference to an empty list.
60214     /**
60215       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
60216       \code
60217       void f(const CImgList<char>& list=CImgList<char>::empty());
60218       \endcode
60219     **/
60220     static CImgList<T>& empty() {
60221       static CImgList<T> _empty;
60222       return _empty.assign();
60223     }
60224 
60225     //! Return a reference to an empty list \const.
60226     static const CImgList<T>& const_empty() {
60227       static const CImgList<T> _empty;
60228       return _empty;
60229     }
60230 
60231     //@}
60232     //------------------------------------------
60233     //
60234     //! \name Overloaded Operators
60235     //@{
60236     //------------------------------------------
60237 
60238     //! Return a reference to one image element of the list.
60239     /**
60240        \param pos Index of the image element.
60241     **/
60242     CImg<T>& operator()(const unsigned int pos) {
60243 #if cimg_verbosity>=3
60244       if (pos>=_width) {
60245         cimg::warn(_cimglist_instance
60246                    "operator(): Invalid image request, at position [%u].",
60247                    cimglist_instance,
60248                    pos);
60249         return *_data;
60250       }
60251 #endif
60252       return _data[pos];
60253     }
60254 
60255     //! Return a reference to one image of the list.
60256     /**
60257        \param pos Index of the image element.
60258     **/
60259     const CImg<T>& operator()(const unsigned int pos) const {
60260       return const_cast<CImgList<T>*>(this)->operator()(pos);
60261     }
60262 
60263     //! Return a reference to one pixel value of one image of the list.
60264     /**
60265        \param pos Index of the image element.
60266        \param x X-coordinate of the pixel value.
60267        \param y Y-coordinate of the pixel value.
60268        \param z Z-coordinate of the pixel value.
60269        \param c C-coordinate of the pixel value.
60270        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
60271     **/
60272     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
60273                   const unsigned int z=0, const unsigned int c=0) {
60274       return (*this)[pos](x,y,z,c);
60275     }
60276 
60277     //! Return a reference to one pixel value of one image of the list \const.
60278     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
60279                         const unsigned int z=0, const unsigned int c=0) const {
60280       return (*this)[pos](x,y,z,c);
60281     }
60282 
60283     //! Return pointer to the first image of the list.
60284     /**
60285        \note Images in a list are stored as a buffer of \c CImg<T>.
60286     **/
60287     operator CImg<T>*() {
60288       return _data;
60289     }
60290 
60291     //! Return pointer to the first image of the list \const.
60292     operator const CImg<T>*() const {
60293       return _data;
60294     }
60295 
60296     //! Construct list from one image \inplace.
60297     /**
60298         \param img Input image to copy in the constructed list.
60299         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
60300     **/
60301     template<typename t>
60302     CImgList<T>& operator=(const CImg<t>& img) {
60303       return assign(img);
60304     }
60305 
60306     //! Construct list from another list.
60307     /**
60308        \param list Input list to copy.
60309        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
60310     **/
60311     template<typename t>
60312     CImgList<T>& operator=(const CImgList<t>& list) {
60313       return assign(list);
60314     }
60315 
60316     //! Construct list from another list \specialization.
60317     CImgList<T>& operator=(const CImgList<T>& list) {
60318       return assign(list);
60319     }
60320 
60321     //! Construct list by reading the content of a file \inplace.
60322     /**
60323        \see CImgList(const char *const).
60324     **/
60325     CImgList<T>& operator=(const char *const filename) {
60326       return assign(filename);
60327     }
60328 
60329     //! Construct list from the content of a display window \inplace.
60330     /**
60331         \see CImgList(const CImgDisplay&).
60332     **/
60333     CImgList<T>& operator=(const CImgDisplay& disp) {
60334       return assign(disp);
60335     }
60336 
60337     //! Return a non-shared copy of a list.
60338     /**
60339         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
60340           It forces the copy to have non-shared elements.
60341     **/
60342     CImgList<T> operator+() const {
60343       return CImgList<T>(*this,false);
60344     }
60345 
60346     //! Return a copy of the list instance, where image \c img has been inserted at the end.
60347     /**
60348        \param img Image inserted at the end of the instance copy.
60349        \note Define a convenient way to create temporary lists of images, as in the following code:
60350        \code
60351        (img1,img2,img3,img4).display("My four images");
60352        \endcode
60353     **/
60354     template<typename t>
60355     CImgList<T>& operator,(const CImg<t>& img) {
60356       return insert(img);
60357     }
60358 
60359     //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
60360     template<typename t>
60361     CImgList<T> operator,(const CImg<t>& img) const {
60362       return (+*this).insert(img);
60363     }
60364 
60365     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
60366     /**
60367        \param list List inserted at the end of the instance copy.
60368     **/
60369     template<typename t>
60370     CImgList<T>& operator,(const CImgList<t>& list) {
60371       return insert(list);
60372     }
60373 
60374     //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
60375     template<typename t>
60376     CImgList<T>& operator,(const CImgList<t>& list) const {
60377       return (+*this).insert(list);
60378     }
60379 
60380     //! Return image corresponding to the appending of all images of the instance list along specified axis.
60381     /**
60382       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
60383       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
60384     **/
60385     CImg<T> operator>(const char axis) const {
60386       return get_append(axis,0);
60387     }
60388 
60389     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
60390     /**
60391       \param axis Axis used for image splitting.
60392       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
60393     **/
60394     CImgList<T> operator<(const char axis) const {
60395       return get_split(axis);
60396     }
60397 
60398     //@}
60399     //-------------------------------------
60400     //
60401     //! \name Instance Characteristics
60402     //@{
60403     //-------------------------------------
60404 
60405     //! Return the type of image pixel values as a C string.
60406     /**
60407        Return a \c char* string containing the usual type name of the image pixel values
60408        (i.e. a stringified version of the template parameter \c T).
60409        \note
60410        - The returned string may contain spaces (as in \c "unsigned char").
60411        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
60412     **/
60413     static const char* pixel_type() {
60414       return cimg::type<T>::string();
60415     }
60416 
60417     //! Return the size of the list, i.e. the number of images contained in it.
60418     /**
60419       \note Similar to size() but returns result as a (signed) integer.
60420     **/
60421     int width() const {
60422       return (int)_width;
60423     }
60424 
60425     //! Return the size of the list, i.e. the number of images contained in it.
60426     /**
60427       \note Similar to width() but returns result as an unsigned integer.
60428     **/
60429     unsigned int size() const {
60430       return _width;
60431     }
60432 
60433     //! Return pointer to the first image of the list.
60434     /**
60435        \note Images in a list are stored as a buffer of \c CImg<T>.
60436     **/
60437     CImg<T> *data() {
60438       return _data;
60439     }
60440 
60441     //! Return pointer to the first image of the list \const.
60442     const CImg<T> *data() const {
60443       return _data;
60444     }
60445 
60446     //! Return pointer to the pos-th image of the list.
60447     /**
60448        \param pos Index of the image element to access.
60449        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
60450     **/
60451 #if cimg_verbosity>=3
60452     CImg<T> *data(const unsigned int pos) {
60453       if (pos>=size())
60454         cimg::warn(_cimglist_instance
60455                    "data(): Invalid pointer request, at position [%u].",
60456                    cimglist_instance,
60457                    pos);
60458       return _data + pos;
60459     }
60460 
60461     const CImg<T> *data(const unsigned int l) const {
60462       return const_cast<CImgList<T>*>(this)->data(l);
60463     }
60464 #else
60465     CImg<T> *data(const unsigned int l) {
60466       return _data + l;
60467     }
60468 
60469     //! Return pointer to the pos-th image of the list \const.
60470     const CImg<T> *data(const unsigned int l) const {
60471       return _data + l;
60472     }
60473 #endif
60474 
60475     //! Return iterator to the first image of the list.
60476     /**
60477     **/
60478     iterator begin() {
60479       return _data;
60480     }
60481 
60482     //! Return iterator to the first image of the list \const.
60483     const_iterator begin() const {
60484       return _data;
60485     }
60486 
60487     //! Return iterator to one position after the last image of the list.
60488     /**
60489     **/
60490     iterator end() {
60491       return _data + _width;
60492     }
60493 
60494     //! Return iterator to one position after the last image of the list \const.
60495     const_iterator end() const {
60496       return _data + _width;
60497     }
60498 
60499     //! Return reference to the first image of the list.
60500     /**
60501     **/
60502     CImg<T>& front() {
60503       return *_data;
60504     }
60505 
60506     //! Return reference to the first image of the list \const.
60507     const CImg<T>& front() const {
60508       return *_data;
60509     }
60510 
60511     //! Return a reference to the last image of the list.
60512     /**
60513     **/
60514     const CImg<T>& back() const {
60515       return *(_data + _width - 1);
60516     }
60517 
60518     //! Return a reference to the last image of the list \const.
60519     CImg<T>& back() {
60520       return *(_data + _width - 1);
60521     }
60522 
60523     //! Return pos-th image of the list.
60524     /**
60525        \param pos Index of the image element to access.
60526     **/
60527     CImg<T>& at(const int pos) {
60528       if (is_empty())
60529         throw CImgInstanceException(_cimglist_instance
60530                                     "at(): Empty instance.",
60531                                     cimglist_instance);
60532 
60533       return _data[cimg::cut(pos,0,width() - 1)];
60534     }
60535 
60536     //! Access to pixel value with Dirichlet boundary conditions.
60537     /**
60538        \param pos Index of the image element to access.
60539        \param x X-coordinate of the pixel value.
60540        \param y Y-coordinate of the pixel value.
60541        \param z Z-coordinate of the pixel value.
60542        \param c C-coordinate of the pixel value.
60543        \param out_value Default value returned if \c offset is outside image bounds.
60544        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
60545     **/
60546     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60547       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
60548     }
60549 
60550     //! Access to pixel value with Dirichlet boundary conditions \const.
60551     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60552       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
60553     }
60554 
60555     //! Access to pixel value with Neumann boundary conditions.
60556     /**
60557        \param pos Index of the image element to access.
60558        \param x X-coordinate of the pixel value.
60559        \param y Y-coordinate of the pixel value.
60560        \param z Z-coordinate of the pixel value.
60561        \param c C-coordinate of the pixel value.
60562        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
60563     **/
60564     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
60565       if (is_empty())
60566         throw CImgInstanceException(_cimglist_instance
60567                                     "atNXYZC(): Empty instance.",
60568                                     cimglist_instance);
60569 
60570       return _atNXYZC(pos,x,y,z,c);
60571     }
60572 
60573     //! Access to pixel value with Neumann boundary conditions \const.
60574     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
60575       if (is_empty())
60576         throw CImgInstanceException(_cimglist_instance
60577                                     "atNXYZC(): Empty instance.",
60578                                     cimglist_instance);
60579 
60580       return _atNXYZC(pos,x,y,z,c);
60581     }
60582 
60583     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
60584       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
60585     }
60586 
60587     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
60588       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
60589     }
60590 
60591     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
60592     /**
60593        \param pos Index of the image element to access.
60594        \param x X-coordinate of the pixel value.
60595        \param y Y-coordinate of the pixel value.
60596        \param z Z-coordinate of the pixel value.
60597        \param c C-coordinate of the pixel value.
60598        \param out_value Default value returned if \c offset is outside image bounds.
60599        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60600     **/
60601     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60602       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
60603     }
60604 
60605     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
60606     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60607       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
60608     }
60609 
60610     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
60611     /**
60612        \param pos Index of the image element to access.
60613        \param x X-coordinate of the pixel value.
60614        \param y Y-coordinate of the pixel value.
60615        \param z Z-coordinate of the pixel value.
60616        \param c C-coordinate of the pixel value.
60617        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60618     **/
60619    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
60620       if (is_empty())
60621         throw CImgInstanceException(_cimglist_instance
60622                                     "atNXYZ(): Empty instance.",
60623                                     cimglist_instance);
60624 
60625       return _atNXYZ(pos,x,y,z,c);
60626     }
60627 
60628     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
60629     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
60630       if (is_empty())
60631         throw CImgInstanceException(_cimglist_instance
60632                                     "atNXYZ(): Empty instance.",
60633                                     cimglist_instance);
60634 
60635       return _atNXYZ(pos,x,y,z,c);
60636     }
60637 
60638     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
60639       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
60640     }
60641 
60642     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
60643       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
60644     }
60645 
60646     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
60647     /**
60648        \param pos Index of the image element to access.
60649        \param x X-coordinate of the pixel value.
60650        \param y Y-coordinate of the pixel value.
60651        \param z Z-coordinate of the pixel value.
60652        \param c C-coordinate of the pixel value.
60653        \param out_value Default value returned if \c offset is outside image bounds.
60654        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60655     **/
60656     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60657       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
60658     }
60659 
60660     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
60661     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60662       return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value);
60663     }
60664 
60665     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
60666     /**
60667        \param pos Index of the image element to access.
60668        \param x X-coordinate of the pixel value.
60669        \param y Y-coordinate of the pixel value.
60670        \param z Z-coordinate of the pixel value.
60671        \param c C-coordinate of the pixel value.
60672        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60673     **/
60674     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
60675       if (is_empty())
60676         throw CImgInstanceException(_cimglist_instance
60677                                     "atNXY(): Empty instance.",
60678                                     cimglist_instance);
60679 
60680       return _atNXY(pos,x,y,z,c);
60681     }
60682 
60683     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
60684     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
60685       if (is_empty())
60686         throw CImgInstanceException(_cimglist_instance
60687                                     "atNXY(): Empty instance.",
60688                                     cimglist_instance);
60689 
60690       return _atNXY(pos,x,y,z,c);
60691     }
60692 
60693     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
60694       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
60695     }
60696 
60697     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
60698       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
60699     }
60700 
60701     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
60702     /**
60703        \param pos Index of the image element to access.
60704        \param x X-coordinate of the pixel value.
60705        \param y Y-coordinate of the pixel value.
60706        \param z Z-coordinate of the pixel value.
60707        \param c C-coordinate of the pixel value.
60708        \param out_value Default value returned if \c offset is outside image bounds.
60709        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60710     **/
60711     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60712       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
60713     }
60714 
60715     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
60716     T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60717       return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
60718     }
60719 
60720     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
60721     /**
60722        \param pos Index of the image element to access.
60723        \param x X-coordinate of the pixel value.
60724        \param y Y-coordinate of the pixel value.
60725        \param z Z-coordinate of the pixel value.
60726        \param c C-coordinate of the pixel value.
60727        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60728     **/
60729     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
60730       if (is_empty())
60731         throw CImgInstanceException(_cimglist_instance
60732                                     "atNX(): Empty instance.",
60733                                     cimglist_instance);
60734 
60735       return _atNX(pos,x,y,z,c);
60736     }
60737 
60738     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
60739     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
60740       if (is_empty())
60741         throw CImgInstanceException(_cimglist_instance
60742                                     "atNX(): Empty instance.",
60743                                     cimglist_instance);
60744 
60745       return _atNX(pos,x,y,z,c);
60746     }
60747 
60748     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
60749       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
60750     }
60751 
60752     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
60753       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
60754     }
60755 
60756     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
60757     /**
60758        \param pos Index of the image element to access.
60759        \param x X-coordinate of the pixel value.
60760        \param y Y-coordinate of the pixel value.
60761        \param z Z-coordinate of the pixel value.
60762        \param c C-coordinate of the pixel value.
60763        \param out_value Default value returned if \c offset is outside image bounds.
60764        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60765     **/
60766     T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
60767       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
60768     }
60769 
60770     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
60771     T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
60772       return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c);
60773     }
60774 
60775     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
60776     /**
60777        \param pos Index of the image element to access.
60778        \param x X-coordinate of the pixel value.
60779        \param y Y-coordinate of the pixel value.
60780        \param z Z-coordinate of the pixel value.
60781        \param c C-coordinate of the pixel value.
60782        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
60783     **/
60784     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
60785       if (is_empty())
60786         throw CImgInstanceException(_cimglist_instance
60787                                     "atN(): Empty instance.",
60788                                     cimglist_instance);
60789       return _atN(pos,x,y,z,c);
60790     }
60791 
60792     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
60793     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
60794       if (is_empty())
60795         throw CImgInstanceException(_cimglist_instance
60796                                     "atN(): Empty instance.",
60797                                     cimglist_instance);
60798       return _atN(pos,x,y,z,c);
60799     }
60800 
60801     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
60802       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
60803     }
60804 
60805     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
60806       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
60807     }
60808 
60809     //@}
60810     //-------------------------------------
60811     //
60812     //! \name Instance Checking
60813     //@{
60814     //-------------------------------------
60815 
60816     //! Return \c true if list is empty.
60817     /**
60818     **/
60819     bool is_empty() const {
60820       return (!_data || !_width);
60821     }
60822 
60823     //! Test if number of image elements is equal to specified value.
60824     /**
60825         \param size_n Number of image elements to test.
60826     **/
60827     bool is_sameN(const unsigned int size_n) const {
60828       return _width==size_n;
60829     }
60830 
60831     //! Test if number of image elements is equal between two images lists.
60832     /**
60833         \param list Input list to compare with.
60834     **/
60835     template<typename t>
60836     bool is_sameN(const CImgList<t>& list) const {
60837       return is_sameN(list._width);
60838     }
60839 
60840     // Define useful functions to check list dimensions.
60841     // (cannot be documented because macro-generated).
60842 #define _cimglist_def_is_same1(axis) \
60843     bool is_same##axis(const unsigned int val) const { \
60844       bool res = true; \
60845       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \
60846       return res; \
60847     } \
60848     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
60849       return is_sameN(n) && is_same##axis(val); \
60850     } \
60851 
60852 #define _cimglist_def_is_same2(axis1,axis2) \
60853     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
60854       bool res = true; \
60855       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \
60856       return res; \
60857     } \
60858     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
60859       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
60860     } \
60861 
60862 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
60863     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
60864                                       const unsigned int val3) const { \
60865       bool res = true; \
60866       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
60867       return res; \
60868     } \
60869     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
60870                                        const unsigned int val2, const unsigned int val3) const { \
60871       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
60872     } \
60873 
60874 #define _cimglist_def_is_same(axis) \
60875     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
60876       bool res = true; \
60877       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \
60878       return res; \
60879     } \
60880     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
60881       const unsigned int lmin = std::min(_width,list._width); \
60882       bool res = true; \
60883       for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); \
60884       return res; \
60885     } \
60886     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
60887       return (is_sameN(n) && is_same##axis(img)); \
60888     } \
60889     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
60890       return (is_sameN(list) && is_same##axis(list)); \
60891     }
60892 
60893     _cimglist_def_is_same(XY)
60894     _cimglist_def_is_same(XZ)
60895     _cimglist_def_is_same(XC)
60896     _cimglist_def_is_same(YZ)
60897     _cimglist_def_is_same(YC)
60898     _cimglist_def_is_same(XYZ)
60899     _cimglist_def_is_same(XYC)
60900     _cimglist_def_is_same(YZC)
60901     _cimglist_def_is_same(XYZC)
60902     _cimglist_def_is_same1(X)
60903     _cimglist_def_is_same1(Y)
60904     _cimglist_def_is_same1(Z)
60905     _cimglist_def_is_same1(C)
60906     _cimglist_def_is_same2(X,Y)
60907     _cimglist_def_is_same2(X,Z)
60908     _cimglist_def_is_same2(X,C)
60909     _cimglist_def_is_same2(Y,Z)
60910     _cimglist_def_is_same2(Y,C)
60911     _cimglist_def_is_same2(Z,C)
60912     _cimglist_def_is_same3(X,Y,Z)
60913     _cimglist_def_is_same3(X,Y,C)
60914     _cimglist_def_is_same3(X,Z,C)
60915     _cimglist_def_is_same3(Y,Z,C)
60916 
60917     //! Test if dimensions of each image of the list match specified arguments.
60918     /**
60919       \param dx Checked image width.
60920       \param dy Checked image height.
60921       \param dz Checked image depth.
60922       \param dc Checked image spectrum.
60923     **/
60924     bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
60925                      const unsigned int dz, const unsigned int dc) const {
60926       bool res = true;
60927       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
60928       return res;
60929     }
60930 
60931     //! Test if list dimensions match specified arguments.
60932     /**
60933        \param n Number of images in the list.
60934        \param dx Checked image width.
60935        \param dy Checked image height.
60936        \param dz Checked image depth.
60937        \param dc Checked image spectrum.
60938     **/
60939     bool is_sameNXYZC(const unsigned int n,
60940                       const unsigned int dx, const unsigned int dy,
60941                       const unsigned int dz, const unsigned int dc) const {
60942       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
60943     }
60944 
60945     //! Test if list contains one particular pixel location.
60946     /**
60947        \param n Index of the image whom checked pixel value belong to.
60948        \param x X-coordinate of the checked pixel value.
60949        \param y Y-coordinate of the checked pixel value.
60950        \param z Z-coordinate of the checked pixel value.
60951        \param c C-coordinate of the checked pixel value.
60952     **/
60953     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
60954       if (is_empty()) return false;
60955       return n>=0 && n<width() && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
60956         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
60957     }
60958 
60959     //! Test if list contains image with specified index.
60960     /**
60961        \param n Index of the checked image.
60962     **/
60963     bool containsN(const int n) const {
60964       if (is_empty()) return false;
60965       return n>=0 && n<width();
60966     }
60967 
60968     //! Test if one image of the list contains the specified referenced value.
60969     /**
60970        \param pixel Reference to pixel value to test.
60971        \param[out] n Index of image containing the pixel value, if test succeeds.
60972        \param[out] x X-coordinate of the pixel value, if test succeeds.
60973        \param[out] y Y-coordinate of the pixel value, if test succeeds.
60974        \param[out] z Z-coordinate of the pixel value, if test succeeds.
60975        \param[out] c C-coordinate of the pixel value, if test succeeds.
60976        \note If true, set coordinates (n,x,y,z,c).
60977     **/
60978     template<typename t>
60979     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
60980       if (is_empty()) return false;
60981       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
60982       return false;
60983     }
60984 
60985     //! Test if one of the image list contains the specified referenced value.
60986     /**
60987        \param pixel Reference to pixel value to test.
60988        \param[out] n Index of image containing the pixel value, if test succeeds.
60989        \param[out] x X-coordinate of the pixel value, if test succeeds.
60990        \param[out] y Y-coordinate of the pixel value, if test succeeds.
60991        \param[out] z Z-coordinate of the pixel value, if test succeeds.
60992        \note If true, set coordinates (n,x,y,z).
60993     **/
60994     template<typename t>
60995     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
60996       t c;
60997       return contains(pixel,n,x,y,z,c);
60998     }
60999 
61000     //! Test if one of the image list contains the specified referenced value.
61001     /**
61002        \param pixel Reference to pixel value to test.
61003        \param[out] n Index of image containing the pixel value, if test succeeds.
61004        \param[out] x X-coordinate of the pixel value, if test succeeds.
61005        \param[out] y Y-coordinate of the pixel value, if test succeeds.
61006        \note If true, set coordinates (n,x,y).
61007     **/
61008     template<typename t>
61009     bool contains(const T& pixel, t& n, t& x, t&y) const {
61010       t z, c;
61011       return contains(pixel,n,x,y,z,c);
61012     }
61013 
61014     //! Test if one of the image list contains the specified referenced value.
61015     /**
61016        \param pixel Reference to pixel value to test.
61017        \param[out] n Index of image containing the pixel value, if test succeeds.
61018        \param[out] x X-coordinate of the pixel value, if test succeeds.
61019        \note If true, set coordinates (n,x).
61020     **/
61021     template<typename t>
61022     bool contains(const T& pixel, t& n, t& x) const {
61023       t y, z, c;
61024       return contains(pixel,n,x,y,z,c);
61025     }
61026 
61027     //! Test if one of the image list contains the specified referenced value.
61028     /**
61029        \param pixel Reference to pixel value to test.
61030        \param[out] n Index of image containing the pixel value, if test succeeds.
61031        \note If true, set coordinates (n).
61032     **/
61033     template<typename t>
61034     bool contains(const T& pixel, t& n) const {
61035       t x, y, z, c;
61036       return contains(pixel,n,x,y,z,c);
61037     }
61038 
61039     //! Test if one of the image list contains the specified referenced value.
61040     /**
61041        \param pixel Reference to pixel value to test.
61042     **/
61043     bool contains(const T& pixel) const {
61044       unsigned int n, x, y, z, c;
61045       return contains(pixel,n,x,y,z,c);
61046     }
61047 
61048     //! Test if the list contains the image 'img'.
61049     /**
61050        \param img Reference to image to test.
61051        \param[out] n Index of image in the list, if test succeeds.
61052        \note If true, returns the position (n) of the image in the list.
61053     **/
61054     template<typename t>
61055     bool contains(const CImg<T>& img, t& n) const {
61056       if (is_empty()) return false;
61057       const CImg<T> *const ptr = &img;
61058       cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
61059       return false;
61060     }
61061 
61062     //! Test if the list contains the image img.
61063     /**
61064        \param img Reference to image to test.
61065     **/
61066     bool contains(const CImg<T>& img) const {
61067       unsigned int n;
61068       return contains(img,n);
61069     }
61070 
61071     //@}
61072     //-------------------------------------
61073     //
61074     //! \name Mathematical Functions
61075     //@{
61076     //-------------------------------------
61077 
61078     //! Return a reference to the minimum pixel value of the instance list.
61079     /**
61080     **/
61081     T& min() {
61082       bool is_all_empty = true;
61083       T *ptr_min = 0;
61084       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61085         ptr_min = _data[l]._data;
61086         is_all_empty = false;
61087         break;
61088       }
61089       if (is_all_empty)
61090         throw CImgInstanceException(_cimglist_instance
61091                                     "min(): %s.",
61092                                     _data?"List of empty images":"Empty instance",
61093                                     cimglist_instance);
61094       T min_value = *ptr_min;
61095       cimglist_for(*this,l) {
61096         const CImg<T>& img = _data[l];
61097         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
61098       }
61099       return *ptr_min;
61100     }
61101 
61102     //! Return a reference to the minimum pixel value of the instance list \const.
61103     const T& min() const {
61104       bool is_all_empty = true;
61105       T *ptr_min = 0;
61106       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61107         ptr_min = _data[l]._data;
61108         is_all_empty = false;
61109         break;
61110       }
61111       if (is_all_empty)
61112         throw CImgInstanceException(_cimglist_instance
61113                                     "min(): %s.",
61114                                     _data?"List of empty images":"Empty instance",
61115                                     cimglist_instance);
61116       T min_value = *ptr_min;
61117       cimglist_for(*this,l) {
61118         const CImg<T>& img = _data[l];
61119         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
61120       }
61121       return *ptr_min;
61122     }
61123 
61124     //! Return a reference to the maximum pixel value of the instance list.
61125     /**
61126     **/
61127     T& max() {
61128       bool is_all_empty = true;
61129       T *ptr_max = 0;
61130       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61131         ptr_max = _data[l]._data;
61132         is_all_empty = false;
61133         break;
61134       }
61135       if (is_all_empty)
61136         throw CImgInstanceException(_cimglist_instance
61137                                     "max(): %s.",
61138                                     _data?"List of empty images":"Empty instance",
61139                                     cimglist_instance);
61140       T max_value = *ptr_max;
61141       cimglist_for(*this,l) {
61142         const CImg<T>& img = _data[l];
61143         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
61144       }
61145       return *ptr_max;
61146     }
61147 
61148     //! Return a reference to the maximum pixel value of the instance list \const.
61149     const T& max() const {
61150       bool is_all_empty = true;
61151       T *ptr_max = 0;
61152       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61153         ptr_max = _data[l]._data;
61154         is_all_empty = false;
61155         break;
61156       }
61157       if (is_all_empty)
61158         throw CImgInstanceException(_cimglist_instance
61159                                     "max(): %s.",
61160                                     _data?"List of empty images":"Empty instance",
61161                                     cimglist_instance);
61162       T max_value = *ptr_max;
61163       cimglist_for(*this,l) {
61164         const CImg<T>& img = _data[l];
61165         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
61166       }
61167       return *ptr_max;
61168     }
61169 
61170     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
61171     /**
61172        \param[out] max_val Value of the maximum value found.
61173     **/
61174     template<typename t>
61175     T& min_max(t& max_val) {
61176       bool is_all_empty = true;
61177       T *ptr_min = 0;
61178       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61179         ptr_min = _data[l]._data;
61180         is_all_empty = false;
61181         break;
61182       }
61183       if (is_all_empty)
61184         throw CImgInstanceException(_cimglist_instance
61185                                     "min_max(): %s.",
61186                                     _data?"List of empty images":"Empty instance",
61187                                     cimglist_instance);
61188       T min_value = *ptr_min, max_value = min_value;
61189       cimglist_for(*this,l) {
61190         const CImg<T>& img = _data[l];
61191         cimg_for(img,ptrs,T) {
61192           const T val = *ptrs;
61193           if (val<min_value) { min_value = val; ptr_min = ptrs; }
61194           if (val>max_value) max_value = val;
61195         }
61196       }
61197       max_val = (t)max_value;
61198       return *ptr_min;
61199     }
61200 
61201     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
61202     /**
61203        \param[out] max_val Value of the maximum value found.
61204     **/
61205     template<typename t>
61206     const T& min_max(t& max_val) const {
61207       bool is_all_empty = true;
61208       T *ptr_min = 0;
61209       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61210         ptr_min = _data[l]._data;
61211         is_all_empty = false;
61212         break;
61213       }
61214       if (is_all_empty)
61215         throw CImgInstanceException(_cimglist_instance
61216                                     "min_max(): %s.",
61217                                     _data?"List of empty images":"Empty instance",
61218                                     cimglist_instance);
61219       T min_value = *ptr_min, max_value = min_value;
61220       cimglist_for(*this,l) {
61221         const CImg<T>& img = _data[l];
61222         cimg_for(img,ptrs,T) {
61223           const T val = *ptrs;
61224           if (val<min_value) { min_value = val; ptr_min = ptrs; }
61225           if (val>max_value) max_value = val;
61226         }
61227       }
61228       max_val = (t)max_value;
61229       return *ptr_min;
61230     }
61231 
61232     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
61233     /**
61234        \param[out] min_val Value of the minimum value found.
61235     **/
61236     template<typename t>
61237     T& max_min(t& min_val) {
61238       bool is_all_empty = true;
61239       T *ptr_max = 0;
61240       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61241         ptr_max = _data[l]._data;
61242         is_all_empty = false;
61243         break;
61244       }
61245       if (is_all_empty)
61246         throw CImgInstanceException(_cimglist_instance
61247                                     "max_min(): %s.",
61248                                     _data?"List of empty images":"Empty instance",
61249                                     cimglist_instance);
61250       T min_value = *ptr_max, max_value = min_value;
61251       cimglist_for(*this,l) {
61252         const CImg<T>& img = _data[l];
61253         cimg_for(img,ptrs,T) {
61254           const T val = *ptrs;
61255           if (val>max_value) { max_value = val; ptr_max = ptrs; }
61256           if (val<min_value) min_value = val;
61257         }
61258       }
61259       min_val = (t)min_value;
61260       return *ptr_max;
61261     }
61262 
61263     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
61264     template<typename t>
61265     const T& max_min(t& min_val) const {
61266       bool is_all_empty = true;
61267       T *ptr_max = 0;
61268       cimglist_for(*this,l) if (!_data[l].is_empty()) {
61269         ptr_max = _data[l]._data;
61270         is_all_empty = false;
61271         break;
61272       }
61273       if (is_all_empty)
61274         throw CImgInstanceException(_cimglist_instance
61275                                     "max_min(): %s.",
61276                                     _data?"List of empty images":"Empty instance",
61277                                     cimglist_instance);
61278       T min_value = *ptr_max, max_value = min_value;
61279       cimglist_for(*this,l) {
61280         const CImg<T>& img = _data[l];
61281         cimg_for(img,ptrs,T) {
61282           const T val = *ptrs;
61283           if (val>max_value) { max_value = val; ptr_max = ptrs; }
61284           if (val<min_value) min_value = val;
61285         }
61286       }
61287       min_val = (t)min_value;
61288       return *ptr_max;
61289     }
61290 
61291     //@}
61292     //---------------------------
61293     //
61294     //! \name List Manipulation
61295     //@{
61296     //---------------------------
61297 
61298     //! Insert a copy of the image \c img into the current image list, at position \c pos.
61299     /**
61300         \param img Image to insert a copy to the list.
61301         \param pos Index of the insertion.
61302         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
61303     **/
61304     template<typename t>
61305     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
61306       const unsigned int npos = pos==~0U?_width:pos;
61307       if (npos>_width)
61308         throw CImgArgumentException(_cimglist_instance
61309                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
61310                                     "at position %u.",
61311                                     cimglist_instance,
61312                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
61313       if (is_shared)
61314         throw CImgArgumentException(_cimglist_instance
61315                                     "insert(): Invalid insertion request of specified shared image "
61316                                     "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
61317                                     cimglist_instance,
61318                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
61319 
61320       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
61321                                                                         (_allocated_width=16)]:0;
61322       if (!_data) { // Insert new element into empty list
61323         _data = new_data;
61324         *_data = img;
61325       } else {
61326         if (new_data) { // Insert with re-allocation
61327           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
61328           if (npos!=_width - 1)
61329             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
61330           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
61331           delete[] _data;
61332           _data = new_data;
61333         } else if (npos!=_width - 1) // Insert without re-allocation
61334           std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
61335         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
61336         _data[npos]._data = 0;
61337         _data[npos] = img;
61338       }
61339       return *this;
61340     }
61341 
61342     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
61343     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
61344       const unsigned int npos = pos==~0U?_width:pos;
61345       if (npos>_width)
61346         throw CImgArgumentException(_cimglist_instance
61347                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
61348                                     "at position %u.",
61349                                     cimglist_instance,
61350                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
61351       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
61352                                                                         (_allocated_width=16)]:0;
61353       if (!_data) { // Insert new element into empty list
61354         _data = new_data;
61355         if (is_shared && img) {
61356           _data->_width = img._width;
61357           _data->_height = img._height;
61358           _data->_depth = img._depth;
61359           _data->_spectrum = img._spectrum;
61360           _data->_is_shared = true;
61361           _data->_data = img._data;
61362         } else *_data = img;
61363       }
61364       else {
61365         if (new_data) { // Insert with re-allocation
61366           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
61367           if (npos!=_width - 1)
61368             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
61369           if (is_shared && img) {
61370             new_data[npos]._width = img._width;
61371             new_data[npos]._height = img._height;
61372             new_data[npos]._depth = img._depth;
61373             new_data[npos]._spectrum = img._spectrum;
61374             new_data[npos]._is_shared = true;
61375             new_data[npos]._data = img._data;
61376           } else {
61377             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
61378             new_data[npos]._data = 0;
61379             new_data[npos] = img;
61380           }
61381           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
61382           delete[] _data;
61383           _data = new_data;
61384         } else { // Insert without re-allocation
61385           if (npos!=_width - 1)
61386             std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
61387           if (is_shared && img) {
61388             _data[npos]._width = img._width;
61389             _data[npos]._height = img._height;
61390             _data[npos]._depth = img._depth;
61391             _data[npos]._spectrum = img._spectrum;
61392             _data[npos]._is_shared = true;
61393             _data[npos]._data = img._data;
61394           } else {
61395             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
61396             _data[npos]._data = 0;
61397             _data[npos] = img;
61398           }
61399         }
61400       }
61401       return *this;
61402     }
61403 
61404     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
61405     template<typename t>
61406     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
61407       return (+*this).insert(img,pos,is_shared);
61408     }
61409 
61410     //! Insert n empty images img into the current image list, at position \p pos.
61411     /**
61412        \param n Number of empty images to insert.
61413        \param pos Index of the insertion.
61414     **/
61415     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
61416       CImg<T> empty;
61417       if (!n) return *this;
61418       const unsigned int npos = pos==~0U?_width:pos;
61419       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
61420       return *this;
61421     }
61422 
61423     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
61424     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
61425       return (+*this).insert(n,pos);
61426     }
61427 
61428     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
61429     /**
61430        \param n Number of image copies to insert.
61431        \param img Image to insert by copy.
61432        \param pos Index of the insertion.
61433        \param is_shared Tells if inserted images are shared copies of \c img or not.
61434     **/
61435     template<typename t>
61436     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
61437                         const bool is_shared=false) {
61438       if (!n) return *this;
61439       const unsigned int npos = pos==~0U?_width:pos;
61440       insert(img,npos,is_shared);
61441       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
61442       return *this;
61443     }
61444 
61445     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
61446     template<typename t>
61447     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
61448                            const bool is_shared=false) const {
61449       return (+*this).insert(n,img,pos,is_shared);
61450     }
61451 
61452     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
61453     /**
61454       \param list Image list to insert.
61455       \param pos Index of the insertion.
61456       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
61457     **/
61458     template<typename t>
61459     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
61460       const unsigned int npos = pos==~0U?_width:pos;
61461       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
61462       else insert(CImgList<T>(list),npos,is_shared);
61463       return *this;
61464     }
61465 
61466     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
61467     template<typename t>
61468     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
61469       return (+*this).insert(list,pos,is_shared);
61470     }
61471 
61472     //! Insert n copies of the list \c list at position \c pos of the current list.
61473     /**
61474       \param n Number of list copies to insert.
61475       \param list Image list to insert.
61476       \param pos Index of the insertion.
61477       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
61478     **/
61479     template<typename t>
61480     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
61481                         const bool is_shared=false) {
61482       if (!n) return *this;
61483       const unsigned int npos = pos==~0U?_width:pos;
61484       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
61485       return *this;
61486     }
61487 
61488     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
61489     template<typename t>
61490     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
61491                            const bool is_shared=false) const {
61492       return (+*this).insert(n,list,pos,is_shared);
61493     }
61494 
61495     //! Remove all images between from indexes.
61496     /**
61497       \param pos1 Starting index of the removal.
61498       \param pos2 Ending index of the removal.
61499     **/
61500     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
61501       const unsigned int
61502         npos1 = pos1<pos2?pos1:pos2,
61503         tpos2 = pos1<pos2?pos2:pos1,
61504         npos2 = tpos2<_width?tpos2:_width - 1;
61505       if (npos1>=_width)
61506         throw CImgArgumentException(_cimglist_instance
61507                                     "remove(): Invalid remove request at positions %u->%u.",
61508                                     cimglist_instance,
61509                                     npos1,tpos2);
61510       else {
61511         if (tpos2>=_width)
61512           throw CImgArgumentException(_cimglist_instance
61513                                       "remove(): Invalid remove request at positions %u->%u.",
61514                                       cimglist_instance,
61515                                       npos1,tpos2);
61516 
61517         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
61518         const unsigned int nb = 1 + npos2 - npos1;
61519         if (!(_width-=nb)) return assign();
61520         if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
61521           if (npos1!=_width)
61522             std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
61523           std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
61524         } else { // Removing items with reallocation
61525           _allocated_width>>=4;
61526           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
61527           CImg<T> *const new_data = new CImg<T>[_allocated_width];
61528           if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
61529           if (npos1!=_width)
61530             std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
61531           if (_width!=_allocated_width)
61532             std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
61533           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
61534           delete[] _data;
61535           _data = new_data;
61536         }
61537       }
61538       return *this;
61539     }
61540 
61541     //! Remove all images between from indexes \newinstance.
61542     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
61543       return (+*this).remove(pos1,pos2);
61544     }
61545 
61546     //! Remove image at index \c pos from the image list.
61547     /**
61548       \param pos Index of the image to remove.
61549     **/
61550     CImgList<T>& remove(const unsigned int pos) {
61551       return remove(pos,pos);
61552     }
61553 
61554     //! Remove image at index \c pos from the image list \newinstance.
61555     CImgList<T> get_remove(const unsigned int pos) const {
61556       return (+*this).remove(pos);
61557     }
61558 
61559     //! Remove last image.
61560     /**
61561     **/
61562     CImgList<T>& remove() {
61563       return remove(_width - 1);
61564     }
61565 
61566     //! Remove last image \newinstance.
61567     CImgList<T> get_remove() const {
61568       return (+*this).remove();
61569     }
61570 
61571     //! Reverse list order.
61572     CImgList<T>& reverse() {
61573       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
61574       return *this;
61575     }
61576 
61577     //! Reverse list order \newinstance.
61578     CImgList<T> get_reverse() const {
61579       return (+*this).reverse();
61580     }
61581 
61582     //! Return a sublist.
61583     /**
61584       \param pos0 Starting index of the sublist.
61585       \param pos1 Ending index of the sublist.
61586     **/
61587     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
61588       return get_images(pos0,pos1).move_to(*this);
61589     }
61590 
61591     //! Return a sublist \newinstance.
61592     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
61593       if (pos0>pos1 || pos1>=_width)
61594         throw CImgArgumentException(_cimglist_instance
61595                                     "images(): Specified sub-list indices (%u->%u) are out of bounds.",
61596                                     cimglist_instance,
61597                                     pos0,pos1);
61598       CImgList<T> res(pos1 - pos0 + 1);
61599       cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
61600       return res;
61601     }
61602 
61603     //! Return a shared sublist.
61604     /**
61605       \param pos0 Starting index of the sublist.
61606       \param pos1 Ending index of the sublist.
61607     **/
61608     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
61609       if (pos0>pos1 || pos1>=_width)
61610         throw CImgArgumentException(_cimglist_instance
61611                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
61612                                     cimglist_instance,
61613                                     pos0,pos1);
61614       CImgList<T> res(pos1 - pos0 + 1);
61615       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
61616       return res;
61617     }
61618 
61619     //! Return a shared sublist \newinstance.
61620     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
61621       if (pos0>pos1 || pos1>=_width)
61622         throw CImgArgumentException(_cimglist_instance
61623                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
61624                                     cimglist_instance,
61625                                     pos0,pos1);
61626       CImgList<T> res(pos1 - pos0 + 1);
61627       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
61628       return res;
61629     }
61630 
61631     //! Return a single image which is the appending of all images of the current CImgList instance.
61632     /**
61633        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
61634        \param align Appending alignment.
61635     **/
61636     CImg<T> get_append(const char axis, const float align=0) const {
61637       if (is_empty()) return CImg<T>();
61638       if (_width==1) return +((*this)[0]);
61639       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
61640       CImg<T> res;
61641       switch (cimg::lowercase(axis)) {
61642       case 'x' : { // Along the X-axis
61643         cimglist_for(*this,l) {
61644           const CImg<T>& img = (*this)[l];
61645           if (img) {
61646             dx+=img._width;
61647             dy = std::max(dy,img._height);
61648             dz = std::max(dz,img._depth);
61649             dc = std::max(dc,img._spectrum);
61650           }
61651         }
61652         res.assign(dx,dy,dz,dc,(T)0);
61653         if (res) cimglist_for(*this,l) {
61654             const CImg<T>& img = (*this)[l];
61655             if (img) {
61656               if (img._height==1 && img._depth==1 && img._spectrum==1 &&
61657                   res._height==1 && res._depth==1 && res._spectrum==1)
61658                 std::memcpy(&res[pos],img._data,sizeof(T)*img._width);
61659               else
61660                 res.draw_image(pos,
61661                                (int)(align*(dy - img._height)),
61662                                (int)(align*(dz - img._depth)),
61663                                (int)(align*(dc - img._spectrum)),
61664                                img);
61665             }
61666             pos+=img._width;
61667           }
61668       } break;
61669       case 'y' : { // Along the Y-axis
61670         cimglist_for(*this,l) {
61671           const CImg<T>& img = (*this)[l];
61672           if (img) {
61673             dx = std::max(dx,img._width);
61674             dy+=img._height;
61675             dz = std::max(dz,img._depth);
61676             dc = std::max(dc,img._spectrum);
61677           }
61678         }
61679         res.assign(dx,dy,dz,dc,(T)0);
61680         if (res) cimglist_for(*this,l) {
61681             const CImg<T>& img = (*this)[l];
61682             if (img) {
61683               if (img._width==1 && img._depth==1 && img._spectrum==1 &&
61684                   res._width==1 && res._depth==1 && res._spectrum==1)
61685                 std::memcpy(&res[pos],img._data,sizeof(T)*img._height);
61686               else
61687                 res.draw_image((int)(align*(dx - img._width)),
61688                                pos,
61689                                (int)(align*(dz - img._depth)),
61690                                (int)(align*(dc - img._spectrum)),
61691                                img);
61692             }
61693             pos+=img._height;
61694           }
61695       } break;
61696       case 'z' : { // Along the Z-axis
61697         cimglist_for(*this,l) {
61698           const CImg<T>& img = (*this)[l];
61699           if (img) {
61700             dx = std::max(dx,img._width);
61701             dy = std::max(dy,img._height);
61702             dz+=img._depth;
61703             dc = std::max(dc,img._spectrum);
61704           }
61705         }
61706         res.assign(dx,dy,dz,dc,(T)0);
61707         if (res) cimglist_for(*this,l) {
61708             const CImg<T>& img = (*this)[l];
61709             if (img) {
61710               if (img._width==1 && img._height==1 && img._spectrum==1 &&
61711                   res._width==1 && res._height==1 && res._spectrum==1)
61712                 std::memcpy(&res[pos],img._data,sizeof(T)*img._depth);
61713               else
61714                 res.draw_image((int)(align*(dx - img._width)),
61715                                (int)(align*(dy - img._height)),
61716                                pos,
61717                                (int)(align*(dc - img._spectrum)),
61718                                img);
61719             }
61720             pos+=img._depth;
61721           }
61722       } break;
61723       default : { // Along the C-axis
61724         cimglist_for(*this,l) {
61725           const CImg<T>& img = (*this)[l];
61726           if (img) {
61727             dx = std::max(dx,img._width);
61728             dy = std::max(dy,img._height);
61729             dz = std::max(dz,img._depth);
61730             dc+=img._spectrum;
61731           }
61732         }
61733         res.assign(dx,dy,dz,dc,(T)0);
61734         if (res) cimglist_for(*this,l) {
61735             const CImg<T>& img = (*this)[l];
61736             if (img) {
61737               if (img._width==1 && img._height==1 && img._depth==1 &&
61738                   res._width==1 && res._height==1 && res._depth==1)
61739                 std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum);
61740               else
61741                 res.draw_image((int)(align*(dx - img._width)),
61742                                (int)(align*(dy - img._height)),
61743                                (int)(align*(dz - img._depth)),
61744                                pos,
61745                                img);
61746             }
61747             pos+=img._spectrum;
61748           }
61749       }
61750       }
61751       return res;
61752     }
61753 
61754     //! Return a list where each image has been split along the specified axis.
61755     /**
61756         \param axis Axis to split images along.
61757         \param nb Number of split parts for each image.
61758     **/
61759     CImgList<T>& split(const char axis, const int nb=-1) {
61760       return get_split(axis,nb).move_to(*this);
61761     }
61762 
61763     //! Return a list where each image has been split along the specified axis \newinstance.
61764     CImgList<T> get_split(const char axis, const int nb=-1) const {
61765       CImgList<T> res;
61766       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
61767       return res;
61768     }
61769 
61770     //! Insert image at the end of the list.
61771     /**
61772       \param img Image to insert.
61773     **/
61774     template<typename t>
61775     CImgList<T>& push_back(const CImg<t>& img) {
61776       return insert(img);
61777     }
61778 
61779     //! Insert image at the front of the list.
61780     /**
61781       \param img Image to insert.
61782     **/
61783     template<typename t>
61784     CImgList<T>& push_front(const CImg<t>& img) {
61785       return insert(img,0);
61786     }
61787 
61788     //! Insert list at the end of the current list.
61789     /**
61790       \param list List to insert.
61791     **/
61792     template<typename t>
61793     CImgList<T>& push_back(const CImgList<t>& list) {
61794       return insert(list);
61795     }
61796 
61797     //! Insert list at the front of the current list.
61798     /**
61799       \param list List to insert.
61800     **/
61801     template<typename t>
61802     CImgList<T>& push_front(const CImgList<t>& list) {
61803       return insert(list,0);
61804     }
61805 
61806     //! Remove last image.
61807     /**
61808     **/
61809     CImgList<T>& pop_back() {
61810       return remove(_width - 1);
61811     }
61812 
61813     //! Remove first image.
61814     /**
61815     **/
61816     CImgList<T>& pop_front() {
61817       return remove(0);
61818     }
61819 
61820     //! Remove image pointed by iterator.
61821     /**
61822       \param iter Iterator pointing to the image to remove.
61823     **/
61824     CImgList<T>& erase(const iterator iter) {
61825       return remove(iter - _data);
61826     }
61827 
61828     //@}
61829     //----------------------------------
61830     //
61831     //! \name Data Input
61832     //@{
61833     //----------------------------------
61834 
61835     //! Display a simple interactive interface to select images or sublists.
61836     /**
61837        \param disp Window instance to display selection and user interface.
61838        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
61839        \param axis Axis along whom images are appended for visualization.
61840        \param align Alignment setting when images have not all the same size.
61841        \param exit_on_anykey Exit function when any key is pressed.
61842        \return A one-column vector containing the selected image indexes.
61843     **/
61844     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
61845                           const char axis='x', const float align=0,
61846                           const bool exit_on_anykey=false) const {
61847       return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
61848     }
61849 
61850     //! Display a simple interactive interface to select images or sublists.
61851     /**
61852        \param title Title of a new window used to display selection and user interface.
61853        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
61854        \param axis Axis along whom images are appended for visualization.
61855        \param align Alignment setting when images have not all the same size.
61856        \param exit_on_anykey Exit function when any key is pressed.
61857        \return A one-column vector containing the selected image indexes.
61858     **/
61859     CImg<intT> get_select(const char *const title, const bool feature_type=true,
61860                           const char axis='x', const float align=0,
61861                           const bool exit_on_anykey=false) const {
61862       CImgDisplay disp;
61863       return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
61864     }
61865 
61866     CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
61867                        const char axis, const float align, const bool exit_on_anykey,
61868                        const unsigned int orig, const bool resize_disp,
61869                        const bool exit_on_rightbutton, const bool exit_on_wheel) const {
61870       if (is_empty())
61871         throw CImgInstanceException(_cimglist_instance
61872                                     "select(): Empty instance.",
61873                                     cimglist_instance);
61874 
61875       // Create image correspondence table and get list dimensions for visualization.
61876       CImgList<uintT> _indices;
61877       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
61878       cimglist_for(*this,l) {
61879         const CImg<T>& img = _data[l];
61880         const unsigned int
61881           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
61882           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
61883         if (w>max_width) max_width = w;
61884         if (h>max_height) max_height = h;
61885         sum_width+=w; sum_height+=h;
61886         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
61887         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
61888       }
61889       const CImg<uintT> indices0 = _indices>'x';
61890 
61891       // Create display window.
61892       if (!disp) {
61893         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
61894         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
61895         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
61896       } else {
61897         if (title) disp.set_title("%s",title);
61898         disp.move_inside_screen();
61899       }
61900       if (resize_disp) {
61901         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
61902         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
61903       }
61904 
61905       const unsigned int old_normalization = disp.normalization();
61906       bool old_is_resized = disp.is_resized();
61907       disp._normalization = 0;
61908       disp.show().set_key(0).show_mouse();
61909       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
61910 
61911       // Enter event loop.
61912       CImg<ucharT> visu0, visu;
61913       CImg<uintT> indices;
61914       CImg<intT> positions(_width,4,1,1,-1);
61915       int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1;
61916       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
61917       unsigned int key = 0, font_size = 32;
61918 
61919       while (!is_selected && !disp.is_closed() && !key) {
61920 
61921         // Create background image.
61922         if (!visu0) {
61923           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
61924           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
61925           unsigned int _ind = 0;
61926           const CImg<T> onexone(1,1,1,1,(T)0);
61927           if (axis=='x')
61928             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
61929             cimglist_for(*this,ind) {
61930               unsigned int x0 = 0;
61931               while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
61932               unsigned int x1 = x0;
61933               while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
61934               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
61935               CImg<ucharT> res;
61936               src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2).
61937                 move_to(res);
61938               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
61939               res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
61940               positions(ind,0) = positions(ind,2) = (int)x0;
61941               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
61942               positions(ind,2)+=res._width;
61943               positions(ind,3)+=res._height - 1;
61944               visu0.draw_image(positions(ind,0),positions(ind,1),res);
61945             }
61946           else
61947             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
61948             cimglist_for(*this,ind) {
61949               unsigned int y0 = 0;
61950               while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
61951               unsigned int y1 = y0;
61952               while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
61953               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
61954               CImg<ucharT> res;
61955               src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
61956                 move_to(res);
61957               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
61958               res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
61959               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
61960               positions(ind,1) = positions(ind,3) = (int)y0;
61961               positions(ind,2)+=res._width - 1;
61962               positions(ind,3)+=res._height;
61963               visu0.draw_image(positions(ind,0),positions(ind,1),res);
61964             }
61965           if (axis=='x') --positions(_ind,2); else --positions(_ind,3);
61966           update_display = true;
61967         }
61968 
61969         if (!visu || oindex0!=index0 || oindex1!=index1) {
61970           if (index0>=0 && index1>=0) {
61971             visu.assign(visu0,false);
61972             const int indm = std::min(index0,index1), indM = std::max(index0,index1);
61973             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
61974                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
61975                                     background_color,0.2f);
61976                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
61977                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
61978                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
61979                                       foreground_color,0.9f,0xAAAAAAAA);
61980               }
61981             if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down,
61982                                              orig + indm,orig + indM,indM - indm + 1);
61983             else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down,
61984                                   orig + index0,
61985                                   _data[index0]._width,
61986                                   _data[index0]._height,
61987                                   _data[index0]._depth,
61988                                   _data[index0]._spectrum);
61989             update_display = true;
61990           } else visu.assign();
61991         }
61992         if (!visu) { visu.assign(visu0,true); update_display = true; }
61993         if (update_display) { visu.display(disp); update_display = false; }
61994         disp.wait();
61995 
61996         // Manage user events.
61997         const int xm = disp.mouse_x(), ym = disp.mouse_y();
61998         int index = -1;
61999 
62000         if (xm>=0) {
62001           index = (int)indices(axis=='x'?xm:ym);
62002           if (disp.button()&1) {
62003             if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; }
62004             oindex1 = index1; index1 = index;
62005             if (!feature_type) is_selected = true;
62006           } else {
62007             if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; }
62008             else is_selected = true;
62009           }
62010         } else {
62011           if (is_clicked) {
62012             if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; }
62013             else index1 = -1;
62014           } else index0 = index1 = -1;
62015         }
62016 
62017         if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; }
62018         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; }
62019         if (disp.wheel() && exit_on_wheel) is_selected = true;
62020 
62021         CImg<charT> filename(32);
62022         switch (key = disp.key()) {
62023 #if cimg_OS!=2
62024         case cimg::keyCTRLRIGHT :
62025 #endif
62026         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
62027         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62028             disp.set_fullscreen(false).
62029               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
62030                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
62031               _is_resized = true;
62032             disp.set_key(key,false); key = 0; visu0.assign();
62033           } break;
62034         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62035             disp.set_fullscreen(false).
62036               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
62037             disp.set_key(key,false); key = 0; visu0.assign();
62038           } break;
62039         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62040             disp.set_fullscreen(false).
62041               resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
62042               _is_resized = true;
62043             disp.set_key(key,false); key = 0; visu0.assign();
62044           } break;
62045         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62046             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
62047             disp.set_key(key,false); key = 0; visu0.assign();
62048           } break;
62049         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62050             static unsigned int snap_number = 0;
62051             std::FILE *file;
62052             do {
62053               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
62054               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
62055             } while (file);
62056             if (visu0) {
62057               (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp);
62058               visu0.save(filename);
62059               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
62060             }
62061             disp.set_key(key,false).wait(); key = 0;
62062           } break;
62063         case cimg::keyO :
62064           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
62065             static unsigned int snap_number = 0;
62066             std::FILE *file;
62067             do {
62068 #ifdef cimg_use_zlib
62069               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
62070 #else
62071               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
62072 #endif
62073               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
62074             } while (file);
62075             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
62076             save(filename);
62077             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
62078             disp.set_key(key,false).wait(); key = 0;
62079           } break;
62080         }
62081         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
62082         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
62083         else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
62084         if (!exit_on_anykey && key && key!=cimg::keyESC &&
62085             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
62086           key = 0;
62087         }
62088       }
62089       CImg<intT> res(1,2,1,1,-1);
62090       if (is_selected) {
62091         if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1));
62092         else res.fill(index0);
62093       }
62094       if (!(disp.button()&2)) disp.set_button();
62095       disp._normalization = old_normalization;
62096       disp._is_resized = old_is_resized;
62097       disp.set_key(key);
62098       return res;
62099     }
62100 
62101     //! Load a list from a file.
62102     /**
62103      \param filename Filename to read data from.
62104     **/
62105     CImgList<T>& load(const char *const filename) {
62106       if (!filename)
62107         throw CImgArgumentException(_cimglist_instance
62108                                     "load(): Specified filename is (null).",
62109                                     cimglist_instance);
62110 
62111       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
62112         CImg<charT> filename_local(256);
62113         load(cimg::load_network(filename,filename_local));
62114         std::remove(filename_local);
62115         return *this;
62116       }
62117 
62118       const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
62119       const char *const ext = cimg::split_filename(filename);
62120       const unsigned int omode = cimg::exception_mode();
62121       cimg::exception_mode(0);
62122       bool is_loaded = true;
62123       try {
62124 #ifdef cimglist_load_plugin
62125         cimglist_load_plugin(filename);
62126 #endif
62127 #ifdef cimglist_load_plugin1
62128         cimglist_load_plugin1(filename);
62129 #endif
62130 #ifdef cimglist_load_plugin2
62131         cimglist_load_plugin2(filename);
62132 #endif
62133 #ifdef cimglist_load_plugin3
62134         cimglist_load_plugin3(filename);
62135 #endif
62136 #ifdef cimglist_load_plugin4
62137         cimglist_load_plugin4(filename);
62138 #endif
62139 #ifdef cimglist_load_plugin5
62140         cimglist_load_plugin5(filename);
62141 #endif
62142 #ifdef cimglist_load_plugin6
62143         cimglist_load_plugin6(filename);
62144 #endif
62145 #ifdef cimglist_load_plugin7
62146         cimglist_load_plugin7(filename);
62147 #endif
62148 #ifdef cimglist_load_plugin8
62149         cimglist_load_plugin8(filename);
62150 #endif
62151         if (!cimg::strcasecmp(ext,"tif") ||
62152             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
62153         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
62154         else if (!cimg::strcasecmp(ext,"cimg") ||
62155                  !cimg::strcasecmp(ext,"cimgz") ||
62156                  !*ext) load_cimg(filename);
62157         else if (!cimg::strcasecmp(ext,"rec") ||
62158                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
62159         else if (!cimg::strcasecmp(ext,"avi") ||
62160                  !cimg::strcasecmp(ext,"mov") ||
62161                  !cimg::strcasecmp(ext,"asf") ||
62162                  !cimg::strcasecmp(ext,"divx") ||
62163                  !cimg::strcasecmp(ext,"flv") ||
62164                  !cimg::strcasecmp(ext,"mpg") ||
62165                  !cimg::strcasecmp(ext,"m1v") ||
62166                  !cimg::strcasecmp(ext,"m2v") ||
62167                  !cimg::strcasecmp(ext,"m4v") ||
62168                  !cimg::strcasecmp(ext,"mjp") ||
62169                  !cimg::strcasecmp(ext,"mp4") ||
62170                  !cimg::strcasecmp(ext,"mkv") ||
62171                  !cimg::strcasecmp(ext,"mpe") ||
62172                  !cimg::strcasecmp(ext,"movie") ||
62173                  !cimg::strcasecmp(ext,"ogm") ||
62174                  !cimg::strcasecmp(ext,"ogg") ||
62175                  !cimg::strcasecmp(ext,"ogv") ||
62176                  !cimg::strcasecmp(ext,"qt") ||
62177                  !cimg::strcasecmp(ext,"rm") ||
62178                  !cimg::strcasecmp(ext,"vob") ||
62179                  !cimg::strcasecmp(ext,"webm") ||
62180                  !cimg::strcasecmp(ext,"wmv") ||
62181                  !cimg::strcasecmp(ext,"xvid") ||
62182                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
62183         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
62184         else is_loaded = false;
62185       } catch (CImgIOException&) { is_loaded = false; }
62186 
62187       // If nothing loaded, try to guess file format from magic number in file.
62188       if (!is_loaded && !is_stdin) {
62189         std::FILE *const file = cimg::std_fopen(filename,"rb");
62190         if (!file) {
62191           cimg::exception_mode(omode);
62192           throw CImgIOException(_cimglist_instance
62193                                 "load(): Failed to open file '%s'.",
62194                                 cimglist_instance,
62195                                 filename);
62196         }
62197 
62198         const char *const f_type = cimg::ftype(file,filename);
62199         cimg::fclose(file);
62200         is_loaded = true;
62201         try {
62202           if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
62203           else if (!cimg::strcasecmp(f_type,"tif") &&
62204                    cimg::strcasecmp(ext,"nef") &&
62205                    cimg::strcasecmp(ext,"dng")) load_tiff(filename);
62206           else is_loaded = false;
62207         } catch (CImgIOException&) { is_loaded = false; }
62208       }
62209 
62210       // If nothing loaded, try to load file as a single image.
62211       if (!is_loaded) {
62212         assign(1);
62213         try {
62214           _data->load(filename);
62215         } catch (CImgIOException&) {
62216           cimg::exception_mode(omode);
62217           throw CImgIOException(_cimglist_instance
62218                                 "load(): Failed to recognize format of file '%s'.",
62219                                 cimglist_instance,
62220                                 filename);
62221         }
62222       }
62223       cimg::exception_mode(omode);
62224       return *this;
62225     }
62226 
62227     //! Load a list from a file \newinstance.
62228     static CImgList<T> get_load(const char *const filename) {
62229       return CImgList<T>().load(filename);
62230     }
62231 
62232     //! Load a list from a .cimg file.
62233     /**
62234       \param filename Filename to read data from.
62235     **/
62236     CImgList<T>& load_cimg(const char *const filename) {
62237       return _load_cimg(0,filename);
62238     }
62239 
62240     //! Load a list from a .cimg file \newinstance.
62241     static CImgList<T> get_load_cimg(const char *const filename) {
62242       return CImgList<T>().load_cimg(filename);
62243     }
62244 
62245     //! Load a list from a .cimg file.
62246     /**
62247       \param file File to read data from.
62248     **/
62249     CImgList<T>& load_cimg(std::FILE *const file) {
62250       return _load_cimg(file,0);
62251     }
62252 
62253     //! Load a list from a .cimg file \newinstance.
62254     static CImgList<T> get_load_cimg(std::FILE *const file) {
62255       return CImgList<T>().load_cimg(file);
62256     }
62257 
62258     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
62259 #ifdef cimg_use_zlib
62260 #define _cimgz_load_cimg_case(Tss) { \
62261    Bytef *const cbuf = new Bytef[csiz]; \
62262    cimg::fread(cbuf,(size_t)csiz,nfile); \
62263    if (is_bool) { \
62264      CImg<ucharT> raw(W*H*D*C/8); \
62265      uLongf destlen = (uLongf)raw.size(); \
62266      uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
62267      img.assign(W,H,D,C); \
62268      img._uchar2bool(raw,raw.size(),false); \
62269    } else { \
62270      CImg<Tss> raw(W,H,D,C); \
62271      uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \
62272      uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
62273      if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
62274      raw.move_to(img); \
62275    } \
62276    delete[] cbuf; \
62277 }
62278 #else
62279 #define _cimgz_load_cimg_case(Tss) \
62280    throw CImgIOException(_cimglist_instance \
62281                          "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
62282                          cimglist_instance, \
62283                          filename?filename:"(FILE*)");
62284 #endif
62285 
62286 #define _cimg_load_cimg_case(Ts,Tss) \
62287       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
62288         const bool is_bool = cimg::type<Tss>::string()==cimg::type<bool>::string(); \
62289         for (unsigned int l = 0; l<N; ++l) { \
62290           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
62291           W = H = D = C = 0; csiz = 0; \
62292           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
62293             throw CImgIOException(_cimglist_instance \
62294                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
62295                                   cimglist_instance, \
62296                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
62297           if (W*H*D*C>0) { \
62298             CImg<T> &img = _data[l]; \
62299             if (err==5) _cimgz_load_cimg_case(Tss) \
62300             else { \
62301               img.assign(W,H,D,C); \
62302               T *ptrd = img._data; \
62303               if (is_bool) { \
62304                 CImg<ucharT> raw; \
62305                 for (ulongT to_read = img.size(); to_read; ) { \
62306                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
62307                   cimg::fread(raw._data,raw._width,nfile); \
62308                   CImg<T>(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\
62309                     _uchar2bool(raw,raw._width,false); \
62310                   to_read-=raw._width; \
62311                 } \
62312               } else { \
62313                 CImg<Tss> raw; \
62314                 for (ulongT to_read = img.size(); to_read; ) { \
62315                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
62316                   cimg::fread(raw._data,raw._width,nfile); \
62317                   if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
62318                   const Tss *ptrs = raw._data; \
62319                   for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
62320                   to_read-=raw._width; \
62321                 } \
62322               } \
62323             } \
62324           } \
62325         } \
62326         loaded = true; \
62327       }
62328 
62329       if (!filename && !file)
62330         throw CImgArgumentException(_cimglist_instance
62331                                     "load_cimg(): Specified filename is (null).",
62332                                     cimglist_instance);
62333 
62334       const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
62335       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
62336       bool loaded = false, endian = cimg::endianness();
62337       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
62338       *tmp = *str_pixeltype = *str_endian = 0;
62339       unsigned int j, N = 0, W, H, D, C;
62340       cimg_uint64 csiz;
62341       int i, err;
62342       do {
62343         j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
62344       } while (*tmp=='#' && i>=0);
62345       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
62346                         &N,str_pixeltype._data,str_endian._data);
62347       if (err<2) {
62348         if (!file) cimg::fclose(nfile);
62349         throw CImgIOException(_cimglist_instance
62350                               "load_cimg(): File or CImg header not found in file '%s'.",
62351                               cimglist_instance,
62352                               filename?filename:"(FILE*)");
62353       }
62354       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
62355       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
62356       assign(N);
62357       _cimg_load_cimg_case("bool",bool);
62358       _cimg_load_cimg_case("unsigned_char",unsigned char);
62359       _cimg_load_cimg_case("uchar",unsigned char);
62360       _cimg_load_cimg_case("char",char);
62361       _cimg_load_cimg_case("unsigned_short",unsigned short);
62362       _cimg_load_cimg_case("ushort",unsigned short);
62363       _cimg_load_cimg_case("short",short);
62364       _cimg_load_cimg_case("unsigned_int",unsigned int);
62365       _cimg_load_cimg_case("uint",unsigned int);
62366       _cimg_load_cimg_case("int",int);
62367       _cimg_load_cimg_case("unsigned_long",ulongT);
62368       _cimg_load_cimg_case("ulong",ulongT);
62369       _cimg_load_cimg_case("long",longT);
62370       _cimg_load_cimg_case("unsigned_int64",uint64T);
62371       _cimg_load_cimg_case("uint64",uint64T);
62372       _cimg_load_cimg_case("int64",int64T);
62373       _cimg_load_cimg_case("float",float);
62374       _cimg_load_cimg_case("double",double);
62375 
62376       if (!loaded) {
62377         if (!file) cimg::fclose(nfile);
62378         throw CImgIOException(_cimglist_instance
62379                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
62380                               cimglist_instance,
62381                               str_pixeltype._data,filename?filename:"(FILE*)");
62382       }
62383       if (!file) cimg::fclose(nfile);
62384       return *this;
62385     }
62386 
62387     //! Load a sublist list from a (non compressed) .cimg file.
62388     /**
62389       \param filename Filename to read data from.
62390       \param n0 Starting index of images to read (~0U for max).
62391       \param n1 Ending index of images to read (~0U for max).
62392       \param x0 Starting X-coordinates of image regions to read.
62393       \param y0 Starting Y-coordinates of image regions to read.
62394       \param z0 Starting Z-coordinates of image regions to read.
62395       \param c0 Starting C-coordinates of image regions to read.
62396       \param x1 Ending X-coordinates of image regions to read (~0U for max).
62397       \param y1 Ending Y-coordinates of image regions to read (~0U for max).
62398       \param z1 Ending Z-coordinates of image regions to read (~0U for max).
62399       \param c1 Ending C-coordinates of image regions to read (~0U for max).
62400     **/
62401     CImgList<T>& load_cimg(const char *const filename,
62402                            const unsigned int n0, const unsigned int n1,
62403                            const unsigned int x0, const unsigned int y0,
62404                            const unsigned int z0, const unsigned int c0,
62405                            const unsigned int x1, const unsigned int y1,
62406                            const unsigned int z1, const unsigned int c1) {
62407       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
62408     }
62409 
62410     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
62411     static CImgList<T> get_load_cimg(const char *const filename,
62412                                      const unsigned int n0, const unsigned int n1,
62413                                      const unsigned int x0, const unsigned int y0,
62414                                      const unsigned int z0, const unsigned int c0,
62415                                      const unsigned int x1, const unsigned int y1,
62416                                      const unsigned int z1, const unsigned int c1) {
62417       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
62418     }
62419 
62420     //! Load a sub-image list from a (non compressed) .cimg file \overloading.
62421     CImgList<T>& load_cimg(std::FILE *const file,
62422                            const unsigned int n0, const unsigned int n1,
62423                            const unsigned int x0, const unsigned int y0,
62424                            const unsigned int z0, const unsigned int c0,
62425                            const unsigned int x1, const unsigned int y1,
62426                            const unsigned int z1, const unsigned int c1) {
62427       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
62428     }
62429 
62430     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
62431     static CImgList<T> get_load_cimg(std::FILE *const file,
62432                                      const unsigned int n0, const unsigned int n1,
62433                                      const unsigned int x0, const unsigned int y0,
62434                                      const unsigned int z0, const unsigned int c0,
62435                                      const unsigned int x1, const unsigned int y1,
62436                                      const unsigned int z1, const unsigned int c1) {
62437       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
62438     }
62439 
62440     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
62441                             const unsigned int n0, const unsigned int n1,
62442                             const unsigned int x0, const unsigned int y0,
62443                             const unsigned int z0, const unsigned int c0,
62444                             const unsigned int x1, const unsigned int y1,
62445                             const unsigned int z1, const unsigned int c1) {
62446 #define _cimg_load_cimg_case2(Ts,Tss) \
62447       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
62448         for (unsigned int l = 0; l<=nn1; ++l) { \
62449           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
62450           W = H = D = C = 0; \
62451           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
62452             throw CImgIOException(_cimglist_instance \
62453                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
62454                                   cimglist_instance, \
62455                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
62456           if (W*H*D*C>0) { \
62457             if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
62458             else { \
62459               const unsigned int \
62460                 _nx1 = nx1==~0U?W - 1:nx1, \
62461                 _ny1 = ny1==~0U?H - 1:ny1, \
62462                 _nz1 = nz1==~0U?D - 1:nz1, \
62463                 _nc1 = nc1==~0U?C - 1:nc1; \
62464               if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
62465                 throw CImgArgumentException(_cimglist_instance \
62466                                             "load_cimg(): Invalid specified coordinates " \
62467                                             "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
62468                                             "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
62469                                             cimglist_instance, \
62470                                             n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
62471               CImg<Tss> raw(1 + _nx1 - nx0); \
62472               CImg<T> &img = _data[l - nn0]; \
62473               img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
62474               T *ptrd = img._data; \
62475               ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
62476               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
62477               for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
62478                 const ulongT skipzb = nz0*W*H*sizeof(Tss); \
62479                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
62480                 for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
62481                   const ulongT skipyb = ny0*W*sizeof(Tss); \
62482                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
62483                   for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
62484                     const ulongT skipxb = nx0*sizeof(Tss); \
62485                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
62486                     cimg::fread(raw._data,raw._width,nfile); \
62487                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
62488                     const Tss *ptrs = raw._data; \
62489                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
62490                     const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
62491                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
62492                   } \
62493                   const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
62494                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
62495                 } \
62496                 const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
62497                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
62498               } \
62499               const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
62500               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
62501             } \
62502           } \
62503         } \
62504         loaded = true; \
62505       }
62506 
62507       if (!filename && !file)
62508         throw CImgArgumentException(_cimglist_instance
62509                                     "load_cimg(): Specified filename is (null).",
62510                                     cimglist_instance);
62511       unsigned int
62512         nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
62513         nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
62514         ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
62515         nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
62516         nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
62517 
62518       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
62519       bool loaded = false, endian = cimg::endianness();
62520       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
62521       *tmp = *str_pixeltype = *str_endian = 0;
62522       unsigned int j, N, W, H, D, C;
62523       int i, err;
62524       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
62525       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
62526                         &N,str_pixeltype._data,str_endian._data);
62527       if (err<2) {
62528         if (!file) cimg::fclose(nfile);
62529         throw CImgIOException(_cimglist_instance
62530                               "load_cimg(): CImg header not found in file '%s'.",
62531                               cimglist_instance,
62532                               filename?filename:"(FILE*)");
62533       }
62534       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
62535       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
62536       nn1 = n1==~0U?N - 1:n1;
62537       if (nn1>=N)
62538         throw CImgArgumentException(_cimglist_instance
62539                                     "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
62540                                     "because file '%s' contains only %u images.",
62541                                     cimglist_instance,
62542                                     n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
62543       assign(1 + nn1 - n0);
62544       _cimg_load_cimg_case2("bool",bool);
62545       _cimg_load_cimg_case2("unsigned_char",unsigned char);
62546       _cimg_load_cimg_case2("uchar",unsigned char);
62547       _cimg_load_cimg_case2("char",char);
62548       _cimg_load_cimg_case2("unsigned_short",unsigned short);
62549       _cimg_load_cimg_case2("ushort",unsigned short);
62550       _cimg_load_cimg_case2("short",short);
62551       _cimg_load_cimg_case2("unsigned_int",unsigned int);
62552       _cimg_load_cimg_case2("uint",unsigned int);
62553       _cimg_load_cimg_case2("int",int);
62554       _cimg_load_cimg_case2("unsigned_long",ulongT);
62555       _cimg_load_cimg_case2("ulong",ulongT);
62556       _cimg_load_cimg_case2("long",longT);
62557       _cimg_load_cimg_case2("unsigned_int64",uint64T);
62558       _cimg_load_cimg_case2("uint64",uint64T);
62559       _cimg_load_cimg_case2("int64",int64T);
62560       _cimg_load_cimg_case2("float",float);
62561       _cimg_load_cimg_case2("double",double);
62562       if (!loaded) {
62563         if (!file) cimg::fclose(nfile);
62564         throw CImgIOException(_cimglist_instance
62565                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
62566                               cimglist_instance,
62567                               str_pixeltype._data,filename?filename:"(FILE*)");
62568       }
62569       if (!file) cimg::fclose(nfile);
62570       return *this;
62571     }
62572 
62573     //! Load a list from a PAR/REC (Philips) file.
62574     /**
62575       \param filename Filename to read data from.
62576     **/
62577     CImgList<T>& load_parrec(const char *const filename) {
62578       if (!filename)
62579         throw CImgArgumentException(_cimglist_instance
62580                                     "load_parrec(): Specified filename is (null).",
62581                                     cimglist_instance);
62582 
62583       CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
62584       *body = *filenamepar = *filenamerec = 0;
62585       const char *const ext = cimg::split_filename(filename,body);
62586       if (!std::strcmp(ext,"par")) {
62587         std::strncpy(filenamepar,filename,filenamepar._width - 1);
62588         cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
62589       }
62590       if (!std::strcmp(ext,"PAR")) {
62591         std::strncpy(filenamepar,filename,filenamepar._width - 1);
62592         cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
62593       }
62594       if (!std::strcmp(ext,"rec")) {
62595         std::strncpy(filenamerec,filename,filenamerec._width - 1);
62596         cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
62597       }
62598       if (!std::strcmp(ext,"REC")) {
62599         std::strncpy(filenamerec,filename,filenamerec._width - 1);
62600         cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
62601       }
62602       std::FILE *file = cimg::fopen(filenamepar,"r");
62603 
62604       // Parse header file
62605       CImgList<floatT> st_slices;
62606       CImgList<uintT> st_global;
62607       CImg<charT> line(256); *line = 0;
62608       int err;
62609       do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
62610       do {
62611         unsigned int sn,size_x,size_y,pixsize;
62612         float rs,ri,ss;
62613         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);
62614         if (err==7) {
62615           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
62616           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
62617           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
62618           else {
62619             CImg<uintT> &vec = st_global[i];
62620             if (size_x>vec[0]) vec[0] = size_x;
62621             if (size_y>vec[1]) vec[1] = size_y;
62622             vec[2] = sn;
62623           }
62624           st_slices[st_slices._width - 1][7] = (float)i;
62625         }
62626       } while (err==7);
62627 
62628       // Read data
62629       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
62630       cimglist_for(st_global,l) {
62631         const CImg<uintT>& vec = st_global[l];
62632         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
62633       }
62634 
62635       cimglist_for(st_slices,l) {
62636         const CImg<floatT>& vec = st_slices[l];
62637         const unsigned int
62638           sn = (unsigned int)vec[0] - 1,
62639           pixsize = (unsigned int)vec[1],
62640           size_x = (unsigned int)vec[2],
62641           size_y = (unsigned int)vec[3],
62642           imn = (unsigned int)vec[7];
62643         const float ri = vec[4], rs = vec[5], ss = vec[6];
62644         switch (pixsize) {
62645         case 8 : {
62646           CImg<ucharT> buf(size_x,size_y);
62647           cimg::fread(buf._data,size_x*size_y,file2);
62648           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62649           CImg<T>& img = (*this)[imn];
62650           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62651         } break;
62652         case 16 : {
62653           CImg<ushortT> buf(size_x,size_y);
62654           cimg::fread(buf._data,size_x*size_y,file2);
62655           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62656           CImg<T>& img = (*this)[imn];
62657           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62658         } break;
62659         case 32 : {
62660           CImg<uintT> buf(size_x,size_y);
62661           cimg::fread(buf._data,size_x*size_y,file2);
62662           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
62663           CImg<T>& img = (*this)[imn];
62664           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
62665         } break;
62666         default :
62667           cimg::fclose(file);
62668           cimg::fclose(file2);
62669           throw CImgIOException(_cimglist_instance
62670                                 "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
62671                                 cimglist_instance,
62672                                 pixsize,filename);
62673         }
62674       }
62675       cimg::fclose(file);
62676       cimg::fclose(file2);
62677       if (!_width)
62678         throw CImgIOException(_cimglist_instance
62679                               "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
62680                               cimglist_instance,
62681                               filename);
62682       return *this;
62683     }
62684 
62685     //! Load a list from a PAR/REC (Philips) file \newinstance.
62686     static CImgList<T> get_load_parrec(const char *const filename) {
62687       return CImgList<T>().load_parrec(filename);
62688     }
62689 
62690     //! Load a list from a YUV image sequence file.
62691     /**
62692         \param filename Filename to read data from.
62693         \param size_x Width of the images.
62694         \param size_y Height of the images.
62695         \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
62696         \param first_frame Index of first image frame to read.
62697         \param last_frame Index of last image frame to read.
62698         \param step_frame Step applied between each frame.
62699         \param yuv2rgb Apply YUV to RGB transformation during reading.
62700     **/
62701     CImgList<T>& load_yuv(const char *const filename,
62702                           const unsigned int size_x, const unsigned int size_y,
62703                           const unsigned int chroma_subsampling=444,
62704                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62705                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
62706       return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
62707                        first_frame,last_frame,step_frame,yuv2rgb);
62708     }
62709 
62710     //! Load a list from a YUV image sequence file \newinstance.
62711     static CImgList<T> get_load_yuv(const char *const filename,
62712                                     const unsigned int size_x, const unsigned int size_y=1,
62713                                     const unsigned int chroma_subsampling=444,
62714                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62715                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
62716       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
62717                                     first_frame,last_frame,step_frame,yuv2rgb);
62718     }
62719 
62720     //! Load a list from an image sequence YUV file \overloading.
62721     CImgList<T>& load_yuv(std::FILE *const file,
62722                           const unsigned int size_x, const unsigned int size_y,
62723                           const unsigned int chroma_subsampling=444,
62724                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62725                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
62726       return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
62727                        first_frame,last_frame,step_frame,yuv2rgb);
62728     }
62729 
62730     //! Load a list from an image sequence YUV file \newinstance.
62731     static CImgList<T> get_load_yuv(std::FILE *const file,
62732                                     const unsigned int size_x, const unsigned int size_y=1,
62733                                     const unsigned int chroma_subsampling=444,
62734                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62735                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
62736       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
62737                                     first_frame,last_frame,step_frame,yuv2rgb);
62738     }
62739 
62740     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
62741                            const unsigned int size_x, const unsigned int size_y,
62742                            const unsigned int chroma_subsampling,
62743                            const unsigned int first_frame, const unsigned int last_frame,
62744                            const unsigned int step_frame, const bool yuv2rgb) {
62745       if (!filename && !file)
62746         throw CImgArgumentException(_cimglist_instance
62747                                     "load_yuv(): Specified filename is (null).",
62748                                     cimglist_instance);
62749       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
62750         throw CImgArgumentException(_cimglist_instance
62751                                     "load_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
62752                                     cimglist_instance,
62753                                     chroma_subsampling,filename?filename:"(FILE*)");
62754       const unsigned int
62755         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
62756         cfy = chroma_subsampling==420?2:1,
62757         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
62758         nlast_frame = first_frame<last_frame?last_frame:first_frame,
62759         nstep_frame = step_frame?step_frame:1;
62760 
62761       if (!size_x || !size_y || size_x%cfx || size_y%cfy)
62762         throw CImgArgumentException(_cimglist_instance
62763                                     "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
62764                                     cimglist_instance,
62765                                     size_x,size_y,filename?filename:"(FILE*)");
62766 
62767       CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
62768       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
62769       bool stop_flag = false;
62770       int err;
62771       if (nfirst_frame) {
62772         err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
62773         if (err) {
62774           if (!file) cimg::fclose(nfile);
62775           throw CImgIOException(_cimglist_instance
62776                                 "load_yuv(): File '%s' doesn't contain frame number %u.",
62777                                 cimglist_instance,
62778                                 filename?filename:"(FILE*)",nfirst_frame);
62779         }
62780       }
62781       unsigned int frame;
62782       for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
62783         YUV.get_shared_channel(0).fill(0);
62784         // *TRY* to read the luminance part, do not replace by cimg::fread!
62785         err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
62786         if (err!=(int)(YUV._width*YUV._height)) {
62787           stop_flag = true;
62788           if (err>0)
62789             cimg::warn(_cimglist_instance
62790                        "load_yuv(): File '%s' contains incomplete data or given image dimensions "
62791                        "(%u,%u) are incorrect.",
62792                        cimglist_instance,
62793                        filename?filename:"(FILE*)",size_x,size_y);
62794         } else {
62795           UV.fill(0);
62796           // *TRY* to read the luminance part, do not replace by cimg::fread!
62797           err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
62798           if (err!=(int)(UV.size())) {
62799             stop_flag = true;
62800             if (err>0)
62801               cimg::warn(_cimglist_instance
62802                          "load_yuv(): File '%s' contains incomplete data or given image dimensions "
62803                          "(%u,%u) are incorrect.",
62804                          cimglist_instance,
62805                          filename?filename:"(FILE*)",size_x,size_y);
62806           } else {
62807             const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
62808             ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
62809             const unsigned int wd = YUV._width;
62810             switch (chroma_subsampling) {
62811             case 420 :
62812               cimg_forY(UV,y) {
62813                 cimg_forX(UV,x) {
62814                   const ucharT U = *(ptrs1++), V = *(ptrs2++);
62815                   ptrd1[wd] = U; *(ptrd1)++ = U;
62816                   ptrd1[wd] = U; *(ptrd1)++ = U;
62817                   ptrd2[wd] = V; *(ptrd2)++ = V;
62818                   ptrd2[wd] = V; *(ptrd2)++ = V;
62819                 }
62820                 ptrd1+=wd; ptrd2+=wd;
62821               }
62822               break;
62823             case 422 :
62824               cimg_forXY(UV,x,y) {
62825                 const ucharT U = *(ptrs1++), V = *(ptrs2++);
62826                 *(ptrd1++) = U; *(ptrd1++) = U;
62827                 *(ptrd2++) = V; *(ptrd2++) = V;
62828               }
62829               break;
62830             default :
62831               YUV.draw_image(0,0,0,1,UV);
62832             }
62833             if (yuv2rgb) YUV.YCbCrtoRGB();
62834             insert(YUV);
62835             if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
62836           }
62837         }
62838       }
62839       if (is_empty())
62840         throw CImgIOException(_cimglist_instance
62841                               "load_yuv() : Missing data in file '%s'.",
62842                               cimglist_instance,
62843                               filename?filename:"(FILE*)");
62844       if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
62845         cimg::warn(_cimglist_instance
62846                    "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
62847                    cimglist_instance,
62848                    nlast_frame,frame - 1,filename?filename:"(FILE*)");
62849 
62850       if (!file) cimg::fclose(nfile);
62851       return *this;
62852     }
62853 
62854     //! Load an image from a video file, using OpenCV library.
62855     /**
62856       \param filename Filename, as a C-string.
62857       \param first_frame Index of the first frame to read.
62858       \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U').
62859       \param step_frame Step value for frame reading.
62860       \note If step_frame==0, the current video stream is forced to be released (without any frames read).
62861     **/
62862     CImgList<T>& load_video(const char *const filename,
62863                             const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62864                             const unsigned int step_frame=1) {
62865 #ifndef cimg_use_opencv
62866       if (first_frame || last_frame!=~0U || step_frame>1)
62867         throw CImgArgumentException(_cimglist_instance
62868                                     "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
62869                                     "and 'step_frame' requires features from the OpenCV library "
62870                                     "('-Dcimg_use_opencv' must be defined).",
62871                                     cimglist_instance,filename);
62872       return load_ffmpeg_external(filename);
62873 #else
62874       static cv::VideoCapture *captures[32] = { 0 };
62875       static CImgList<charT> filenames(32);
62876       static CImg<uintT> positions(32,1,1,1,0);
62877       static int last_used_index = -1;
62878 
62879       // Detect if a video capture already exists for the specified filename.
62880       cimg::mutex(9);
62881       int index = -1;
62882       if (filename) {
62883         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
62884           index = last_used_index;
62885         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
62886             index = l; break;
62887           }
62888       } else index = last_used_index;
62889       cimg::mutex(9,0);
62890 
62891       // Release stream if needed.
62892       if (!step_frame || (index>=0 && positions[index]>first_frame)) {
62893         if (index>=0) {
62894           cimg::mutex(9);
62895           captures[index]->release();
62896           delete captures[index];
62897           captures[index] = 0;
62898           positions[index] = 0;
62899           filenames[index].assign();
62900           if (last_used_index==index) last_used_index = -1;
62901           index = -1;
62902           cimg::mutex(9,0);
62903         } else
62904           if (filename)
62905             cimg::warn(_cimglist_instance
62906                        "load_video() : File '%s', no opened video stream associated with filename found.",
62907                        cimglist_instance,filename);
62908           else
62909             cimg::warn(_cimglist_instance
62910                        "load_video() : No opened video stream found.",
62911                        cimglist_instance,filename);
62912         if (!step_frame) return *this;
62913       }
62914 
62915       // Find empty slot for capturing video stream.
62916       if (index<0) {
62917         if (!filename)
62918           throw CImgArgumentException(_cimglist_instance
62919                                       "load_video(): No already open video reader found. You must specify a "
62920                                       "non-(null) filename argument for the first call.",
62921                                       cimglist_instance);
62922         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
62923         if (index<0)
62924           throw CImgIOException(_cimglist_instance
62925                                 "load_video(): File '%s', no video reader slots available. "
62926                                 "You have to release some of your previously opened videos.",
62927                                 cimglist_instance,filename);
62928         cimg::mutex(9);
62929         captures[index] = new cv::VideoCapture(filename);
62930         positions[index] = 0;
62931         if (!captures[index]->isOpened()) {
62932           delete captures[index];
62933           captures[index] = 0;
62934           cimg::mutex(9,0);
62935           cimg::fclose(cimg::fopen(filename,"rb"));  // Check file availability
62936           throw CImgIOException(_cimglist_instance
62937                                 "load_video(): File '%s', unable to detect format of video file.",
62938                                 cimglist_instance,filename);
62939         }
62940         CImg<charT>::string(filename).move_to(filenames[index]);
62941         cimg::mutex(9,0);
62942       }
62943 
62944       cimg::mutex(9);
62945       const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count));
62946       cimg::mutex(9,0);
62947       assign();
62948 
62949       // Skip frames if requested.
62950       bool go_on = true;
62951       unsigned int &pos = positions[index];
62952       while (pos<first_frame) {
62953         cimg::mutex(9);
62954         if (!captures[index]->grab()) { cimg::mutex(9,0); go_on = false; break; }
62955         cimg::mutex(9,0);
62956         ++pos;
62957       }
62958 
62959       // Read and convert frames.
62960       const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
62961       while (go_on && pos<=_last_frame) {
62962         cv::Mat cvimg;
62963         cimg::mutex(9);
62964         if (captures[index]->read(cvimg)) { CImg<T>::_cvmat2cimg(cvimg).move_to(*this); ++pos; }
62965         else go_on = false;
62966         cimg::mutex(9,0);
62967         if (go_on)
62968           for (unsigned int i = 1; go_on && i<step_frame && pos<=_last_frame; ++i, ++pos) {
62969             cimg::mutex(9);
62970             if (!captures[index]->grab()) go_on = false;
62971             cimg::mutex(9,0);
62972           }
62973       }
62974 
62975       if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary
62976         cimg::mutex(9);
62977         captures[index]->release();
62978         delete captures[index];
62979         captures[index] = 0;
62980         filenames[index].assign();
62981         positions[index] = 0;
62982         index = -1;
62983         cimg::mutex(9,0);
62984       }
62985 
62986       cimg::mutex(9);
62987       last_used_index = index;
62988       cimg::mutex(9,0);
62989 
62990       if (is_empty())
62991         throw CImgIOException(_cimglist_instance
62992                               "load_video(): File '%s', unable to locate frame %u.",
62993                               cimglist_instance,filename,first_frame);
62994       return *this;
62995 #endif
62996     }
62997 
62998     //! Load an image from a video file, using OpenCV library \newinstance.
62999     static CImgList<T> get_load_video(const char *const filename,
63000                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
63001                            const unsigned int step_frame=1) {
63002       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
63003     }
63004 
63005     //! Load an image from a video file using the external tool 'ffmpeg'.
63006     /**
63007       \param filename Filename to read data from.
63008     **/
63009     CImgList<T>& load_ffmpeg_external(const char *const filename) {
63010       if (!filename)
63011         throw CImgArgumentException(_cimglist_instance
63012                                     "load_ffmpeg_external(): Specified filename is (null).",
63013                                     cimglist_instance);
63014       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
63015       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
63016       std::FILE *file = 0;
63017       do {
63018         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63019                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63020         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
63021         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63022       } while (file);
63023       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
63024       cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"",
63025                     cimg::ffmpeg_path(),
63026                     CImg<charT>::string(filename)._system_strescape().data(),
63027                     CImg<charT>::string(filename_tmp2)._system_strescape().data());
63028       cimg::system(command, cimg::ffmpeg_path());
63029       const unsigned int omode = cimg::exception_mode();
63030       cimg::exception_mode(0);
63031       assign();
63032       unsigned int i = 1;
63033       for (bool stop_flag = false; !stop_flag; ++i) {
63034         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
63035         CImg<T> img;
63036         try { img.load_pnm(filename_tmp2); }
63037         catch (CImgException&) { stop_flag = true; }
63038         if (img) { img.move_to(*this); std::remove(filename_tmp2); }
63039       }
63040       cimg::exception_mode(omode);
63041       if (is_empty())
63042         throw CImgIOException(_cimglist_instance
63043                               "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
63044                               cimglist_instance,
63045                               filename);
63046       return *this;
63047     }
63048 
63049     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
63050     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
63051       return CImgList<T>().load_ffmpeg_external(filename);
63052     }
63053 
63054     //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
63055     /**
63056       \param filename Filename to read data from.
63057     **/
63058     CImgList<T>& load_gif_external(const char *const filename) {
63059       if (!filename)
63060         throw CImgArgumentException(_cimglist_instance
63061                                     "load_gif_external(): Specified filename is (null).",
63062                                     cimglist_instance);
63063       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
63064       if (!_load_gif_external(filename,false))
63065         if (!_load_gif_external(filename,true))
63066           try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
63067       if (is_empty())
63068         throw CImgIOException(_cimglist_instance
63069                               "load_gif_external(): Failed to open file '%s'.",
63070                               cimglist_instance,filename);
63071       return *this;
63072     }
63073 
63074     CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
63075       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
63076       std::FILE *file = 0;
63077       do {
63078         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63079                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63080         if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
63081         else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
63082         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63083       } while (file);
63084       if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
63085                                             cimg::graphicsmagick_path(),
63086                                             CImg<charT>::string(filename)._system_strescape().data(),
63087                                             CImg<charT>::string(filename_tmp)._system_strescape().data());
63088       else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"",
63089                          cimg::imagemagick_path(),
63090                          CImg<charT>::string(filename)._system_strescape().data(),
63091                          CImg<charT>::string(filename_tmp)._system_strescape().data());
63092       cimg::system(command, cimg::imagemagick_path());
63093       const unsigned int omode = cimg::exception_mode();
63094       cimg::exception_mode(0);
63095       assign();
63096 
63097       // Try to read a single frame gif.
63098       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
63099       CImg<T> img;
63100       try { img.load_png(filename_tmp2); }
63101       catch (CImgException&) { }
63102       if (img) { img.move_to(*this); std::remove(filename_tmp2); }
63103       else { // Try to read animated gif
63104         unsigned int i = 0;
63105         for (bool stop_flag = false; !stop_flag; ++i) {
63106           if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
63107           else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
63108           try { img.load_png(filename_tmp2); }
63109           catch (CImgException&) { stop_flag = true; }
63110           if (img) { img.move_to(*this); std::remove(filename_tmp2); }
63111         }
63112       }
63113       cimg::exception_mode(omode);
63114       return *this;
63115     }
63116 
63117     //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
63118     static CImgList<T> get_load_gif_external(const char *const filename) {
63119       return CImgList<T>().load_gif_external(filename);
63120     }
63121 
63122     //! Load a gzipped list, using external tool 'gunzip'.
63123     /**
63124       \param filename Filename to read data from.
63125     **/
63126     CImgList<T>& load_gzip_external(const char *const filename) {
63127       if (!filename)
63128         throw CImgIOException(_cimglist_instance
63129                               "load_gzip_external(): Specified filename is (null).",
63130                               cimglist_instance);
63131       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
63132       CImg<charT> command(1024), filename_tmp(256), body(256);
63133       const char
63134         *ext = cimg::split_filename(filename,body),
63135         *ext2 = cimg::split_filename(body,0);
63136       std::FILE *file = 0;
63137       do {
63138         if (!cimg::strcasecmp(ext,"gz")) {
63139           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
63140                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
63141           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63142                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63143         } else {
63144           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
63145                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
63146           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63147                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63148         }
63149         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
63150       } while (file);
63151       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
63152                     cimg::gunzip_path(),
63153                     CImg<charT>::string(filename)._system_strescape().data(),
63154                     CImg<charT>::string(filename_tmp)._system_strescape().data());
63155       cimg::system(command, cimg::gunzip_path());
63156       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
63157         cimg::fclose(cimg::fopen(filename,"r"));
63158         throw CImgIOException(_cimglist_instance
63159                               "load_gzip_external(): Failed to open file '%s'.",
63160                               cimglist_instance,
63161                               filename);
63162 
63163       } else cimg::fclose(file);
63164       load(filename_tmp);
63165       std::remove(filename_tmp);
63166       return *this;
63167     }
63168 
63169     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
63170     static CImgList<T> get_load_gzip_external(const char *const filename) {
63171       return CImgList<T>().load_gzip_external(filename);
63172     }
63173 
63174     //! Load images from a TIFF file.
63175     /**
63176         \param filename Filename to read data from.
63177         \param first_frame Index of first image frame to read.
63178         \param last_frame Index of last image frame to read.
63179         \param step_frame Step applied between each frame.
63180         \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
63181         \param[out] voxel_size Voxel size, as stored in the filename.
63182         \param[out] description Description, as stored in the filename.
63183     **/
63184     CImgList<T>& load_tiff(const char *const filename,
63185                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
63186                            const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
63187                            float *const voxel_size=0, CImg<charT> *const description=0) {
63188       const unsigned int
63189         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
63190         nstep_frame = step_frame?step_frame:1;
63191       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
63192 #ifndef cimg_use_tiff
63193       cimg::unused(bits_per_value,voxel_size,description);
63194       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
63195         throw CImgArgumentException(_cimglist_instance
63196                                     "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
63197                                     cimglist_instance,
63198                                     filename);
63199 
63200       return assign(CImg<T>::get_load_tiff(filename));
63201 #else
63202 #if cimg_verbosity<3
63203         TIFFSetWarningHandler(0);
63204         TIFFSetErrorHandler(0);
63205 #endif
63206       TIFF *tif = TIFFOpen(filename,"r");
63207       if (tif) {
63208         unsigned int nb_images = 0;
63209         do ++nb_images; while (TIFFReadDirectory(tif));
63210         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
63211           cimg::warn(_cimglist_instance
63212                      "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
63213                      "file '%s' contains %u image(s).",
63214                      cimglist_instance,
63215                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
63216 
63217         if (nfirst_frame>=nb_images) return assign();
63218         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
63219         assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
63220         TIFFSetDirectory(tif,0);
63221         cimglist_for(*this,l)
63222           _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description);
63223         TIFFClose(tif);
63224       } else throw CImgIOException(_cimglist_instance
63225                                    "load_tiff(): Failed to open file '%s'.",
63226                                    cimglist_instance,
63227                                    filename);
63228       return *this;
63229 #endif
63230     }
63231 
63232     //! Load a multi-page TIFF file \newinstance.
63233     static CImgList<T> get_load_tiff(const char *const filename,
63234                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
63235                                      const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
63236                                      float *const voxel_size=0, CImg<charT> *const description=0) {
63237       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
63238     }
63239 
63240     //@}
63241     //----------------------------------
63242     //
63243     //! \name Data Output
63244     //@{
63245     //----------------------------------
63246 
63247     //! Print information about the list on the standard output.
63248     /**
63249       \param title Label set to the information displayed.
63250       \param display_stats Tells if image statistics must be computed and displayed.
63251     **/
63252     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
63253       unsigned int msiz = 0;
63254       cimglist_for(*this,l) msiz+=_data[l].size();
63255       msiz*=sizeof(T);
63256       const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
63257       CImg<charT> _title(64);
63258       if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
63259       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
63260                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
63261                    cimg::t_bold,cimg::t_normal,(void*)this,
63262                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
63263                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
63264                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
63265                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
63266       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
63267       else std::fprintf(cimg::output(),".\n");
63268 
63269       char tmp[16] = { 0 };
63270       cimglist_for(*this,ll) {
63271         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
63272         std::fprintf(cimg::output(),"  ");
63273         _data[ll].print(tmp,display_stats);
63274         if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output(),"  ...\n"); }
63275       }
63276       std::fflush(cimg::output());
63277       return *this;
63278     }
63279 
63280     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
63281     /**
63282        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
63283        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
63284        \param align Appending alignment.
63285        \note This function displays the list images of the current CImgList instance into an existing
63286          CImgDisplay window.
63287        Images of the list are appended in a single temporary image for visualization purposes.
63288        The function returns immediately.
63289     **/
63290     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
63291       disp.display(*this,axis,align);
63292       return *this;
63293     }
63294 
63295     //! Display the current CImgList instance in a new display window.
63296     /**
63297         \param disp Display window.
63298         \param display_info Tells if image information are displayed on the standard output.
63299         \param axis Alignment axis for images viewing.
63300         \param align Appending alignment.
63301         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
63302         \param exit_on_anykey Exit function when any key is pressed.
63303         \note This function opens a new window with a specific title and displays the list images of the
63304           current CImgList instance into it.
63305         Images of the list are appended in a single temporary image for visualization purposes.
63306         The function returns when a key is pressed or the display window is closed by the user.
63307     **/
63308     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
63309                                const char axis='x', const float align=0,
63310                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
63311       bool is_exit = false;
63312       return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
63313     }
63314 
63315     //! Display the current CImgList instance in a new display window.
63316     /**
63317       \param title Title of the opening display window.
63318       \param display_info Tells if list information must be written on standard output.
63319       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
63320       \param align Appending alignment.
63321       \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
63322       \param exit_on_anykey Exit function when any key is pressed.
63323     **/
63324     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
63325                                const char axis='x', const float align=0,
63326                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
63327       CImgDisplay disp;
63328       bool is_exit = false;
63329       return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
63330     }
63331 
63332     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
63333                                 const bool display_info, const char axis, const float align, unsigned int *const XYZ,
63334                                 const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
63335                                 bool &is_exit) const {
63336       if (is_empty())
63337         throw CImgInstanceException(_cimglist_instance
63338                                     "display(): Empty instance.",
63339                                     cimglist_instance);
63340       if (!disp) {
63341         if (axis=='x') {
63342           unsigned int sum_width = 0, max_height = 0;
63343           cimglist_for(*this,l) {
63344             const CImg<T> &img = _data[l];
63345             const unsigned int
63346               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
63347               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
63348             sum_width+=w;
63349             if (h>max_height) max_height = h;
63350           }
63351           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
63352         } else {
63353           unsigned int max_width = 0, sum_height = 0;
63354           cimglist_for(*this,l) {
63355             const CImg<T> &img = _data[l];
63356             const unsigned int
63357               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
63358               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
63359             if (w>max_width) max_width = w;
63360             sum_height+=h;
63361           }
63362           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
63363         }
63364         if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
63365       } else if (title) disp.set_title("%s",title);
63366       else if (titles) disp.set_title("%s",titles->__display()._data);
63367       const CImg<char> dtitle = CImg<char>::string(disp.title());
63368       if (display_info) print(disp.title());
63369       disp.show().flush();
63370 
63371       if (_width==1) {
63372         const unsigned int dw = disp._width, dh = disp._height;
63373         if (!is_first_call)
63374           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
63375         disp.set_title("%s (%ux%ux%ux%u)",
63376                        dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
63377         _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
63378         if (disp.key()) is_exit = true;
63379         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
63380       } else {
63381         bool disp_resize = !is_first_call;
63382         while (!disp.is_closed() && !is_exit) {
63383           const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
63384           disp_resize = true;
63385           if (s[0]<0 && !disp.wheel()) { // No selections done
63386             if (disp.button()&2) { disp.flush(); break; }
63387             is_exit = true;
63388           } else if (disp.wheel()) { // Zoom in/out
63389             const int wheel = disp.wheel();
63390             disp.set_wheel();
63391             if (!is_first_call && wheel<0) break;
63392             if (wheel>0 && _width>=4) {
63393               const unsigned int
63394                 delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
63395                 ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
63396                 ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
63397               if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
63398                 const CImgList<T> sublist = get_shared_images(ind0,ind1);
63399                 CImgList<charT> t_sublist;
63400                 if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
63401                 sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
63402                                  orig + ind0,false,is_exit);
63403               }
63404             }
63405           } else if (s[0]!=0 || s[1]!=width() - 1) {
63406             const CImgList<T> sublist = get_shared_images(s[0],s[1]);
63407             CImgList<charT> t_sublist;
63408             if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
63409             sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
63410                              orig + s[0],false,is_exit);
63411           }
63412           disp.set_title("%s",dtitle.data());
63413         }
63414       }
63415       return *this;
63416     }
63417 
63418     // [internal] Return string to describe display title.
63419     CImg<charT> __display() const {
63420       CImg<charT> res, str;
63421       cimglist_for(*this,l) {
63422         CImg<charT>::string((char*)_data[l]).move_to(str);
63423         if (l!=width() - 1) {
63424           str.resize(str._width + 1,1,1,1,0);
63425           str[str._width - 2] = ',';
63426           str[str._width - 1] = ' ';
63427         }
63428         res.append(str,'x');
63429       }
63430       if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
63431       cimg::strellipsize(res,128,false);
63432       if (_width>1) {
63433         const unsigned int l = (unsigned int)std::strlen(res);
63434         if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
63435         cimg_snprintf(res._data + l,16," (#%u)",_width);
63436       }
63437       return res;
63438     }
63439 
63440     //! Save list into a file.
63441     /**
63442       \param filename Filename to write data to.
63443       \param number When positive, represents an index added to the filename. Otherwise, no number is added.
63444       \param digits Number of digits used for adding the number to the filename.
63445     **/
63446     const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
63447       if (!filename)
63448         throw CImgArgumentException(_cimglist_instance
63449                                     "save(): Specified filename is (null).",
63450                                     cimglist_instance);
63451       // Do not test for empty instances, since .cimg format is able to manage empty instances.
63452       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
63453       const char *const ext = cimg::split_filename(filename);
63454       CImg<charT> nfilename(1024);
63455       const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
63456         filename;
63457 
63458 #ifdef cimglist_save_plugin
63459       cimglist_save_plugin(fn);
63460 #endif
63461 #ifdef cimglist_save_plugin1
63462       cimglist_save_plugin1(fn);
63463 #endif
63464 #ifdef cimglist_save_plugin2
63465       cimglist_save_plugin2(fn);
63466 #endif
63467 #ifdef cimglist_save_plugin3
63468       cimglist_save_plugin3(fn);
63469 #endif
63470 #ifdef cimglist_save_plugin4
63471       cimglist_save_plugin4(fn);
63472 #endif
63473 #ifdef cimglist_save_plugin5
63474       cimglist_save_plugin5(fn);
63475 #endif
63476 #ifdef cimglist_save_plugin6
63477       cimglist_save_plugin6(fn);
63478 #endif
63479 #ifdef cimglist_save_plugin7
63480       cimglist_save_plugin7(fn);
63481 #endif
63482 #ifdef cimglist_save_plugin8
63483       cimglist_save_plugin8(fn);
63484 #endif
63485       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
63486       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
63487       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
63488       else if (!cimg::strcasecmp(ext,"avi") ||
63489                !cimg::strcasecmp(ext,"mov") ||
63490                !cimg::strcasecmp(ext,"asf") ||
63491                !cimg::strcasecmp(ext,"divx") ||
63492                !cimg::strcasecmp(ext,"flv") ||
63493                !cimg::strcasecmp(ext,"mpg") ||
63494                !cimg::strcasecmp(ext,"m1v") ||
63495                !cimg::strcasecmp(ext,"m2v") ||
63496                !cimg::strcasecmp(ext,"m4v") ||
63497                !cimg::strcasecmp(ext,"mjp") ||
63498                !cimg::strcasecmp(ext,"mp4") ||
63499                !cimg::strcasecmp(ext,"mkv") ||
63500                !cimg::strcasecmp(ext,"mpe") ||
63501                !cimg::strcasecmp(ext,"movie") ||
63502                !cimg::strcasecmp(ext,"ogm") ||
63503                !cimg::strcasecmp(ext,"ogg") ||
63504                !cimg::strcasecmp(ext,"ogv") ||
63505                !cimg::strcasecmp(ext,"qt") ||
63506                !cimg::strcasecmp(ext,"rm") ||
63507                !cimg::strcasecmp(ext,"vob") ||
63508                !cimg::strcasecmp(ext,"webm") ||
63509                !cimg::strcasecmp(ext,"wmv") ||
63510                !cimg::strcasecmp(ext,"xvid") ||
63511                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
63512 #ifdef cimg_use_tiff
63513       else if (!cimg::strcasecmp(ext,"tif") ||
63514           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
63515 #endif
63516       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
63517       else {
63518         if (_width==1) _data[0].save(fn,-1);
63519         else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
63520       }
63521       return *this;
63522     }
63523 
63524     //! Tell if an image list can be saved as one single file.
63525     /**
63526        \param filename Filename, as a C-string.
63527        \return \c true if the file format supports multiple images, \c false otherwise.
63528     **/
63529     static bool is_saveable(const char *const filename) {
63530       const char *const ext = cimg::split_filename(filename);
63531       if (!cimg::strcasecmp(ext,"cimgz") ||
63532 #ifdef cimg_use_tiff
63533           !cimg::strcasecmp(ext,"tif") ||
63534           !cimg::strcasecmp(ext,"tiff") ||
63535 #endif
63536           !cimg::strcasecmp(ext,"yuv") ||
63537           !cimg::strcasecmp(ext,"avi") ||
63538           !cimg::strcasecmp(ext,"mov") ||
63539           !cimg::strcasecmp(ext,"asf") ||
63540           !cimg::strcasecmp(ext,"divx") ||
63541           !cimg::strcasecmp(ext,"flv") ||
63542           !cimg::strcasecmp(ext,"mpg") ||
63543           !cimg::strcasecmp(ext,"m1v") ||
63544           !cimg::strcasecmp(ext,"m2v") ||
63545           !cimg::strcasecmp(ext,"m4v") ||
63546           !cimg::strcasecmp(ext,"mjp") ||
63547           !cimg::strcasecmp(ext,"mp4") ||
63548           !cimg::strcasecmp(ext,"mkv") ||
63549           !cimg::strcasecmp(ext,"mpe") ||
63550           !cimg::strcasecmp(ext,"movie") ||
63551           !cimg::strcasecmp(ext,"ogm") ||
63552           !cimg::strcasecmp(ext,"ogg") ||
63553           !cimg::strcasecmp(ext,"ogv") ||
63554           !cimg::strcasecmp(ext,"qt") ||
63555           !cimg::strcasecmp(ext,"rm") ||
63556           !cimg::strcasecmp(ext,"vob") ||
63557           !cimg::strcasecmp(ext,"webm") ||
63558           !cimg::strcasecmp(ext,"wmv") ||
63559           !cimg::strcasecmp(ext,"xvid") ||
63560           !cimg::strcasecmp(ext,"mpeg")) return true;
63561       return false;
63562     }
63563 
63564     //! Save image sequence as a GIF animated file.
63565     /**
63566        \param filename Filename to write data to.
63567        \param fps Number of desired frames per second.
63568        \param nb_loops Number of loops (\c 0 for infinite looping).
63569     **/
63570     const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
63571                                          const unsigned int nb_loops=0) {
63572       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
63573       CImgList<charT> filenames;
63574       std::FILE *file = 0;
63575 
63576 #ifdef cimg_use_png
63577 #define _cimg_save_gif_extension "png"
63578 #else
63579 #define _cimg_save_gif_extension "ppm"
63580 #endif
63581 
63582       do {
63583         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63584                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63585         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
63586         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63587       } while (file);
63588       cimglist_for(*this,l) {
63589         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
63590         CImg<charT>::string(filename_tmp2).move_to(filenames);
63591         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
63592         else _data[l].save(filename_tmp2);
63593       }
63594       cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u",
63595                     cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops);
63596       CImg<ucharT>::string(command).move_to(filenames,0);
63597       cimg_snprintf(command,command._width,"\"%s\"",
63598                     CImg<charT>::string(filename)._system_strescape().data());
63599       CImg<ucharT>::string(command).move_to(filenames);
63600       CImg<charT> _command = filenames>'x';
63601       cimg_for(_command,p,char) if (!*p) *p = ' ';
63602       _command.back() = 0;
63603 
63604       cimg::system(_command, cimg::imagemagick_path());
63605       file = cimg::std_fopen(filename,"rb");
63606       if (!file)
63607         throw CImgIOException(_cimglist_instance
63608                               "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
63609                               cimglist_instance,
63610                               filename);
63611       else cimg::fclose(file);
63612       cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
63613       return *this;
63614     }
63615 
63616     //! Save list as a YUV image sequence file.
63617     /**
63618       \param filename Filename to write data to.
63619       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
63620       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
63621     **/
63622     const CImgList<T>& save_yuv(const char *const filename=0,
63623                                 const unsigned int chroma_subsampling=444,
63624                                 const bool is_rgb=true) const {
63625       return _save_yuv(0,filename,chroma_subsampling,is_rgb);
63626     }
63627 
63628     //! Save image sequence into a YUV file.
63629     /**
63630       \param file File to write data to.
63631       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
63632       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
63633     **/
63634     const CImgList<T>& save_yuv(std::FILE *const file,
63635                                 const unsigned int chroma_subsampling=444,
63636                                 const bool is_rgb=true) const {
63637       return _save_yuv(file,0,chroma_subsampling,is_rgb);
63638     }
63639 
63640     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
63641                                  const unsigned int chroma_subsampling,
63642                                  const bool is_rgb) const {
63643       if (!file && !filename)
63644         throw CImgArgumentException(_cimglist_instance
63645                                     "save_yuv(): Specified filename is (null).",
63646                                     cimglist_instance);
63647       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
63648         throw CImgArgumentException(_cimglist_instance
63649                                     "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
63650                                     cimglist_instance,
63651                                     chroma_subsampling,filename?filename:"(FILE*)");
63652       if (is_empty()) { cimg::fempty(file,filename); return *this; }
63653       const unsigned int
63654         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
63655         cfy = chroma_subsampling==420?2:1,
63656         w0 = (*this)[0]._width, h0 = (*this)[0]._height,
63657         width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
63658       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63659       cimglist_for(*this,l) {
63660         const CImg<T> &frame = (*this)[l];
63661         cimg_forZ(frame,z) {
63662           CImg<ucharT> YUV;
63663           if (sizeof(T)==1 && !is_rgb &&
63664               frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
63665             YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
63666           else {
63667             YUV = frame.get_slice(z);
63668             if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
63669             if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
63670             if (is_rgb) YUV.RGBtoYCbCr();
63671           }
63672           if (chroma_subsampling==444)
63673             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
63674           else {
63675             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
63676             CImg<ucharT> UV = YUV.get_channels(1,2);
63677             UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
63678             cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
63679           }
63680         }
63681       }
63682       if (!file) cimg::fclose(nfile);
63683       return *this;
63684     }
63685 
63686     //! Save list into a .cimg file.
63687     /**
63688        \param filename Filename to write data to.
63689        \param is_compressed Tells if data compression must be enabled.
63690     **/
63691     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
63692       return _save_cimg(0,filename,is_compressed);
63693     }
63694 
63695     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
63696       if (!file && !filename)
63697         throw CImgArgumentException(_cimglist_instance
63698                                     "save_cimg(): Specified filename is (null).",
63699                                     cimglist_instance);
63700 #ifndef cimg_use_zlib
63701       if (is_compressed)
63702         cimg::warn(_cimglist_instance
63703                    "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
63704                    "saving them uncompressed.",
63705                    cimglist_instance,
63706                    filename?filename:"(FILE*)");
63707 #endif
63708       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
63709       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63710       const bool is_bool = ptype==cimg::type<bool>::string();
63711       if (!is_bool && std::strstr(ptype,"unsigned")==ptype)
63712         std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
63713       else
63714         std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
63715 
63716       cimglist_for(*this,l) {
63717         const CImg<T>& img = _data[l];
63718         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
63719         if (img._data) {
63720           CImg<T> tmp;
63721           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
63722           const CImg<T>& ref = cimg::endianness()?tmp:img;
63723           bool failed_to_compress = true;
63724           if (is_compressed) {
63725 #ifdef cimg_use_zlib
63726             Bytef *cbuf = 0;
63727             uLongf csiz = 0;
63728 
63729             if (is_bool) { // Boolean data (bitwise)
63730               ulongT siz;
63731               const unsigned char *const buf = ref._bool2uchar(siz,false);
63732               csiz = siz + siz/100 + 16;
63733               cbuf = new Bytef[csiz];
63734               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz);
63735               if (!failed_to_compress) {
63736                 std::fprintf(nfile," #%lu\n",csiz);
63737                 cimg::fwrite(cbuf,csiz,nfile);
63738               }
63739               delete[] buf;
63740             } else { // Non-boolean data
63741               const ulongT siz = sizeof(T)*ref.size();
63742               csiz = siz + siz/100 + 16;
63743               cbuf = new Bytef[csiz];
63744               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz);
63745               if (!failed_to_compress) {
63746                 std::fprintf(nfile," #%lu\n",csiz);
63747                 cimg::fwrite(cbuf,csiz,nfile);
63748               }
63749             }
63750             if (failed_to_compress)
63751               cimg::warn(_cimglist_instance
63752                          "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
63753                          cimglist_instance,
63754                          filename?filename:"(FILE*)");
63755             delete[] cbuf;
63756 #endif
63757           }
63758           if (failed_to_compress) { // Write non-compressed
63759             std::fputc('\n',nfile);
63760             if (is_bool) { // Boolean data (bitwise)
63761               ulongT siz;
63762               const unsigned char *const buf = ref._bool2uchar(siz,false);
63763               cimg::fwrite(buf,siz,nfile);
63764               delete[] buf;
63765             } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data
63766           }
63767         } else std::fputc('\n',nfile);
63768       }
63769       if (!file) cimg::fclose(nfile);
63770       return *this;
63771     }
63772 
63773     //! Save list into a .cimg file.
63774     /**
63775        \param file File to write data to.
63776        \param is_compressed Tells if data compression must be enabled.
63777     **/
63778     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
63779       return _save_cimg(file,0,is_compressed);
63780     }
63781 
63782     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
63783                                  const unsigned int n0,
63784                                  const unsigned int x0, const unsigned int y0,
63785                                  const unsigned int z0, const unsigned int c0) const {
63786 #define _cimg_save_cimg_case(Ts,Tss) \
63787       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
63788         for (unsigned int l = 0; l<lmax; ++l) { \
63789           j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
63790           W = H = D = C = 0; \
63791           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
63792             throw CImgIOException(_cimglist_instance \
63793                                   "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
63794                                   cimglist_instance, \
63795                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
63796           if (W*H*D*C>0) { \
63797             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
63798             else { \
63799               const CImg<T>& img = (*this)[l - n0]; \
63800               const T *ptrs = img._data; \
63801               const unsigned int \
63802                 x1 = x0 + img._width - 1, \
63803                 y1 = y0 + img._height - 1, \
63804                 z1 = z0 + img._depth - 1, \
63805                 c1 = c0 + img._spectrum - 1, \
63806                 nx1 = x1>=W?W - 1:x1, \
63807                 ny1 = y1>=H?H - 1:y1, \
63808                 nz1 = z1>=D?D - 1:z1, \
63809                 nc1 = c1>=C?C - 1:c1; \
63810               CImg<Tss> raw(1 + nx1 - x0); \
63811               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
63812               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
63813               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
63814                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
63815                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
63816                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
63817                   const unsigned int skipyb = y0*W*sizeof(Tss); \
63818                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
63819                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
63820                     const unsigned int skipxb = x0*sizeof(Tss); \
63821                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
63822                     raw.assign(ptrs, raw._width); \
63823                     ptrs+=img._width; \
63824                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
63825                     cimg::fwrite(raw._data,raw._width,nfile); \
63826                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
63827                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
63828                   } \
63829                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
63830                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
63831                 } \
63832                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
63833                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
63834               } \
63835               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
63836               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
63837             } \
63838           } \
63839         } \
63840         saved = true; \
63841       }
63842 
63843       if (!file && !filename)
63844         throw CImgArgumentException(_cimglist_instance
63845                                     "save_cimg(): Specified filename is (null).",
63846                                     cimglist_instance);
63847       if (is_empty())
63848         throw CImgInstanceException(_cimglist_instance
63849                                     "save_cimg(): Empty instance, for file '%s'.",
63850                                     cimglist_instance,
63851                                     filename?filename:"(FILE*)");
63852 
63853       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
63854       bool saved = false, endian = cimg::endianness();
63855       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
63856       *tmp = *str_pixeltype = *str_endian = 0;
63857       unsigned int j, N, W, H, D, C;
63858       int i, err;
63859       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
63860       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
63861       if (err<2) {
63862         if (!file) cimg::fclose(nfile);
63863         throw CImgIOException(_cimglist_instance
63864                               "save_cimg(): CImg header not found in file '%s'.",
63865                               cimglist_instance,
63866                               filename?filename:"(FILE*)");
63867       }
63868       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
63869       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
63870       const unsigned int lmax = std::min(N,n0 + _width);
63871       _cimg_save_cimg_case("bool",bool);
63872       _cimg_save_cimg_case("unsigned_char",unsigned char);
63873       _cimg_save_cimg_case("uchar",unsigned char);
63874       _cimg_save_cimg_case("char",char);
63875       _cimg_save_cimg_case("unsigned_short",unsigned short);
63876       _cimg_save_cimg_case("ushort",unsigned short);
63877       _cimg_save_cimg_case("short",short);
63878       _cimg_save_cimg_case("unsigned_int",unsigned int);
63879       _cimg_save_cimg_case("uint",unsigned int);
63880       _cimg_save_cimg_case("int",int);
63881       _cimg_save_cimg_case("unsigned_int64",uint64T);
63882       _cimg_save_cimg_case("uint64",uint64T);
63883       _cimg_save_cimg_case("int64",int64T);
63884       _cimg_save_cimg_case("float",float);
63885       _cimg_save_cimg_case("double",double);
63886       if (!saved) {
63887         if (!file) cimg::fclose(nfile);
63888         throw CImgIOException(_cimglist_instance
63889                               "save_cimg(): Unsupported data type '%s' for file '%s'.",
63890                               cimglist_instance,
63891                               filename?filename:"(FILE*)",str_pixeltype._data);
63892       }
63893       if (!file) cimg::fclose(nfile);
63894       return *this;
63895     }
63896 
63897     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
63898     /**
63899       \param filename Filename to write data to.
63900       \param n0 Starting index of images to write.
63901       \param x0 Starting X-coordinates of image regions to write.
63902       \param y0 Starting Y-coordinates of image regions to write.
63903       \param z0 Starting Z-coordinates of image regions to write.
63904       \param c0 Starting C-coordinates of image regions to write.
63905     **/
63906     const CImgList<T>& save_cimg(const char *const filename,
63907                                  const unsigned int n0,
63908                                  const unsigned int x0, const unsigned int y0,
63909                                  const unsigned int z0, const unsigned int c0) const {
63910       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
63911     }
63912 
63913     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
63914     /**
63915       \param file File to write data to.
63916       \param n0 Starting index of images to write.
63917       \param x0 Starting X-coordinates of image regions to write.
63918       \param y0 Starting Y-coordinates of image regions to write.
63919       \param z0 Starting Z-coordinates of image regions to write.
63920       \param c0 Starting C-coordinates of image regions to write.
63921     **/
63922     const CImgList<T>& save_cimg(std::FILE *const file,
63923                                  const unsigned int n0,
63924                                  const unsigned int x0, const unsigned int y0,
63925                                  const unsigned int z0, const unsigned int c0) const {
63926       return _save_cimg(file,0,n0,x0,y0,z0,c0);
63927     }
63928 
63929     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
63930                                 const unsigned int nb,
63931                                 const unsigned int dx, const unsigned int dy,
63932                                 const unsigned int dz, const unsigned int dc) {
63933       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
63934       const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
63935       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
63936       for (unsigned int i=nb; i; --i) {
63937         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
63938         for (ulongT off = siz; off; --off) std::fputc(0,nfile);
63939       }
63940       if (!file) cimg::fclose(nfile);
63941     }
63942 
63943     //! Save empty (non-compressed) .cimg file with specified dimensions.
63944     /**
63945         \param filename Filename to write data to.
63946         \param nb Number of images to write.
63947         \param dx Width of images in the written file.
63948         \param dy Height of images in the written file.
63949         \param dz Depth of images in the written file.
63950         \param dc Spectrum of images in the written file.
63951     **/
63952     static void save_empty_cimg(const char *const filename,
63953                                 const unsigned int nb,
63954                                 const unsigned int dx, const unsigned int dy=1,
63955                                 const unsigned int dz=1, const unsigned int dc=1) {
63956       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
63957     }
63958 
63959     //! Save empty .cimg file with specified dimensions.
63960     /**
63961         \param file File to write data to.
63962         \param nb Number of images to write.
63963         \param dx Width of images in the written file.
63964         \param dy Height of images in the written file.
63965         \param dz Depth of images in the written file.
63966         \param dc Spectrum of images in the written file.
63967     **/
63968     static void save_empty_cimg(std::FILE *const file,
63969                                 const unsigned int nb,
63970                                 const unsigned int dx, const unsigned int dy=1,
63971                                 const unsigned int dz=1, const unsigned int dc=1) {
63972       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
63973     }
63974 
63975     //! Save list as a TIFF file.
63976     /**
63977       \param filename Filename to write data to.
63978       \param compression_type Compression mode used to write data.
63979       \param voxel_size Voxel size, to be stored in the filename.
63980       \param description Description, to be stored in the filename.
63981       \param use_bigtiff Allow to save big tiff files (>4Gb).
63982     **/
63983     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
63984                                  const float *const voxel_size=0, const char *const description=0,
63985                                  const bool use_bigtiff=true) const {
63986       if (!filename)
63987         throw CImgArgumentException(_cimglist_instance
63988                                     "save_tiff(): Specified filename is (null).",
63989                                     cimglist_instance);
63990       if (is_empty()) { cimg::fempty(0,filename); return *this; }
63991 
63992 #ifndef cimg_use_tiff
63993       if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
63994       else cimglist_for(*this,l) {
63995           CImg<charT> nfilename(1024);
63996           cimg::number_filename(filename,l,6,nfilename);
63997           _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
63998         }
63999 #else
64000       ulongT siz = 0;
64001       cimglist_for(*this,l) siz+=_data[l].size();
64002       const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images
64003       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
64004       if (tif) {
64005         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
64006           const CImg<T>& img = (*this)[l];
64007           cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
64008         }
64009         TIFFClose(tif);
64010       } else
64011         throw CImgIOException(_cimglist_instance
64012                               "save_tiff(): Failed to open stream for file '%s'.",
64013                               cimglist_instance,
64014                               filename);
64015 #endif
64016       return *this;
64017     }
64018 
64019     //! Save list as a gzipped file, using external tool 'gzip'.
64020     /**
64021       \param filename Filename to write data to.
64022     **/
64023     const CImgList<T>& save_gzip_external(const char *const filename) const {
64024       if (!filename)
64025         throw CImgIOException(_cimglist_instance
64026                               "save_gzip_external(): Specified filename is (null).",
64027                               cimglist_instance);
64028       CImg<charT> command(1024), filename_tmp(256), body(256);
64029       const char
64030         *ext = cimg::split_filename(filename,body),
64031         *ext2 = cimg::split_filename(body,0);
64032       std::FILE *file;
64033       do {
64034         if (!cimg::strcasecmp(ext,"gz")) {
64035           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
64036                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
64037           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
64038                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
64039         } else {
64040           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
64041                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
64042           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
64043                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
64044         }
64045         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
64046       } while (file);
64047 
64048       if (is_saveable(body)) {
64049         save(filename_tmp);
64050         cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
64051                       cimg::gzip_path(),
64052                       CImg<charT>::string(filename_tmp)._system_strescape().data(),
64053                       CImg<charT>::string(filename)._system_strescape().data());
64054         cimg::system(command, cimg::gzip_path());
64055         file = cimg::std_fopen(filename,"rb");
64056         if (!file)
64057           throw CImgIOException(_cimglist_instance
64058                                 "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
64059                                 cimglist_instance,
64060                                 filename);
64061         else cimg::fclose(file);
64062         std::remove(filename_tmp);
64063       } else {
64064         CImg<charT> nfilename(1024);
64065         cimglist_for(*this,l) {
64066           cimg::number_filename(body,l,6,nfilename);
64067           if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
64068           _data[l].save_gzip_external(nfilename);
64069         }
64070       }
64071       return *this;
64072     }
64073 
64074     //! Save image sequence (using the OpenCV library when available).
64075     /**
64076        \param filename Filename to write data to.
64077        \param fps Number of frames per second.
64078        \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
64079        \param keep_open Tells if the video writer associated to the specified filename
64080        must be kept open or not (to allow frames to be added in the same file afterwards).
64081     **/
64082     const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
64083                                   const char *codec=0, const bool keep_open=false) const {
64084 #ifndef cimg_use_opencv
64085       cimg::unused(codec,keep_open);
64086       return save_ffmpeg_external(filename,fps);
64087 #else
64088       try {
64089         static cv::VideoWriter *writers[32] = { 0 };
64090         static CImgList<charT> filenames(32);
64091         static CImg<intT> sizes(32,2,1,1,0);
64092         static int last_used_index = -1;
64093 
64094         // Detect if a video writer already exists for the specified filename.
64095         cimg::mutex(9);
64096         int index = -1;
64097         if (filename) {
64098           if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
64099             index = last_used_index;
64100           } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
64101               index = l; break;
64102             }
64103         } else index = last_used_index;
64104         cimg::mutex(9,0);
64105 
64106         // Find empty slot for capturing video stream.
64107         if (index<0) {
64108           if (!filename)
64109             throw CImgArgumentException(_cimglist_instance
64110                                         "save_video(): No already open video writer found. You must specify a "
64111                                         "non-(null) filename argument for the first call.",
64112                                         cimglist_instance);
64113           else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
64114           if (index<0)
64115             throw CImgIOException(_cimglist_instance
64116                                   "save_video(): File '%s', no video writer slots available. "
64117                                   "You have to release some of your previously opened videos.",
64118                                   cimglist_instance,filename);
64119           if (is_empty())
64120             throw CImgInstanceException(_cimglist_instance
64121                                         "save_video(): Instance list is empty.",
64122                                         cimglist_instance);
64123           const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
64124           if (!W || !H)
64125             throw CImgInstanceException(_cimglist_instance
64126                                         "save_video(): Frame [0] is an empty image.",
64127                                         cimglist_instance);
64128           const char
64129             *const _codec = codec && *codec?codec:"h264",
64130             codec0 = cimg::uppercase(_codec[0]),
64131             codec1 = _codec[0]?cimg::uppercase(_codec[1]):0,
64132             codec2 = _codec[1]?cimg::uppercase(_codec[2]):0,
64133             codec3 = _codec[2]?cimg::uppercase(_codec[3]):0;
64134           cimg::mutex(9);
64135           writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H));
64136           if (!writers[index]->isOpened()) {
64137             delete writers[index];
64138             writers[index] = 0;
64139             cimg::mutex(9,0);
64140             throw CImgIOException(_cimglist_instance
64141                                   "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
64142                                   cimglist_instance,filename,
64143                                   codec0,codec1,codec2,codec3);
64144           }
64145           CImg<charT>::string(filename).move_to(filenames[index]);
64146           sizes(index,0) = W;
64147           sizes(index,1) = H;
64148           cimg::mutex(9,0);
64149         }
64150 
64151         if (!is_empty()) {
64152           const unsigned int W = sizes(index,0), H = sizes(index,1);
64153           cimg::mutex(9);
64154           cimglist_for(*this,l) {
64155             CImg<T> &src = _data[l];
64156             if (src.is_empty())
64157               cimg::warn(_cimglist_instance
64158                          "save_video(): Skip empty frame %d for file '%s'.",
64159                          cimglist_instance,l,filename);
64160             if (src._spectrum>3)
64161               cimg::warn(_cimglist_instance
64162                          "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
64163                          "Some image data may be ignored when writing frame into video file '%s'.",
64164                          cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
64165             cimg_forZ(src,z) {
64166               CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
64167               if (_src._width==W && _src._height==H && _src._spectrum==3)
64168                 writers[index]->write(CImg<ucharT>(_src)._cimg2cvmat());
64169               else {
64170                 CImg<ucharT> __src(_src,false);
64171                 __src.channels(0,std::min(__src._spectrum - 1,2U)).resize(W,H);
64172                 __src.resize(W,H,1,3,__src._spectrum==1);
64173                 writers[index]->write(__src._cimg2cvmat());
64174               }
64175             }
64176           }
64177           cimg::mutex(9,0);
64178         }
64179 
64180         cimg::mutex(9);
64181         if (!keep_open) {
64182           delete writers[index];
64183           writers[index] = 0;
64184           filenames[index].assign();
64185           sizes(index,0) = sizes(index,1) = 0;
64186           last_used_index = -1;
64187         } else last_used_index = index;
64188         cimg::mutex(9,0);
64189       } catch (CImgIOException &e) {
64190         if (!keep_open) return save_ffmpeg_external(filename,fps);
64191         throw e;
64192       }
64193       return *this;
64194 #endif
64195     }
64196 
64197     //! Save image sequence, using the external tool 'ffmpeg'.
64198     /**
64199       \param filename Filename to write data to.
64200       \param fps Number of frames per second.
64201       \param codec Type of compression.
64202       \param bitrate Output bitrate
64203     **/
64204     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
64205                                             const char *const codec=0, const unsigned int bitrate=2048) const {
64206       if (!filename)
64207         throw CImgArgumentException(_cimglist_instance
64208                                     "save_ffmpeg_external(): Specified filename is (null).",
64209                                     cimglist_instance);
64210       if (is_empty()) { cimg::fempty(0,filename); return *this; }
64211 
64212       const char
64213         *const ext = cimg::split_filename(filename),
64214         *const _codec = codec?codec:
64215         !cimg::strcasecmp(ext,"flv")?"flv":
64216         !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video";
64217 
64218       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
64219       CImgList<charT> filenames;
64220       std::FILE *file = 0;
64221       cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
64222         throw CImgInstanceException(_cimglist_instance
64223                                     "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
64224                                     cimglist_instance,
64225                                     filename);
64226       do {
64227         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
64228                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
64229         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
64230         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
64231       } while (file);
64232       unsigned int frame = 1;
64233       cimglist_for(*this,l) {
64234         CImg<T>& src = _data[l];
64235         cimg_forZ(src,z) {
64236           cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,frame);
64237           CImg<charT>::string(filename_tmp2).move_to(filenames);
64238           CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
64239           if (_src._width%2 || _src._height%2) // Force output to have an even number of columns and rows
64240             _src.assign(_src.get_resize(_src._width + (_src._width%2),_src._height + (_src._height%2),1,-100,0),false);
64241           if (_src._spectrum!=3) // Force output to be one slice, in color
64242             _src.assign(_src.get_resize(-100,-100,1,3),false);
64243           _src.save_pnm(filename_tmp2);
64244           ++frame;
64245         }
64246       }
64247       cimg_snprintf(command,command._width,
64248                     "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
64249                     cimg::ffmpeg_path(),
64250                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
64251                     _codec,bitrate,fps,
64252                     CImg<charT>::string(filename)._system_strescape().data());
64253       cimg::system(command, cimg::ffmpeg_path());
64254       file = cimg::std_fopen(filename,"rb");
64255       if (!file)
64256         throw CImgIOException(_cimglist_instance
64257                               "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
64258                               cimglist_instance,
64259                               filename);
64260       else cimg::fclose(file);
64261       cimglist_for(*this,l) std::remove(filenames[l]);
64262       return *this;
64263     }
64264 
64265     //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
64266     /**
64267        \param is_compressed tells if zlib compression must be used for serialization
64268        (this requires 'cimg_use_zlib' been enabled).
64269     **/
64270     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
64271 #ifndef cimg_use_zlib
64272       if (is_compressed)
64273         cimg::warn(_cimglist_instance
64274                    "get_serialize(): Unable to compress data unless zlib is enabled, "
64275                    "storing them uncompressed.",
64276                    cimglist_instance);
64277 #endif
64278       CImgList<ucharT> stream;
64279       CImg<charT> tmpstr(128);
64280       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
64281       if (std::strstr(ptype,"unsigned")==ptype)
64282         cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
64283       else
64284         cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
64285       CImg<ucharT>::string(tmpstr,false).move_to(stream);
64286       cimglist_for(*this,l) {
64287         const CImg<T>& img = _data[l];
64288         cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
64289         CImg<ucharT>::string(tmpstr,false).move_to(stream);
64290         if (img._data) {
64291           CImg<T> tmp;
64292           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
64293           const CImg<T>& ref = cimg::endianness()?tmp:img;
64294           bool failed_to_compress = true;
64295           if (is_compressed) {
64296 #ifdef cimg_use_zlib
64297             const ulongT siz = sizeof(T)*ref.size();
64298             uLongf csiz = (ulongT)compressBound(siz);
64299             Bytef *const cbuf = new Bytef[csiz];
64300             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
64301               cimg::warn(_cimglist_instance
64302                          "get_serialize(): Failed to save compressed data, saving them uncompressed.",
64303                          cimglist_instance);
64304             else {
64305               cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
64306               CImg<ucharT>::string(tmpstr,false).move_to(stream);
64307               CImg<ucharT>(cbuf,csiz).move_to(stream);
64308               delete[] cbuf;
64309               failed_to_compress = false;
64310             }
64311 #endif
64312           }
64313           if (failed_to_compress) { // Write in a non-compressed way
64314             CImg<charT>::string("\n",false).move_to(stream);
64315             stream.insert(1);
64316             stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
64317           }
64318         } else CImg<charT>::string("\n",false).move_to(stream);
64319       }
64320       cimglist_apply(stream,unroll)('y');
64321       return stream>'y';
64322     }
64323 
64324     //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
64325     template<typename t>
64326     static CImgList<T> get_unserialize(const CImg<t>& buffer) {
64327 #ifdef cimg_use_zlib
64328 #define _cimgz_unserialize_case(Tss) { \
64329         Bytef *cbuf = 0; \
64330         if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type<bool>::string()) { \
64331           cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
64332           for (ulongT k = 0; k<csiz; ++k) *(_cbuf++) = (Bytef)*(stream++); \
64333           is_bytef = false; \
64334         } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
64335         raw.assign(W,H,D,C); \
64336         uLongf destlen = raw.size()*sizeof(Tss); \
64337         uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
64338         if (!is_bytef) delete[] cbuf; \
64339       }
64340 #else
64341 #define _cimgz_unserialize_case(Tss) \
64342       throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
64343                                   "unless zlib is enabled.", \
64344                                   pixel_type());
64345 #endif
64346 
64347 #define _cimg_unserialize_case(Ts,Tss) \
64348       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
64349         for (unsigned int l = 0; l<N; ++l) { \
64350           j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
64351           ++stream; tmp[j] = 0; \
64352           W = H = D = C = 0; csiz = 0; \
64353           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
64354             throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
64355                                         "image #%u in serialized buffer.", \
64356                                         pixel_type(),W,H,D,C,l); \
64357           if (W*H*D*C>0) { \
64358             CImg<Tss> raw; \
64359             CImg<T> &img = res._data[l]; \
64360             if (err==5) _cimgz_unserialize_case(Tss) \
64361             else { \
64362               raw.assign(W,H,D,C); \
64363               CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
64364               if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \
64365               else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
64366             } \
64367             if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
64368             raw.move_to(img); \
64369           } \
64370         } \
64371         loaded = true; \
64372       }
64373 
64374       if (buffer.is_empty())
64375         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
64376                                     pixel_type());
64377       CImgList<T> res;
64378       const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
64379       bool loaded = false, endian = cimg::endianness(), is_bytef = false;
64380       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
64381       *tmp = *str_pixeltype = *str_endian = 0;
64382       unsigned int j, N = 0, W, H, D, C;
64383       uint64T csiz;
64384       int i, err;
64385       cimg::unused(is_bytef);
64386       do {
64387         j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
64388         ++stream; tmp[j] = 0;
64389       } while (*tmp=='#' && stream<estream);
64390       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
64391                         &N,str_pixeltype._data,str_endian._data);
64392       if (err<2)
64393         throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
64394                                     pixel_type());
64395       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
64396       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
64397       res.assign(N);
64398       _cimg_unserialize_case("bool",bool);
64399       _cimg_unserialize_case("unsigned_char",unsigned char);
64400       _cimg_unserialize_case("uchar",unsigned char);
64401       _cimg_unserialize_case("char",char);
64402       _cimg_unserialize_case("unsigned_short",unsigned short);
64403       _cimg_unserialize_case("ushort",unsigned short);
64404       _cimg_unserialize_case("short",short);
64405       _cimg_unserialize_case("unsigned_int",unsigned int);
64406       _cimg_unserialize_case("uint",unsigned int);
64407       _cimg_unserialize_case("int",int);
64408       _cimg_unserialize_case("unsigned_int64",uint64T);
64409       _cimg_unserialize_case("uint64",uint64T);
64410       _cimg_unserialize_case("int64",int64T);
64411       _cimg_unserialize_case("float",float);
64412       _cimg_unserialize_case("double",double);
64413       if (!loaded)
64414         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
64415                                     "in serialized buffer.",
64416                                     pixel_type(),str_pixeltype._data);
64417       return res;
64418     }
64419 
64420     //@}
64421     //----------------------------------
64422     //
64423     //! \name Others
64424     //@{
64425     //----------------------------------
64426 
64427     //! Return a CImg pre-defined font with requested height.
64428     /**
64429        \param font_height Height of the desired font (exact match for 13,23,53,103).
64430        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
64431     **/
64432     static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) {
64433       if (!requested_height) return CImgList<ucharT>::const_empty();
64434       cimg::mutex(11);
64435       static const unsigned char font_resizemap[] = {
64436         0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30,
64437         32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52,
64438         54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72,
64439         73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
64440         90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
64441         107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
64442         123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
64443         138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
64444         152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165,
64445         166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179,
64446         180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192,
64447         193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205,
64448         206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218,
64449         219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231,
64450         231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243,
64451         244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 };
64452       static const char *const *font_data[] = {
64453         cimg::data_font_small,
64454         cimg::data_font_normal,
64455         cimg::data_font_large,
64456         cimg::data_font_huge };
64457       static const unsigned int
64458         font_width[] = { 10,26,52,104 },
64459         font_height[] = { 13,32,64,128 },
64460         font_M[] = { 86,91,91,47 },
64461         font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*),
64462                          sizeof(cimg::data_font_normal)/sizeof(char*),
64463                          sizeof(cimg::data_font_large)/sizeof(char*),
64464                          sizeof(cimg::data_font_huge)/sizeof(char*) };
64465       static const unsigned char font_is_binary[] = { 1,0,0,1 };
64466       static CImg<ucharT> font_base[4];
64467 
64468       unsigned int ind =
64469         requested_height<=font_height[0]?0U:
64470         requested_height<=font_height[1]?1U:
64471         requested_height<=font_height[2]?2U:3U;
64472 
64473       // Decompress nearest base font data if needed.
64474       CImg<ucharT> &basef = font_base[ind];
64475       if (!basef) {
64476         basef.assign(256*font_width[ind],font_height[ind]);
64477 
64478         unsigned char *ptrd = basef;
64479         const unsigned char *const ptrde = basef.end();
64480 
64481         // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb).
64482         CImg<char> dataf;
64483         for (unsigned int k = 0; k<font_chunk[ind]; ++k)
64484           dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x');
64485 
64486         // Uncompress font data (decode RLE).
64487         const unsigned int M = font_M[ind];
64488         if (font_is_binary[ind])
64489           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
64490             const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n;
64491             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
64492             else { std::memset(ptrd,v,ptrde - ptrd); break; }
64493           }
64494         else
64495           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
64496             int n = (int)*ptrs - M - 32, v = 0;
64497             if (n>=0) { v = 85*n; n = 1; }
64498             else {
64499               n = -n;
64500               v = (int)*(++ptrs) - M - 32;
64501               if (v<0) { v = 0; --ptrs; } else v*=85;
64502             }
64503             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
64504             else { std::memset(ptrd,v,ptrde - ptrd); break; }
64505           }
64506       }
64507 
64508       // Find optimal font cache location to return.
64509       static CImgList<ucharT> fonts[16];
64510       static bool is_variable_widths[16] = { 0 };
64511       ind = ~0U;
64512       for (int i = 0; i<16; ++i)
64513         if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) {
64514           ind = (unsigned int)i; break; // Found empty slot or cached font
64515         }
64516       if (ind==~0U) { // No empty slots nor existing font in cache
64517         fonts->assign();
64518         std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList<ucharT>));
64519         std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
64520         std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font
64521       }
64522       CImgList<ucharT> &font = fonts[ind];
64523 
64524       // Render requested font.
64525       if (!font) {
64526         is_variable_widths[ind] = is_variable_width;
64527         basef.get_split('x',256).move_to(font);
64528 
64529 //        cimg::tic();
64530 
64531         if (requested_height!=font[0]._height)
64532           cimglist_for(font,l) {
64533             font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5);
64534             cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr];
64535           }
64536 
64537 //        cimg::toc();
64538 //        std::exit(0);
64539 
64540         if (is_variable_width) { // Crop font
64541           cimglist_for(font,l) {
64542             CImg<ucharT>& letter = font[l];
64543             int xmin = letter.width(), xmax = 0;
64544             cimg_forX(letter,x) { // Find xmin
64545               cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; }
64546               if (xmin!=letter.width()) break;
64547             }
64548             cimg_rofX(letter,x) { // Find xmax
64549               cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; }
64550               if (xmax) break;
64551             }
64552             if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1);
64553           }
64554           font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0);
64555           if (' ' + 256<font.size()) font[' ' + 256].resize(font[(int)'f']._width,-100,-100,-100,0);
64556         }
64557         font.insert(256,0);
64558         cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
64559       }
64560       cimg::mutex(11,0);
64561       return font;
64562     }
64563 
64564     //! Compute a 1D Fast Fourier Transform, along specified axis.
64565     /**
64566        \param axis Axis along which the Fourier transform is computed.
64567        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
64568     **/
64569     CImgList<T>& FFT(const char axis, const bool invert=false) {
64570       if (is_empty()) return *this;
64571       if (_width==1) insert(1);
64572       if (_width>2)
64573         cimg::warn(_cimglist_instance
64574                    "FFT(): Instance has more than 2 images",
64575                    cimglist_instance);
64576       CImg<T>::FFT(_data[0],_data[1],axis,invert);
64577       return *this;
64578     }
64579 
64580     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
64581     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
64582       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
64583     }
64584 
64585     //! Compute n-D Fast Fourier Transform.
64586     /**
64587       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
64588     **/
64589     CImgList<T>& FFT(const bool invert=false) {
64590       if (is_empty()) return *this;
64591       if (_width==1) insert(1);
64592       if (_width>2)
64593         cimg::warn(_cimglist_instance
64594                    "FFT(): Instance has more than 2 images",
64595                    cimglist_instance);
64596 
64597       CImg<T>::FFT(_data[0],_data[1],invert);
64598       return *this;
64599     }
64600 
64601     //! Compute n-D Fast Fourier Transform \newinstance.
64602     CImgList<Tfloat> get_FFT(const bool invert=false) const {
64603       return CImgList<Tfloat>(*this,false).FFT(invert);
64604     }
64605 
64606     //! Reverse primitives orientations of a 3D object.
64607     /**
64608     **/
64609     CImgList<T>& reverse_object3d() {
64610       cimglist_for(*this,l) {
64611         CImg<T>& p = _data[l];
64612         switch (p.size()) {
64613         case 2 : case 3: cimg::swap(p[0],p[1]); break;
64614         case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
64615         case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
64616         case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
64617         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;
64618         }
64619       }
64620       return *this;
64621     }
64622 
64623     //! Reverse primitives orientations of a 3D object \newinstance.
64624     CImgList<T> get_reverse_object3d() const {
64625       return (+*this).reverse_object3d();
64626     }
64627 
64628     //@}
64629   }; // struct CImgList { ...
64630 
64631   // Completion of previously declared functions
64632   //--------------------------------------------
64633   namespace cimg {
64634 
64635     // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
64636     // (throw a CImgIOException when macro 'cimg_use_r' is defined).
64637     inline FILE* _stdin(const bool throw_exception) {
64638 #ifndef cimg_use_r
64639       cimg::unused(throw_exception);
64640       return stdin;
64641 #else
64642       if (throw_exception) {
64643         cimg::exception_mode(0);
64644         throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
64645                               "('cimg_use_r' is defined).");
64646       }
64647       return 0;
64648 #endif
64649     }
64650 
64651     inline FILE* _stdout(const bool throw_exception) {
64652 #ifndef cimg_use_r
64653       cimg::unused(throw_exception);
64654       return stdout;
64655 #else
64656       if (throw_exception) {
64657         cimg::exception_mode(0);
64658         throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
64659                               "('cimg_use_r' is defined).");
64660       }
64661       return 0;
64662 #endif
64663     }
64664 
64665     inline FILE* _stderr(const bool throw_exception) {
64666 #ifndef cimg_use_r
64667       cimg::unused(throw_exception);
64668       return stderr;
64669 #else
64670       if (throw_exception) {
64671         cimg::exception_mode(0);
64672         throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
64673                               "('cimg_use_r' is defined).");
64674       }
64675       return 0;
64676 #endif
64677     }
64678 
64679     // Open a file (similar to std:: fopen(), but with wide character support on Windows).
64680     inline std::FILE *std_fopen(const char *const path, const char *const mode) {
64681       std::FILE *const res = std::fopen(path,mode);
64682       if (res) return res;
64683 #if cimg_OS==2
64684       // Try alternative method, with wide-character string.
64685       int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
64686       if (err) {
64687         CImg<wchar_t> wpath((unsigned int)err);
64688         err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
64689         if (err) { // Convert 'mode' to a wide-character string
64690           err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
64691           if (err) {
64692             CImg<wchar_t> wmode((unsigned int)err);
64693             if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err))
64694               return _wfopen(wpath,wmode);
64695           }
64696         }
64697       }
64698 #endif
64699       return 0;
64700     }
64701 
64702     //! Search path of an executable (Windows only).
64703 #if cimg_OS==2
64704     inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
64705       char *ptr = 0;
64706       DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr);
64707       return err!=0;
64708     }
64709 #endif
64710 
64711     //! Get the file or directory attributes with support for UTF-8 paths (Windows only).
64712 #if cimg_OS==2
64713     inline DWORD win_getfileattributes(const char *const path) {
64714       DWORD res = GetFileAttributesA(path);
64715       if (res==INVALID_FILE_ATTRIBUTES) {
64716         // Try alternative method, with wide-character string.
64717         int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
64718         if (err) {
64719           CImg<wchar_t> wpath((unsigned int)err);
64720           if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath);
64721         }
64722       }
64723       return res;
64724     }
64725 #endif
64726 
64727     //! Get/set path to the <i>Program Files/</i> directory (Windows only).
64728     /**
64729        \param user_path Specified path, or \c 0 to get the path currently used.
64730        \param reinit_path Force path to be recalculated (may take some time).
64731        \return Path containing the program files.
64732     **/
64733 #if cimg_OS==2
64734     inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
64735       static CImg<char> s_path;
64736       cimg::mutex(7);
64737       if (reinit_path) s_path.assign();
64738       if (user_path) {
64739         if (!s_path) s_path.assign(1024);
64740         std::strncpy(s_path,user_path,1023);
64741       } else if (!s_path) {
64742         s_path.assign(MAX_PATH);
64743         *s_path = 0;
64744         // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
64745 #if !defined(__INTEL_COMPILER)
64746         if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
64747           const char *const pfPath = std::getenv("PROGRAMFILES");
64748           if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
64749           else std::strcpy(s_path,"C:\\PROGRA~1");
64750         }
64751 #else
64752         std::strcpy(s_path,"C:\\PROGRA~1");
64753 #endif
64754       }
64755       cimg::mutex(7,0);
64756       return s_path;
64757     }
64758 #endif
64759 
64760     //! Get/set path to the \c curl binary.
64761     /**
64762        \param user_path Specified path, or \c 0 to get the path currently used.
64763        \param reinit_path Force path to be recalculated (may take some time).
64764        \return Path containing the \c curl binary.
64765     **/
64766     inline const char *curl_path(const char *const user_path, const bool reinit_path) {
64767       static CImg<char> s_path;
64768       cimg::mutex(7);
64769       if (reinit_path) s_path.assign();
64770       if (user_path) {
64771         if (!s_path) s_path.assign(1024);
64772         std::strncpy(s_path,user_path,1023);
64773       } else if (!s_path) {
64774         s_path.assign(1024);
64775         bool path_found = false;
64776         std::FILE *file = 0;
64777 #if cimg_OS==2
64778         if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true;
64779         if (!path_found) {
64780           std::strcpy(s_path,".\\curl.exe");
64781           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64782         }
64783         if (!path_found) std::strcpy(s_path,"curl.exe");
64784 #else
64785         if (!path_found) {
64786           std::strcpy(s_path,"./curl");
64787           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64788         }
64789         if (!path_found) std::strcpy(s_path,"curl");
64790 #endif
64791         winformat_string(s_path);
64792       }
64793       cimg::mutex(7,0);
64794       return s_path;
64795     }
64796 
64797     //! Get/set path to the \c dcraw binary.
64798     /**
64799        \param user_path Specified path, or \c 0 to get the path currently used.
64800        \param reinit_path Force path to be recalculated (may take some time).
64801        \return Path containing the \c dcraw binary.
64802     **/
64803     inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
64804       static CImg<char> s_path;
64805       cimg::mutex(7);
64806       if (reinit_path) s_path.assign();
64807       if (user_path) {
64808         if (!s_path) s_path.assign(1024);
64809         std::strncpy(s_path,user_path,1023);
64810       } else if (!s_path) {
64811         s_path.assign(1024);
64812         bool path_found = false;
64813         std::FILE *file = 0;
64814 #if cimg_OS==2
64815         if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true;
64816         if (!path_found) {
64817           std::strcpy(s_path,".\\dcraw.exe");
64818           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64819         }
64820         if (!path_found) std::strcpy(s_path,"dcraw.exe");
64821 #else
64822         if (!path_found) {
64823           std::strcpy(s_path,"./dcraw");
64824           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64825         }
64826         if (!path_found) std::strcpy(s_path,"dcraw");
64827 #endif
64828         winformat_string(s_path);
64829       }
64830       cimg::mutex(7,0);
64831       return s_path;
64832     }
64833 
64834     //! Get/set path to the FFMPEG's \c ffmpeg binary.
64835     /**
64836        \param user_path Specified path, or \c 0 to get the path currently used.
64837        \param reinit_path Force path to be recalculated (may take some time).
64838        \return Path containing the \c ffmpeg binary.
64839     **/
64840     inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
64841       static CImg<char> s_path;
64842       cimg::mutex(7);
64843       if (reinit_path) s_path.assign();
64844       if (user_path) {
64845         if (!s_path) s_path.assign(1024);
64846         std::strncpy(s_path,user_path,1023);
64847       } else if (!s_path) {
64848         s_path.assign(1024);
64849         bool path_found = false;
64850         std::FILE *file = 0;
64851 #if cimg_OS==2
64852         if (win_searchpath("ffmpeg.exe",s_path,s_path._width)) path_found = true;
64853         if (!path_found) {
64854           std::strcpy(s_path,".\\ffmpeg.exe");
64855           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64856         }
64857         if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
64858 #else
64859         if (!path_found) {
64860           std::strcpy(s_path,"./ffmpeg");
64861           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64862         }
64863         if (!path_found) std::strcpy(s_path,"ffmpeg");
64864 #endif
64865         winformat_string(s_path);
64866       }
64867       cimg::mutex(7,0);
64868       return s_path;
64869     }
64870 
64871     //! Get/set path to the GraphicsMagick's \c gm binary.
64872     /**
64873        \param user_path Specified path, or \c 0 to get the path currently used.
64874        \param reinit_path Force path to be recalculated (may take some time).
64875        \return Path containing the \c gm binary.
64876     **/
64877     inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
64878       static CImg<char> s_path;
64879       cimg::mutex(7);
64880       if (reinit_path) s_path.assign();
64881       if (user_path) {
64882         if (!s_path) s_path.assign(1024);
64883         std::strncpy(s_path,user_path,1023);
64884       } else if (!s_path) {
64885         s_path.assign(1024);
64886         bool path_found = false;
64887         std::FILE *file = 0;
64888 #if cimg_OS==2
64889         if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true;
64890         const char *const pf_path = win_programfiles_path();
64891         if (!path_found) {
64892           std::strcpy(s_path,".\\gm.exe");
64893           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64894         }
64895         for (int k = 32; k>=10 && !path_found; --k) {
64896           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
64897           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64898         }
64899         for (int k = 9; k>=0 && !path_found; --k) {
64900           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
64901           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64902         }
64903         for (int k = 32; k>=0 && !path_found; --k) {
64904           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
64905           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64906         }
64907         for (int k = 32; k>=10 && !path_found; --k) {
64908           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64909           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64910         }
64911         for (int k = 9; k>=0 && !path_found; --k) {
64912           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64913           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64914         }
64915         for (int k = 32; k>=0 && !path_found; --k) {
64916           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
64917           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64918         }
64919         for (int k = 32; k>=10 && !path_found; --k) {
64920           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
64921           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64922         }
64923         for (int k = 9; k>=0 && !path_found; --k) {
64924           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
64925           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64926         }
64927         for (int k = 32; k>=0 && !path_found; --k) {
64928           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
64929           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64930         }
64931         for (int k = 32; k>=10 && !path_found; --k) {
64932           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
64933           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64934         }
64935         for (int k = 9; k>=0 && !path_found; --k) {
64936           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
64937           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64938         }
64939         for (int k = 32; k>=0 && !path_found; --k) {
64940           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
64941           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64942         }
64943         for (int k = 32; k>=10 && !path_found; --k) {
64944           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
64945           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64946         }
64947         for (int k = 9; k>=0 && !path_found; --k) {
64948           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
64949           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64950         }
64951         for (int k = 32; k>=0 && !path_found; --k) {
64952           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
64953           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64954         }
64955         for (int k = 32; k>=10 && !path_found; --k) {
64956           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
64957           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64958         }
64959         for (int k = 9; k>=0 && !path_found; --k) {
64960           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
64961           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64962         }
64963         for (int k = 32; k>=0 && !path_found; --k) {
64964           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
64965           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64966         }
64967         if (!path_found) std::strcpy(s_path,"gm.exe");
64968 #else
64969         if (!path_found) {
64970           std::strcpy(s_path,"./gm");
64971           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64972         }
64973         if (!path_found) std::strcpy(s_path,"gm");
64974 #endif
64975         winformat_string(s_path);
64976       }
64977       cimg::mutex(7,0);
64978       return s_path;
64979     }
64980 
64981     //! Get/set path to the \c gunzip binary.
64982     /**
64983        \param user_path Specified path, or \c 0 to get the path currently used.
64984        \param reinit_path Force path to be recalculated (may take some time).
64985        \return Path containing the \c gunzip binary.
64986     **/
64987     inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
64988       static CImg<char> s_path;
64989       cimg::mutex(7);
64990       if (reinit_path) s_path.assign();
64991       if (user_path) {
64992         if (!s_path) s_path.assign(1024);
64993         std::strncpy(s_path,user_path,1023);
64994       } else if (!s_path) {
64995         s_path.assign(1024);
64996         bool path_found = false;
64997         std::FILE *file = 0;
64998 #if cimg_OS==2
64999         if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true;
65000         if (!path_found) {
65001           std::strcpy(s_path,".\\gunzip.exe");
65002           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65003         }
65004         if (!path_found) std::strcpy(s_path,"gunzip.exe");
65005 #else
65006         if (!path_found) {
65007           std::strcpy(s_path,"./gunzip");
65008           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65009         }
65010         if (!path_found) std::strcpy(s_path,"gunzip");
65011 #endif
65012         winformat_string(s_path);
65013       }
65014       cimg::mutex(7,0);
65015       return s_path;
65016     }
65017 
65018     //! Get/set path to the \c gzip binary.
65019     /**
65020        \param user_path Specified path, or \c 0 to get the path currently used.
65021        \param reinit_path Force path to be recalculated (may take some time).
65022        \return Path containing the \c gzip binary.
65023     **/
65024     inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
65025       static CImg<char> s_path;
65026       cimg::mutex(7);
65027       if (reinit_path) s_path.assign();
65028       if (user_path) {
65029         if (!s_path) s_path.assign(1024);
65030         std::strncpy(s_path,user_path,1023);
65031       } else if (!s_path) {
65032         s_path.assign(1024);
65033         bool path_found = false;
65034         std::FILE *file = 0;
65035 #if cimg_OS==2
65036         if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true;
65037         if (!path_found) {
65038           std::strcpy(s_path,".\\gzip.exe");
65039           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65040         }
65041         if (!path_found) std::strcpy(s_path,"gzip.exe");
65042 #else
65043         if (!path_found) {
65044           std::strcpy(s_path,"./gzip");
65045           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65046         }
65047         if (!path_found) std::strcpy(s_path,"gzip");
65048 #endif
65049         winformat_string(s_path);
65050       }
65051       cimg::mutex(7,0);
65052       return s_path;
65053     }
65054 
65055     //! Get/set path to the ImageMagick's \c convert binary.
65056     /**
65057        \param user_path Specified path, or \c 0 to get the path currently used.
65058        \param reinit_path Force path to be recalculated (may take some time).
65059        \return Path containing the \c convert binary.
65060     **/
65061     inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
65062       static CImg<char> s_path;
65063       cimg::mutex(7);
65064       if (reinit_path) s_path.assign();
65065       if (user_path) {
65066         if (!s_path) s_path.assign(1024);
65067         std::strncpy(s_path,user_path,1023);
65068       } else if (!s_path) {
65069         s_path.assign(1024);
65070         bool path_found = false;
65071         std::FILE *file = 0;
65072 #if cimg_OS==2
65073         if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
65074         const char *const pf_path = win_programfiles_path();
65075         for (int l = 0; l<2 && !path_found; ++l) {
65076           const char *const s_exe = l?"convert":"magick";
65077           cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
65078           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65079           for (int k = 32; k>=10 && !path_found; --k) {
65080             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
65081             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65082           }
65083           for (int k = 9; k>=0 && !path_found; --k) {
65084             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
65085             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65086           }
65087           for (int k = 32; k>=0 && !path_found; --k) {
65088             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
65089             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65090           }
65091           for (int k = 32; k>=10 && !path_found; --k) {
65092             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
65093             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65094           }
65095           for (int k = 9; k>=0 && !path_found; --k) {
65096             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
65097             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65098           }
65099           for (int k = 32; k>=0 && !path_found; --k) {
65100             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
65101             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65102           }
65103           for (int k = 32; k>=10 && !path_found; --k) {
65104             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
65105             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65106           }
65107           for (int k = 9; k>=0 && !path_found; --k) {
65108             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
65109             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65110           }
65111           for (int k = 32; k>=0 && !path_found; --k) {
65112             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
65113             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65114           }
65115           for (int k = 32; k>=10 && !path_found; --k) {
65116             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65117             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65118           }
65119           for (int k = 9; k>=0 && !path_found; --k) {
65120             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65121             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65122           }
65123           for (int k = 32; k>=0 && !path_found; --k) {
65124             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65125             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65126           }
65127           for (int k = 32; k>=10 && !path_found; --k) {
65128             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
65129             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65130           }
65131           for (int k = 9; k>=0 && !path_found; --k) {
65132             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
65133             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65134           }
65135           for (int k = 32; k>=0 && !path_found; --k) {
65136             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
65137             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65138           }
65139           for (int k = 32; k>=10 && !path_found; --k) {
65140             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65141             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65142           }
65143           for (int k = 9; k>=0 && !path_found; --k) {
65144             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65145             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65146           }
65147           for (int k = 32; k>=0 && !path_found; --k) {
65148             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
65149             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65150           }
65151           if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
65152         }
65153 #else
65154         std::strcpy(s_path,"./magick");
65155         if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65156         if (!path_found) {
65157           std::strcpy(s_path,"./convert");
65158           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65159         }
65160         if (!path_found) std::strcpy(s_path,"convert");
65161 #endif
65162         winformat_string(s_path);
65163       }
65164       cimg::mutex(7,0);
65165       return s_path;
65166     }
65167 
65168     //! Get/set path to the Medcon's \c medcon binary.
65169     /**
65170        \param user_path Specified path, or \c 0 to get the path currently used.
65171        \param reinit_path Force path to be recalculated (may take some time).
65172        \return Path containing the \c medcon binary.
65173     **/
65174     inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
65175       static CImg<char> s_path;
65176       cimg::mutex(7);
65177       if (reinit_path) s_path.assign();
65178       if (user_path) {
65179         if (!s_path) s_path.assign(1024);
65180         std::strncpy(s_path,user_path,1023);
65181       } else if (!s_path) {
65182         s_path.assign(1024);
65183         bool path_found = false;
65184         std::FILE *file = 0;
65185 #if cimg_OS==2
65186         if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true;
65187         const char *const pf_path = win_programfiles_path();
65188         if (!path_found) {
65189           std::strcpy(s_path,".\\medcon.exe");
65190           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65191         }
65192         if (!path_found) {
65193           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
65194           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65195         }
65196         if (!path_found) {
65197           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
65198           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65199         }
65200         if (!path_found) {
65201           std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
65202           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65203         }
65204         if (!path_found) std::strcpy(s_path,"medcon.exe");
65205 #else
65206         if (!path_found) {
65207           std::strcpy(s_path,"./medcon");
65208           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65209         }
65210         if (!path_found) std::strcpy(s_path,"medcon");
65211 #endif
65212         winformat_string(s_path);
65213       }
65214       cimg::mutex(7,0);
65215       return s_path;
65216     }
65217 
65218     //! Get/set path to store temporary files.
65219     /**
65220        \param user_path Specified path, or \c 0 to get the path currently used.
65221        \param reinit_path Force path to be recalculated (may take some time).
65222        \return Path where temporary files can be saved.
65223     **/
65224     inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
65225 #define _cimg_test_temporary_path(p) \
65226       if (!path_found) { \
65227         cimg_snprintf(s_path,s_path._width,"%s",p); \
65228         cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
65229         if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
65230       }
65231       static CImg<char> s_path;
65232       cimg::mutex(7);
65233       if (reinit_path) s_path.assign();
65234       if (user_path) {
65235         if (!s_path) s_path.assign(1024);
65236         std::strncpy(s_path,user_path,1023);
65237       } else if (!s_path) {
65238         s_path.assign(1024);
65239         bool path_found = false;
65240         CImg<char> tmp(1024), filename_tmp(256);
65241         std::FILE *file = 0;
65242         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
65243         char *tmpPath = std::getenv("TMP");
65244         if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
65245         if (tmpPath) _cimg_test_temporary_path(tmpPath);
65246 #if cimg_OS==2
65247         _cimg_test_temporary_path("C:\\WINNT\\Temp");
65248         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
65249         _cimg_test_temporary_path("C:\\Temp");
65250         _cimg_test_temporary_path("C:");
65251         _cimg_test_temporary_path("D:\\WINNT\\Temp");
65252         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
65253         _cimg_test_temporary_path("D:\\Temp");
65254         _cimg_test_temporary_path("D:");
65255 #else
65256         _cimg_test_temporary_path("/tmp");
65257         _cimg_test_temporary_path("/var/tmp");
65258 #endif
65259         if (!path_found) {
65260           *s_path = 0;
65261           std::strncpy(tmp,filename_tmp,tmp._width - 1);
65262           if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
65263         }
65264         if (!path_found) {
65265           cimg::mutex(7,0);
65266           throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
65267         }
65268       }
65269       cimg::mutex(7,0);
65270       return s_path;
65271     }
65272 
65273     //! Get/set path to the \c wget binary.
65274     /**
65275        \param user_path Specified path, or \c 0 to get the path currently used.
65276        \param reinit_path Force path to be recalculated (may take some time).
65277        \return Path containing the \c wget binary.
65278     **/
65279     inline const char *wget_path(const char *const user_path, const bool reinit_path) {
65280       static CImg<char> s_path;
65281       cimg::mutex(7);
65282       if (reinit_path) s_path.assign();
65283       if (user_path) {
65284         if (!s_path) s_path.assign(1024);
65285         std::strncpy(s_path,user_path,1023);
65286       } else if (!s_path) {
65287         s_path.assign(1024);
65288         bool path_found = false;
65289         std::FILE *file = 0;
65290 #if cimg_OS==2
65291         if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true;
65292         if (!path_found) {
65293           std::strcpy(s_path,".\\wget.exe");
65294           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65295         }
65296         if (!path_found) std::strcpy(s_path,"wget.exe");
65297 #else
65298         if (!path_found) {
65299           std::strcpy(s_path,"./wget");
65300           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
65301         }
65302         if (!path_found) std::strcpy(s_path,"wget");
65303 #endif
65304         winformat_string(s_path);
65305       }
65306       cimg::mutex(7,0);
65307       return s_path;
65308     }
65309 
65310 
65311     // [internal] Sorting function, used by cimg::files().
65312     inline int _sort_files(const void* a, const void* b) {
65313       const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
65314       return std::strcmp(sa._data,sb._data);
65315     }
65316 
65317     //! Generate a numbered version of a filename.
65318     inline char* number_filename(const char *const filename, const int number,
65319                                  const unsigned int digits, char *const str) {
65320       if (!filename) { if (str) *str = 0; return 0; }
65321       const unsigned int siz = (unsigned int)std::strlen(filename);
65322       CImg<char> format(16), body(siz + 32);
65323       const char *const ext = cimg::split_filename(filename,body);
65324       if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits);
65325       else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits);
65326       cimg_snprintf(str,1024,format._data,body._data,number,ext);
65327       return str;
65328     }
65329 
65330     //! Return list of files/directories in specified directory.
65331     /**
65332        \param path Path to the directory. Set to 0 for current directory.
65333        \param is_pattern Tell if specified path has a matching pattern in it.
65334        \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
65335        \param include_path Tell if \c path must be included in resulting filenames.
65336        \return A list of filenames.
65337     **/
65338     inline CImgList<char> files(const char *const path, const bool is_pattern=false,
65339                                 const unsigned int mode=2, const bool include_path=false) {
65340       if (!path || !*path) return files("*",true,mode,include_path);
65341       CImgList<char> res;
65342 
65343       // If path is a valid folder name, ignore argument 'is_pattern'.
65344       const bool _is_pattern = is_pattern && !cimg::is_directory(path);
65345       bool is_root = false, is_current = false;
65346       cimg::unused(is_root,is_current);
65347 
65348       // Clean format of input path.
65349       CImg<char> pattern, _path = CImg<char>::string(path);
65350 #if cimg_OS==2
65351       for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
65352 #endif
65353       char *pd = _path;
65354       for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
65355       *pd = 0;
65356       unsigned int lp = (unsigned int)std::strlen(_path);
65357       if (!_is_pattern && lp && _path[lp - 1]=='/') {
65358         _path[lp - 1] = 0; --lp;
65359 #if cimg_OS!=2
65360         is_root = !*_path;
65361 #endif
65362       }
65363 
65364       // Separate folder path and matching pattern.
65365       if (_is_pattern) {
65366         const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
65367         CImg<char>::string(_path).move_to(pattern);
65368         if (bpos) {
65369           _path[bpos - 1] = 0; // End 'path' at last slash
65370 #if cimg_OS!=2
65371           is_root = !*_path;
65372 #endif
65373         } else { // No path to folder specified, assuming current folder
65374           is_current = true; *_path = 0;
65375         }
65376         lp = (unsigned int)std::strlen(_path);
65377       }
65378 
65379       // Windows version.
65380 #if cimg_OS==2
65381       if (!_is_pattern) {
65382         pattern.assign(lp + 3);
65383         std::memcpy(pattern,_path,lp);
65384         pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
65385       }
65386       WIN32_FIND_DATAA file_data;
65387       const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
65388       if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
65389       do {
65390         const char *const filename = file_data.cFileName;
65391         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
65392           const unsigned int lf = (unsigned int)std::strlen(filename);
65393           const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
65394           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
65395             if (include_path) {
65396               CImg<char> full_filename((lp?lp+1:0) + lf + 1);
65397               if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
65398               std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
65399               full_filename.move_to(res);
65400             } else CImg<char>(filename,lf + 1).move_to(res);
65401           }
65402         }
65403       } while (FindNextFileA(dir,&file_data));
65404       FindClose(dir);
65405 
65406       // Unix version (posix).
65407 #elif cimg_OS == 1
65408       DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
65409       if (!dir) return CImgList<char>::const_empty();
65410       struct dirent *ent;
65411       while ((ent=readdir(dir))!=0) {
65412         const char *const filename = ent->d_name;
65413         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
65414           const unsigned int lf = (unsigned int)std::strlen(filename);
65415           CImg<char> full_filename(lp + lf + 2);
65416 
65417           if (!is_current) {
65418             full_filename.assign(lp + lf + 2);
65419             if (lp) std::memcpy(full_filename,_path,lp);
65420             full_filename[lp] = '/';
65421             std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
65422           } else full_filename.assign(filename,lf + 1);
65423 
65424           struct stat st;
65425           if (stat(full_filename,&st)==-1) continue;
65426           const bool is_directory = (st.st_mode & S_IFDIR)!=0;
65427           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
65428             if (include_path) {
65429               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
65430                 full_filename.move_to(res);
65431             } else {
65432               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
65433                 CImg<char>(filename,lf + 1).move_to(res);
65434             }
65435           }
65436         }
65437       }
65438       closedir(dir);
65439 #endif
65440 
65441       // Sort resulting list by lexicographic order.
65442       if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
65443 
65444       return res;
65445     }
65446 
65447     //! Try to guess format from an image file.
65448     /**
65449        \param file Input file (can be \c 0 if \c filename is set).
65450        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
65451        \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
65452     **/
65453     inline const char *ftype(std::FILE *const file, const char *const filename) {
65454       if (!file && !filename)
65455         throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
65456       static const char
65457         *const _bmp = "bmp",
65458         *const _cr2 = "cr2",
65459         *const _dcm = "dcm",
65460         *const _gif = "gif",
65461         *const _inr = "inr",
65462         *const _jpg = "jpg",
65463         *const _off = "off",
65464         *const _pan = "pan",
65465         *const _pfm = "pfm",
65466         *const _png = "png",
65467         *const _pnm = "pnm",
65468         *const _tif = "tif";
65469 
65470       const char *f_type = 0;
65471       CImg<char> header;
65472       const unsigned int omode = cimg::exception_mode();
65473       cimg::exception_mode(0);
65474       try {
65475         header._load_raw(file,filename,512,1,1,1,false,false,0);
65476         const unsigned char *const uheader = (unsigned char*)header._data;
65477         if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF
65478         else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE
65479           f_type = _inr;
65480         else if (!std::strncmp(header,"PANDORE",7)) // PANDORE
65481           f_type = _pan;
65482         else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM
65483           f_type = _dcm;
65484         else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG
65485           f_type = _jpg;
65486         else if (header[0]=='B' && header[1]=='M') // BMP
65487           f_type = _bmp;
65488         else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' &&
65489                  (header[4]=='7' || header[4]=='9')) // GIF
65490           f_type = _gif;
65491         else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&
65492                  uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG
65493           f_type = _png;
65494         else if (uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00 && // CR2
65495                  uheader[4]==0x10 && uheader[5]==0x00 && uheader[6]==0x00 && uheader[7]==0x00 &&
65496                  uheader[8]==0x43 && uheader[9]==0x52)
65497           f_type = _cr2;
65498         else if ((uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00) ||
65499                  (uheader[0]==0x4D && uheader[1]==0x4D && uheader[2]==0x00 && uheader[3]==0x2A)) // TIFF
65500           f_type = _tif;
65501         else { // PNM or PFM
65502           CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
65503           cimglist_for(_header,l) {
65504             if (_header(l,0)=='#') continue;
65505             if (_header[l]._width==2 && _header(l,0)=='P') {
65506               const char c = _header(l,1);
65507               if (c=='f' || c=='F') { f_type = _pfm; break; }
65508               if (c>='1' && c<='9') { f_type = _pnm; break; }
65509             }
65510             f_type = 0; break;
65511           }
65512         }
65513       } catch (CImgIOException&) { }
65514       cimg::exception_mode(omode);
65515       return f_type;
65516     }
65517 
65518     //! Load file from network as a local temporary file.
65519     /**
65520        \param url URL of the filename, as a C-string.
65521        \param[out] filename_local C-string containing the path to a local copy of \c filename.
65522        \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
65523        \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
65524        \param referer Referer used, as a C-string.
65525        \return Value of \c filename_local.
65526        \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
65527     **/
65528     inline char *load_network(const char *const url, char *const filename_local,
65529                               const unsigned int timeout, const bool try_fallback,
65530                               const char *const referer) {
65531       if (!url)
65532         throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
65533       if (!filename_local)
65534         throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
65535       if (!network_mode())
65536         throw CImgIOException("cimg::load_network(): Loading files from network is disabled.");
65537 
65538       const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
65539       CImg<char> ext = CImg<char>::string(_ext);
65540       std::FILE *file = 0;
65541       *filename_local = 0;
65542       if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
65543       else cimg::strwindows_reserved(ext);
65544       do {
65545         cimg_snprintf(filename_local,256,"%s%c%s%s",
65546                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
65547         if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
65548       } while (file);
65549 
65550 #ifdef cimg_use_curl
65551       const unsigned int omode = cimg::exception_mode();
65552       cimg::exception_mode(0);
65553       try {
65554         CURL *curl = 0;
65555         CURLcode res;
65556         curl = curl_easy_init();
65557         if (curl) {
65558           file = cimg::fopen(filename_local,"wb");
65559           curl_easy_setopt(curl,CURLOPT_URL,url);
65560           curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
65561           curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
65562           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
65563           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
65564           curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
65565           if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
65566           if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
65567           if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
65568           res = curl_easy_perform(curl);
65569           curl_easy_cleanup(curl);
65570           cimg::fseek(file,0,SEEK_END); // Check if file size is 0
65571           const cimg_ulong siz = cimg::ftell(file);
65572           cimg::fclose(file);
65573           if (siz>0 && res==CURLE_OK) {
65574             cimg::exception_mode(omode);
65575             return filename_local;
65576           } else std::remove(filename_local);
65577         }
65578       } catch (...) { }
65579       cimg::exception_mode(omode);
65580       if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
65581 #endif
65582 
65583       CImg<char> command((unsigned int)std::strlen(url) + 64);
65584       cimg::unused(try_fallback);
65585 
65586       // Try with 'curl' first.
65587       if (timeout) {
65588         if (referer)
65589           cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
65590                         cimg::curl_path(),referer,timeout,filename_local,
65591                         CImg<char>::string(url)._system_strescape().data());
65592         else
65593           cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"",
65594                         cimg::curl_path(),timeout,filename_local,
65595                         CImg<char>::string(url)._system_strescape().data());
65596       } else {
65597         if (referer)
65598           cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"",
65599                         cimg::curl_path(),referer,filename_local,
65600                         CImg<char>::string(url)._system_strescape().data());
65601         else
65602           cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"",
65603                         cimg::curl_path(),filename_local,
65604                         CImg<char>::string(url)._system_strescape().data());
65605       }
65606       cimg::system(command, cimg::curl_path());
65607 
65608       if (!(file=cimg::std_fopen(filename_local,"rb"))) {
65609 
65610         // Try with 'wget' otherwise.
65611         if (timeout) {
65612           if (referer)
65613             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65614                           cimg::wget_path(),referer,timeout,filename_local,
65615                           CImg<char>::string(url)._system_strescape().data());
65616           else
65617             cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65618                           cimg::wget_path(),timeout,filename_local,
65619                           CImg<char>::string(url)._system_strescape().data());
65620         } else {
65621           if (referer)
65622             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65623                           cimg::wget_path(),referer,filename_local,
65624                           CImg<char>::string(url)._system_strescape().data());
65625           else
65626             cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
65627                           cimg::wget_path(),filename_local,
65628                           CImg<char>::string(url)._system_strescape().data());
65629         }
65630         cimg::system(command, cimg::wget_path());
65631 
65632         if (!(file=cimg::std_fopen(filename_local,"rb")))
65633           throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
65634                                 "'wget' or 'curl'.",url);
65635         cimg::fclose(file);
65636 
65637         // Try gunzip it.
65638         cimg_snprintf(command,command._width,"%s.gz",filename_local);
65639         std::rename(filename_local,command);
65640         cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"",
65641                       gunzip_path(),filename_local);
65642         cimg::system(command, gunzip_path());
65643         file = cimg::std_fopen(filename_local,"rb");
65644         if (!file) {
65645           cimg_snprintf(command,command._width,"%s.gz",filename_local);
65646           std::rename(command,filename_local);
65647           file = cimg::std_fopen(filename_local,"rb");
65648         }
65649       }
65650       cimg::fseek(file,0,SEEK_END); // Check if file size is 0
65651       if (std::ftell(file)<=0)
65652         throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
65653                               "'wget' or 'curl'.",url);
65654       cimg::fclose(file);
65655       return filename_local;
65656     }
65657 
65658     // Implement a tic/toc mechanism to display elapsed time of algorithms.
65659     inline cimg_uint64 tictoc(const bool is_tic) {
65660       cimg::mutex(2);
65661       static CImg<cimg_uint64> times(64);
65662       static unsigned int pos = 0;
65663       const cimg_uint64 t1 = cimg::time();
65664       if (is_tic) {
65665         // Tic
65666         times[pos++] = t1;
65667         if (pos>=times._width)
65668           throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
65669         cimg::mutex(2,0);
65670         return t1;
65671       }
65672 
65673       // Toc
65674       if (!pos)
65675         throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
65676       const cimg_uint64
65677         t0 = times[--pos],
65678         dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
65679       const unsigned int
65680         edays = (unsigned int)(dt/86400000.),
65681         ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
65682         emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
65683         esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
65684         ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
65685       if (!edays && !ehours && !emin && !esec)
65686         std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
65687                      cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
65688       else {
65689         if (!edays && !ehours && !emin)
65690           std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
65691                        cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
65692         else {
65693           if (!edays && !ehours)
65694             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
65695                          cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
65696           else{
65697             if (!edays)
65698               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
65699                            cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
65700             else{
65701               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
65702                            cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
65703             }
65704           }
65705         }
65706       }
65707       cimg::mutex(2,0);
65708       return dt;
65709     }
65710 
65711     // Return a temporary string describing the size of a memory buffer.
65712     inline const char *strbuffersize(const cimg_ulong size) {
65713       static CImg<char> res(256);
65714       cimg::mutex(5);
65715       if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
65716       else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
65717       else if (size<1024*1024*1024LU) {
65718         const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
65719       } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
65720       cimg::mutex(5,0);
65721       return res;
65722     }
65723 
65724     //! Display a simple dialog box, and wait for the user's response.
65725     /**
65726        \param title Title of the dialog window.
65727        \param msg Main message displayed inside the dialog window.
65728        \param button1_label Label of the 1st button.
65729        \param button2_label Label of the 2nd button (\c 0 to hide button).
65730        \param button3_label Label of the 3rd button (\c 0 to hide button).
65731        \param button4_label Label of the 4th button (\c 0 to hide button).
65732        \param button5_label Label of the 5th button (\c 0 to hide button).
65733        \param button6_label Label of the 6th button (\c 0 to hide button).
65734        \param logo Image logo displayed at the left of the main message.
65735        \param is_centered Tells if the dialog window must be centered on the screen.
65736        \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
65737        \note
65738        - Up to 6 buttons can be defined in the dialog window.
65739        - The function returns when a user clicked one of the button or closed the dialog window.
65740        - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box.
65741        At least one button must be specified.
65742     **/
65743     template<typename t>
65744     inline int dialog(const char *const title, const char *const msg,
65745                       const char *const button1_label, const char *const button2_label,
65746                       const char *const button3_label, const char *const button4_label,
65747                       const char *const button5_label, const char *const button6_label,
65748                       const CImg<t>& logo, const bool is_centered=false) {
65749 #if cimg_display==0
65750       cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
65751                    logo._data,is_centered);
65752       throw CImgIOException("cimg::dialog(): No display available.");
65753 #else
65754       static const unsigned char
65755         black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
65756 
65757       // Create buttons and canvas graphics
65758       CImgList<unsigned char> buttons, cbuttons, sbuttons;
65759       if (button1_label) {
65760         CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
65761         if (button2_label) {
65762           CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
65763           if (button3_label) {
65764             CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
65765             if (button4_label) {
65766               CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
65767               if (button5_label) {
65768                 CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
65769                 if (button6_label) {
65770                   CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
65771                 }}}}}}
65772       if (!buttons._width)
65773         throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
65774       cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
65775 
65776       unsigned int bw = 0, bh = 0;
65777       cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
65778       bw+=8; bh+=8;
65779       if (bw<64) bw = 64;
65780       if (bw>128) bw = 128;
65781       if (bh<24) bh = 24;
65782       if (bh>48) bh = 48;
65783 
65784       CImg<unsigned char> button(bw,bh,1,3);
65785       button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
65786       button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
65787       button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
65788       button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
65789       CImg<unsigned char> sbutton(bw,bh,1,3);
65790       sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
65791       sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
65792       sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
65793       sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
65794       sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
65795       sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
65796       sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
65797         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
65798       sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
65799         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
65800       CImg<unsigned char> cbutton(bw,bh,1,3);
65801       cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
65802         draw_rectangle(2,2,bw - 3,bh - 3,gray);
65803       cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
65804         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
65805       cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
65806         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
65807 
65808       cimglist_for(buttons,ll) {
65809         CImg<unsigned char>(cbutton).
65810           draw_image(1 + (bw  -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
65811           move_to(cbuttons);
65812         CImg<unsigned char>(sbutton).
65813           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
65814           move_to(sbuttons);
65815         CImg<unsigned char>(button).
65816           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
65817           move_to(buttons[ll]);
65818       }
65819 
65820       CImg<unsigned char> canvas;
65821       if (msg)
65822         ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
65823 
65824       const unsigned int
65825         bwall = (buttons._width - 1)*(12 + bw) + bw,
65826         w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
65827         h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
65828         lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
65829         ly = (h - 12 - bh - logo._height)/2,
65830         tx = lx + logo._width + 12,
65831         ty = (h - 12 - bh - canvas._height)/2,
65832         bx = (w - bwall)/2,
65833         by = h - 12 - bh;
65834 
65835       if (canvas._data)
65836         canvas = CImg<unsigned char>(w,h,1,3).
65837           draw_rectangle(0,0,w - 1,h - 1,gray).
65838           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
65839           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
65840           draw_image(tx,ty,canvas);
65841       else
65842         canvas = CImg<unsigned char>(w,h,1,3).
65843           draw_rectangle(0,0,w - 1,h - 1,gray).
65844           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
65845           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
65846       if (logo._data) canvas.draw_image(lx,ly,logo);
65847 
65848       unsigned int xbuttons[6] = { 0 };
65849       cimglist_for(buttons,lll) {
65850         xbuttons[lll] = bx + (bw + 12)*lll;
65851         canvas.draw_image(xbuttons[lll],by,buttons[lll]);
65852       }
65853 
65854       // Open window and enter events loop
65855       CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
65856       if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
65857                                  (CImgDisplay::screen_height() - disp.height())/2);
65858       bool stop_flag = false, refresh = false;
65859       int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
65860       while (!disp.is_closed() && !stop_flag) {
65861         if (refresh) {
65862           if (clicked>=0)
65863             CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
65864           else {
65865             if (selected>=0)
65866               CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
65867             else canvas.display(disp);
65868           }
65869           refresh = false;
65870         }
65871         disp.wait(15);
65872         if (disp.is_resized()) disp.resize(disp,false);
65873 
65874         if (disp.button()&1)  {
65875           oclicked = clicked;
65876           clicked = -1;
65877           cimglist_for(buttons,l)
65878             if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
65879                 disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
65880               clicked = selected = l;
65881               refresh = true;
65882             }
65883           if (clicked!=oclicked) refresh = true;
65884         } else if (clicked>=0) stop_flag = true;
65885 
65886         if (disp.key()) {
65887           oselected = selected;
65888           switch (disp.key()) {
65889           case cimg::keyESC : selected = -1; stop_flag = true; break;
65890           case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
65891           case cimg::keyTAB :
65892           case cimg::keyARROWRIGHT :
65893           case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
65894           case cimg::keyARROWLEFT :
65895           case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
65896           }
65897           disp.set_key();
65898           if (selected!=oselected) refresh = true;
65899         }
65900       }
65901       if (!disp) selected = -1;
65902       return selected;
65903 #endif
65904     }
65905 
65906     //! Display a simple dialog box, and wait for the user's response \specialization.
65907     inline int dialog(const char *const title, const char *const msg,
65908                       const char *const button1_label, const char *const button2_label,
65909                       const char *const button3_label, const char *const button4_label,
65910                       const char *const button5_label, const char *const button6_label,
65911                       const bool is_centered) {
65912       return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
65913                     CImg<unsigned char>::_logo40x38(),is_centered);
65914     }
65915 
65916     //! Evaluate math expression.
65917     /**
65918        \param expression C-string describing the formula to evaluate.
65919        \param x Value of the pre-defined variable \c x.
65920        \param y Value of the pre-defined variable \c y.
65921        \param z Value of the pre-defined variable \c z.
65922        \param c Value of the pre-defined variable \c c.
65923        \return Result of the formula evaluation.
65924        \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
65925        \par Example
65926        \code
65927        const double
65928        res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2),  // will return '1'
65929        res2 = cimg::eval(0,1,1);                    // will return '1' too
65930        \endcode
65931     **/
65932     inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
65933       static const CImg<float> empty;
65934       return empty.eval(expression,x,y,z,c);
65935     }
65936 
65937     template<typename t>
65938     inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
65939       static const CImg<float> empty;
65940       return empty.eval(expression,xyzc);
65941     }
65942 
65943   } // namespace cimg { ...
65944 } // namespace cimg_library { ...
65945 
65946 //! Short alias name.
65947 namespace cil = cimg_library_suffixed;
65948 
65949 #ifdef _cimg_redefine_False
65950 #define False 0
65951 #endif
65952 #ifdef _cimg_redefine_True
65953 #define True 1
65954 #endif
65955 #ifdef _cimg_redefine_Status
65956 #define Status int
65957 #endif
65958 #ifdef _cimg_redefine_Success
65959 #define Success 0
65960 #endif
65961 #ifdef _cimg_redefine_min
65962 #define min(a,b) (((a)<(b))?(a):(b))
65963 #endif
65964 #ifdef _cimg_redefine_max
65965 #define max(a,b) (((a)>(b))?(a):(b))
65966 #endif
65967 #ifdef _cimg_redefine_PI
65968 #define PI 3.141592653589793238462643383
65969 #endif
65970 #ifdef _MSC_VER
65971 #pragma warning(pop)
65972 #endif
65973 
65974 #endif
65975 
65976 // Local Variables:
65977 // mode: c++
65978 // End:
65979